Linux驱动之与硬件通信

1 IO端口和IO内存区别

像x86系列处理器有两种地址空间:一种是内存地址空间,一种是端口地址空间,访问端口有单独的指令,例如in/out指令,而像ARM这类处理器则使用单一的地址空间,没有端口地址空间的概念。在硬件层,IO端口和IO内存并无本质区别,都是通过向地址总线和控制总线发送地址和控制信号,再通过数据总线读写数据。


2 使用IO端口

2.1 IO端口分配

IO端口分配使用request_region函数,该函数用于取得该IO端口的独占访问,头文件在linux/ioport.h中,函数原型如下:

struct resourse *request_region(unsigned long first, unsigned long n, const char *name);

first表示起始端口号,n表示端口数量。

如果分配成功,返回非NULL值,如果request_region返回NULL,表示不能使用这些IO端口(有可能被其它程序占用了)。


2.2 IO端口释放

如果不在使用该IO端口,则调用release_region函数释放该IO端口的控制,函数原型如下:

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


2.3 检查IO端口是否可用

int check_region(unsigned long first, unsigned long n);

如果该端口不可用,则返回错误码。Linux中并不赞成使用这个函数,因为该函数只是对IO端口做检查,并不对IO端口进行锁定,这样有可能其它程序也能使用这组IO端口,这是一个比较老的函数。


2.4 操作IO端口

这组函数与具体的体系架构有关,所以头文件在asm/io.h中

unsigned inb(unsigned port);

void outb(unsigned char byte, unsigned port);

8位(字节)读写函数。


unsigned inw(unsigned port);

void outw(unsigned short word, unsigned port);

16位读写函数。


unsigned inl(unsigned port);

void out(unsinged long word, unsinged port);

32位读写函数。


3 使用IO内存

3.1 IO内存分配

在使用之前,必须首先分配IO内存,需要包含头文件linux/ioport.h,函数原型如下:

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

表示从start开始分配len字节的内存区域,如果成功,返回非NULL指针,否则返回NULL,所有的IO内存分配情况可以从/proc/iomem中看到。


3.2 IO内存释放

不再使用这段内存时,需要调用release_mem_region释放,函数原型如下:

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


同样也提供了一个比较老的函数,用来检查IO内存是否可用

int check_mem_region(unsigned long start, unsigned long len);

同check_region一样,它也是不安全的,应该避免使用。


3.3 IO内存映射和释放

通常,是不能直接访问IO内存的,需要对IO内存进行映射,映射是将物理地址映射成虚拟地址,函数原型如下:

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

void iounmap(void *addr);

要使用这些函数需要包含头文件asm/io.h

在ioremap映射之后,不应该直接使用,而使用内核提供的accessor函数。


3.4 访问IO内存

IO内存读

unsigned int ioread8(void *addr);

unsigned int ioread16(void *addr);

unsinged int ioread32(void *addr);

addr应该是从ioremap获得的地址,可能包含一个整数偏移量,返回值是从IO内存读取到的值。


IO内存写

void iowrite8(u8 value, void *addr);

void iowrite16(u16 value, void *addr);

void iowerite32(u32 value, void *addr);


连续读

如果需要连续读写一段IO内存,则使用如下函数:

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

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

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

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

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

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

上述函数从addr读取或写入count个值,注意,count以被写入的数据大小为单位表示,例如,ioread32从addr读取count个32位值到buf中。


块操作

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);


4 实例

例如,s3c2410_wdt.c中部分代码如下:

352         wdt_mem = request_mem_region(res->start, size, pdev->name);
353         if (wdt_mem == NULL) {
354                 dev_err(dev, "failed to get memory region\n");
355                 ret = -ENOENT;
356                 goto err_req;
357         }
358 
359         wdt_base = ioremap(res->start, size);
360         if (wdt_base == NULL) {
361                 dev_err(dev, "failed to ioremap() region\n");
362                 ret = -EINVAL;
363                 goto err_req;
364         }

首先是申请IO内存,然后是映射。

wdt_base定义如下:

80 static void __iomem     *wdt_base;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值