[LDD3阅读笔记] 与硬件通信

来源:互联网 发布:制作课件的软件 编辑:程序博客网 时间:2024/05/02 04:56

1. IO端口 (Port IO)

就是我们平时用的Port 80这种

2. IO内存 (Memmap IO)

PCI,可以把一片寄存器映射到内存区域这片内存区域就叫作Memmap IO.

3. 内存屏障

程序会被编译器优化,但这对了访问IO可能会造成致命的错误。我们可以在对硬件以特定执行顺序的操作之间设置内存屏障(memory barrier)

void barrier(void)

linux/kernel.h

通知编译器插入一个内存屏障,代码会把当前CPU寄存器中的所有修改过的数值保存到内存中,需要时再读出来。这个函数可以避免在屏障前后的编译器优化,但硬件能完成自己的排序。

__asm__ __volatile__("": : :"memory")

void rmb(void)

asm/system.h

保证屏障之前的读操作一定会在后来的读操作之前完成。

都是已编译的指令中流中插入硬件内存屏障

asm volatile("lfence":::"memory")

void read_barrier_depends(void)

----------------------------------------这个与rmb很微妙----------------不管

do { } while (0)

void wmb(void)

保证写操作不会乱序

asm volatile("sfence" ::: "memory")

void mb(void)

保证读写操作都不会乱序

asm volatile("mfence":::"memory")

void smp_rmb(void)

asm/system.h

同上面那一组,仅针对SMP系统编译时有效.

PS:目前好像都是SMP系统了

__asm__ __volatile__("": : :"memory")

void smp_read_barrier_depends(void)

do { } while (0)

void smp_wmb(void)

__asm__ __volatile__("": : :"memory")

void smp_mb(void)

__asm__ __volatile__("": : :"memory")

4. IO端口分配

#include <linux/port.h>

struct resource * request_region(unsigned long first, unsigned long n, const char *name);//失败返回 NULL

void release_region(unsigned long start, unsigned long n);

int check_region(unsigned long first, unsigned long n);//这函数返回成功并不代表request_region就能成功

5. 访问IO端口

#include <asm/io.h>

unsigned inb(unsigned port);

void outb(unsigned char byte, unsigned port);//不同的平台port的位宽可能不一样

unsigned inw(unsigned port);

void outw(unsigned short word, unsigned port);

unsigned inl(unsigned port);

void outl(unsigned longword, unsigned port);

PS:读返回的都是unsigned, 写则区别数据宽度

串操作

void insb(unsigned port, void *addr, unsigned long count);

void outsb(unsigned port, void *addr, unsigned long count);

从内存地址addr开始连续读/count数目的字节。只对单一端口port读取或写入数据

void insw(unsigned port, void *addr, unsigned long count);

void outsw(unsigned port, void *addr, unsigned long count);

16位端口

void insl(unsigned port, void *addr, unsigned long count);

void outsl(unsigned port, void *addr, unsigned long count);

32位端口

暂停式I/O

在处理器试图从总线快速传输数据时,某些平台(特别是i386)会遇到问题。解决办法是在每条I/O指令之后,如果还有其它类似指令,则插入一个小的延迟。(PS:我感觉一般不会这么用,如果有这种情况,应该明显的调用delay)

inb_p

outb_p

6. IO内存的分配和映射

分配与释放

struct resource *request_mem_region(unsigned long start, unsigned long len, char * name);

void release_mem_region(unsigned long start, unsigned long len);

分配到的IO内存不一定可以直接访问, 一定要先建立映射

映射

#include <asm/io.h>

void *ioremap(unsigned long phys_addr, unsigned long size);

void *ioremap_nocache(unsigned long phys_addr, unsigned long size);

void iounmap(void *addr);

映射得到的地址空间也不应直接引用(虽然有些平台是可以的),而要通过accessor函数(见下一节)

7. 访问IO内存

#include <asm/io.h>s

unsigned int ioread8(void *addr);

unsigned int ioread16(void *addr);

unsigned int ioread32(void *addr);

void iowrite8(u8 value, void *addr);

void iowrite16(u16 value, void *addr);

void iowrite32(u32 value, void *addr);

PS:这一组写函数与读函数好像有点不太一样。。这里用的是u8, u16, u32, 而读是统一用的unsigned int.

重复版本

void ioread8_rep(void *addr, void *buf, unsigned long count);

void ioread16_rep(void *addr, void *buf, unsigned long count);

void ioread32_rep(void *addr, void *buf, unsigned long count);

void iowrite8_rep(void *addr, const void *buf, unsigned long count);

void iowrite16_rep(void *addr, const void *buf, unsigned long count);

void iowrite32_rep(void *addr, const void *buf, unsigned long count);

PS:数据都是连续放的, count是指函数名指定数据大小的个数。

IO内存块

void memset_io(void *addr, u8 value, unsigned int count);

void memcpy_fromio(void *dest, void *source, unsigned int count);

void memcpy_toio(void *dest, void *source, unsigned int count);

一些老的函数

void readb(address);

unsigned readw(address);

unsigned readl(address);

readq//64

void writeb(unsigned value, address);

void writew(unsigned value, address);

void writel(unsigned value, address);

writeq//64

8. IO内存一样使用IO端口

为了统一IO端口和IO内存,出现了一组软件上的统一函数

void *ioport_map(unsigned long port, unsigned int count);//port 一定要不大于PIO_MASK(即是真正的port IO(64K)), 否则一定返回NULL

#define PIO_OFFSET0x10000UL

#define PIO_MASK0x0ffffUL
void __iomem *ioport_map(unsigned long port, unsigned int nr)

{

if (port > PIO_MASK)

return NULL;

return (void __iomem *) (unsigned long) (port + PIO_OFFSET);

}

void ioport_unmap(void *addr);

访问只限使用ioread8 之类的函数

PS:这只是软件上的一种封装,对于没有Port IO(IO端口)的系统没作用。 原理(只限x86)Memmap IO的地址都是高地址,Port IO都在64K以内, 这样就能区分了。 注意不要试图用ioread8()之类的函数去统一所有IO操作(包括64K以下IO), 因为ioread8()访问64K以下IO会报错。

9. ISA

无视, 现在都不用了


原创粉丝点击