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_OFFSET
#define PIO_MASK { 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)是