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: , ,

你可以继续围观

发表评论