文章目录
- 概述
- IO端口和IO内存的区别
- IO资源管理
- IO资源类型
- IO端口资源
- IO内存资源
- IO资源分配
- IO端口访问
- IO端口操作函数
- IO内存访问
- IO内存操作函数
- 相关参考
概述
在计算机系统中,外部设备通常会提供一组寄存器或内存用于处理器配置和访问设备功能。这些寄存器或内存可能位于IO空间,或者内存空间。当寄存器或内存位于IO空间时,称为IO端口;当寄存器或内存位于内存空间时,称为IO内存。
IO端口和IO内存的区别
IO端口和IO内存的主要区别在于CPU对外设IO寄存器物理地址的编址方式不同。已存在的编址方式有两种:独立编址和统一编址,其中:
- 独立编址:外设使用独立的IO地址空间,并使用专门的IO指令进行访问。例如x86体系结构就使用了独立的IO空间,支持64K个IO端口;
- 统一编址:设备寄存器被映射到CPU的物理内存空间,并支持以访问内存的方式来访问这些寄存器。
IO资源管理
IO端口和IO内存都属于外设提供的IO资源,Linux下通过resource结构来对IO资源进行描述,包括使用的I/O资源的地址空间范围、状态等信息,并将这些resource用ioport_resource或iomem_resource作为表头按地址大小的顺序链接起来。
struct resource {
resource_size_t start; /* 描述设备实体在 CPU 总线上的线性起始物理地址 */
resource_size_t end; /* 描述设备实体在 CPU 总线上的线性结尾物理地址 */
const char *name; /* 描述这个设备实体的名称,这个名字开发人员可以随意起,但最好贴切 */
unsigned long flags; /* 描述这个设备实体的一些共性和特性的标志位 */
struct resource *parent, *sibling, *child;
};
IO资源类型
Linux支持运行在多种不同的硬件体系结构上,并同时支持IO端口以及IO内存,为此内核同时维护了这两种类型的IO资源,外部设备按需进行分配。
IO端口资源
IO端口资源由ioport_resource全局变量进行管理,定义如下:
struct resource ioport_resource = {
.name = "PCI IO",
.start = 0,
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};
IO内存资源
IO内存资源由iomem_resource全局变量进行管理,定义如下:
struct resource iomem_resource = {
.name = "PCI mem",
.start = 0,
.end = -1,
.flags = IORESOURCE_MEM,
};
IO资源分配
Linux访问设备IO空间前,需要先调用request_region/request_mem_region接口为其分配IO资源区域,其中request_region用于分配IO端口资源,request_mem_region用于分配IO内存资源。
request_region/request_mem_region调用__request_region函数实现。__request_region函数的主要功能为:查找resource链表中是否有与申请的I/O资源有冲突,如冲突则返回NULL,如不冲突则将新申请resource按resource地址从小到大的顺放插入至以ioport_resource或iomem_resource为表头(root)的单向指针链表中。
IO端口访问
Linux系统下访问IO端口有2种方式:I/O映射方式和内存映射方式。其中:
- IO映射方式直接使用IO端口操作函数,这些函数内部会调用体系结构相关的IO端口操作指令,例如x86架构下的in/out指令;
- 内存映射方式是先使用ioport_remap接口把IO端口映射到IO内存空间,再使用IO内存操作函数来访问IO端口。这种方式主要是为了屏蔽IO端口和IO内存在硬件上的差异,统一使用IO内存的方式进行访问。
无论是使用哪种方式,都需要先使用request_region接口申请对应的IO端口资源。完整的IO端口访问流程如下:
IO端口操作函数
/* inb/outb:读/写字节端口(8位宽) */
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
/* inw/outw:读/写字端口(16位宽) */
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
/* inl/outl:读/写32位端口 */
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
IO内存访问
Linux系统访问IO内存需要先调用request_mem_region接口申请资源,接着将设备IO地址通过ioremap接口映射到内核空间的虚拟地址,之后就可以使用IO内存操作函数对这些寄存器进行访问。
完整的IO内存访问流程如下:
IO内存操作函数
/* I/O内存读函数 */
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
/* I/O内存写函数 */
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
相关参考
- Linux系统对IO端口和IO内存的管理
- Linux设备驱动:I/O端口与I/O内存