LDD读书笔记第九章-与硬件通信
I/O端口和I/O内存
i/o寄存器和常规内存
为了防止边界效应的干扰,驱动程序必须确保不使用高速缓存,并且在访问寄存器时不发生读或写指令的从新排序。
高速缓存:把底层硬件配置成在访问IO区域时禁止硬件缓存。
从新排序:使用内存屏障
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <linux/kernel.h> void barrier(void) /*会把cpu中修改过的所有数据保存到内存中,在使用时从新读出来*/ #include <asm/system.h> void rmb(void) /*保证读操作不会乱序*/ void wmb(void) /*保证写操作不会乱序*/ void mb(void) /*保证读写操作不会乱序*/ void smp_rmb(void) void smp_wmb(void) void smp_mb(void) /*只在针对SMP系统编译时有效,单处理器情况会扩展为上述简单屏障*/ /*赋值并设置屏障宏*/ #define set_mb(var, value) do {var = value; mb( );} while 0 #define set_wmb(var, value) do {var = value; wmb( );} while 0 #define set_rmb(var, value) do {var = value; rmb( );} while 0 |
使用IO端口
IO端口分配
1 2 3 4 5 6 7 8 9 10 | /*注册端口*/ #include <linux/ioport.h> struct resource *request_region(unsigned long first, unsigned long n, const char *name); /*释放端口*/ void release_region(unsigned long start, unsigned long n); /*下面的函数用于查询指定的端口号是否可用。所做的操作并非是原子操作。 不能保证分配成功。尽量避免使用*/ int check_region(unsigned long first, unsigned long n); |
端口的分配表可在系统中/proc/ioports查询到。
操作IO端口
1 2 3 4 5 6 7 8 9 10 | #include <asm/io.h> unsigned inb(unsigned port); void outb(unsigned char byte, unsigned port); /*字节大小传送*/ unsigned inw(unsigned port); void outw(unsigned short word, unsigned port); /*字大小传送*/ unsigned inl(unsigned port); void outl(unsigned longword, unsigned port); /*双字大小传送*/ |
在用户空间访问IO
GNU的C库在sys/io.h定义。
条件:
- 编译该程序时必须带-O参数来强制内联函数展开
- 必须用ioperm或iopl系统调用来获取对端口io操作的权限
- 必须以root身份运行才能嗲用ioperm或iopl
串操作
1 2 3 4 5 6 | void insb(unsigned port, void *addr, unsigned long count); void outsb(unsigned port, void *addr, unsigned long count); void insw(unsigned port, void *addr, unsigned long count); void outsw(unsigned port, void *addr, unsigned long count); void insl(unsigned port, void *addr, unsigned long count); void outsl(unsigned port, void *addr, unsigned long count); |
暂停式IO
当处理器时钟比外设时钟快时会遇到问题。
暂停式IO在一般IO函数后加_p来实现
IO端口示列
并口简介
并口由三个8位端口组成。
第一个端口从0×378开始,是双向数据寄存器,链接到2-9号引脚。
第二个端口从0×278开始,是只读状态寄存器。
第三个端口是输出控制寄存器。
并行通信中使用标准TTL电平:0V和5V阀值1.2
并口连接器没有和计算机内部电路隔离,这点在把逻辑门直连到端口时很有用。
但是很容易烧坏端口和主板。

使用IO内存
IO内存分配和映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | struct resource *request_mem_region(unsigned long start, unsigned long len, char *name); /*从start开始分配lan字节长的内存区域成功返回非null指针*/ void release_mem_region(unsigned long start, unsigned long len); /*释放*/ int check_mem_region(unsigned long start, unsigned long len); /*由于和check_region一样的原因,不安全。避免使用*/ #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); /*书上对ioremap_nocache的解释态绕口看英文 It’s useful if some control registers are in such an area, and write combining or read caching is not desirable. */ |
分配情况可在/proc/iomem查询
访问IO内存
ioremap返回值可以直接当指针使用。
但是为了可移植性尽量使用下面函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <asm/io.h> 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); 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); 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); /*老的函数*/ unsigned readb(address); unsigned readw(address); unsigned readl(address); void writeb(unsigned value, address); void writew(unsigned value, address); void writel(unsigned value, address); |
像IO内存一样使用端口
1 2 | void *ioport_map(unsigned long port, unsigned int count); void ioport_unmap(void *addr); |
----------------- 全文结束 -----------------
© 该日志作者:Senghoo, 于2010年07月26日发表于分类"Linux Kernel"下;
© 除非另有注明本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
© 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
© 转载请注明: LDD读书笔记第九章-与硬件通信 | 转自: Senghoo's Blog
© Tag: ldd , linux , 硬件
© 除非另有注明本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
© 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
© 转载请注明: LDD读书笔记第九章-与硬件通信 | 转自: Senghoo's Blog
© Tag: ldd , linux , 硬件