第11章 内存与IO访问之设备IO端口和I/O内存的访问

11.4 设备I/O端口和I/O内存的访问

    设备通常会提供一组寄存器来控制设备、读写设备和获取设备状态,即控制寄存器、数据寄存器和状态寄存器。这些寄存器可能位于I/O空间中,也可能位于内存空间中。当寄存器位于I/O空间时,被称为I/O端口;当寄存器位于内存空间时,对应的内存空间被称为I/O内存。

11.4.1 Linux I/O端口和I/O内存访问接口

1.I/O端口

    在Linux设备驱动中,使用Linux内核提供的函数来访问定位于I/O空间的端口。

#include <asm/io.h>

1)读写字节端口(8位宽)。

unsigned inb(unsigned port); //读

void outb(unsigned char byte, unsigned port);//写

2)读写字端口(16位宽)。

unsigned inw(unsigned port);//读

void outw(unsigned short word, unsigned port);//写

3)读写长字端口(32位宽)。

unsigned inl(unsigned port);//读

void outl(unsigned longword, unsigned port);//写

4)读写一串字节。

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

insb()从端口port开始读count个字节端口,并将读取结果写入addr指向的内存

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

将addr指向的内存中的count个字节连续写入以port开始的端口。

5)读写一串字。

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

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

6)读写一串长字。

void insl(unsigned port, void *addr, unsigned long count);//读
void outsl(unsigned port, void *addr, unsigned long count);//写

上述各函数中I/O端口号port的类型长度依赖于具体的硬件平台

2.I/O内存

    在内核中访问I/O内存(通常是芯片内部的各个I2C、SPI、USB等控制器的寄存器或者外部内存总线上的设备)之前,需首先使用ioremap()函数将设备所处的物理地址映射到虚拟地址上。

ioremap()的原型:

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

ioremap()与vmalloc()类似,也需要建立新的页表,但是ioremap()并不进行vmalloc()中所执行的内存分配行为。ioremap()返回一个特殊的虚拟地址,该地址可用来存取特定的物理地址范围,这个虚拟地址位于vmalloc映射区域。通过ioremap()获得的虚拟地址应该被iounmap()函数释放,其原型如下:

void iounmap(void * addr);

ioremap()有个变体是devm_ioremap(),通过devm_ioremap()进行的映射通常不需要在驱动退出和出错处理的时候进行iounmap()。

#include <linux/io.h>

devm_ioremap()的原型为:

void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, unsigned long size);

在设备的物理地址(一般都是寄存器)被映射到虚拟地址之后,尽管可以直接通过指针访问这些地址,但是Linux内核推荐用一组标准的API来完成设备内存映射的虚拟地址的读写。

    读寄存器用readb_relaxed()、readw_relaxed()、readl_relaxed()、readb()、readw()、
readl()这一组API,以分别读8bit、16bit、32bit的寄存器,没有_relaxed后缀的版本与有_relaxed后缀的

版本的区别是没有_relaxed后缀的版本包含一个内存屏障,如:

#define readb(c)            ({ u8  __v = readb_relaxed(c); __iormb(); __v; })
#define readw(c)            ({ u16__v = readw_relaxed(c); __iormb(); __v; })

#define readl(c)            ({ u32 __v = readl_relaxed(c); __iormb(); __v; })

写寄存器用writeb_relaxed()、writew_relaxed()、writel_relaxed()、writeb()、writew()、
writel()这一组API,以分别写8bit、16bit、32bit的寄存器,没有_relaxed后缀的版本与有_relaxed后缀的
版本的区别是没有_relaxed后缀的版本包含一个内存屏障,如:

#define writeb(v,c)         ({ __iowmb(); writeb_relaxed(v,c); })
#define writew(v,c)         ({ __iowmb(); writew_relaxed(v,c); })
#define writel(v,c)         ({ __iowmb(); writel_relaxed(v,c); })

11.4.2 申请与释放设备的I/O端口和I/O内存

1.I/O端口申请

Linux内核提供一组函数以申请和释放I/O端口,表明该驱动要访问这片区域。

#include <linux/ioport.h>

struct resource *request_region(unsigned long first, unsigned long n, const char *name);// 申请IO端口

这个函数向内核申请n个端口,这些端口从first开始,name参数为设备的名称。如果分配成功,则返回值不是NULL,如果返回NULL,则意味着申请端口失败。

当用request_region()申请的I/O端口使用完成后,应当使用release_region()函数将它们归还给系统,这个函数的原型如下:

void release_region(unsigned long start, unsigned long n); //释放IO端口

2.I/O内存申请

Linux内核提供一组函数用来申请和释放I/O内存的范围。此处的“申请”表明该驱动要访问这片区域,它不会做任何内存映射的动作。

#include <linux/ioport.h>

struct resource *request_mem_region(unsigned long first, unsigned long n, char *name);// 申请IO内存

这个函数向内核申请n个内存地址,这些地址从first开始,name参数为设备的名称。如果分配成功,则返回值不是NULL,如果返回NULL,则意味着申请I/O内存失败。

当用request_mem_region()申请的I/O内存使用完成后,应当使用release_mem_region()函数将它们归还给系统,这个函数的原型如下:

void release_mem_region(unsigned long start, unsigned long len);// 释放IO内存

request_region()和request_mem_region()也分别有变体,其为devm_request_region()

devm_request_mem_region()

11.4.3 设备I/O端口和I/O内存访问流程

1、设备驱动访问I/O端口的步骤

I/O端口访问的一种途径是直接使用I/O端口操作函数:在设备打开或驱动模块被加载时申请I/O端口区域,之后使用inb()、outb()等进行端口访问,最后,在设备关闭或驱动被卸载时释放I/O端口范围。

整个流程如图11.10所示。


    图11.10 I/O端口访问流程

2、设备驱动访问I/O内存的步骤

1)调用request_mem_region()申请IO内存资源。

2)将设备寄存器的物理地址通过ioremap()映射到内核空间的虚拟地址。

3)通过Linux设备访问编程接口访问设备的寄存器。

4)访问完成后,调用iounmap()函数对ioremap()映射的虚拟地址解除映射,并调用release_mem_region()函数释放申请的I/O内存资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值