内核中有很多资源,但属于IO资源的有:
#define IORESOURCE_IO 0x00000100 /* Resource type */
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
本文我主要研究IORESOURCE_IO IORESOURCE_MEM,及地址空间的管理,涉及的文件只有kernel/resource.c.
这两种资源本质上都是一段地址空间, 只是类型不一样。
IORESOURCE_IO指的是IO地址空间,这个空间从kernel编程上来看,只能通过专门的接口函数才能访问。硬件层面上,cpu需要用特殊指令才能访问或需要用特殊访问方式才能访问,不能直接用指针来寻址。在PC机上,其指的就是PCI/CPU IO address space。在嵌入式中,基本上没有io address space。
IORESOURCE_MEM 指的是属于外设或者用于和设备通讯的支持直接寻址的地址空间。在PC机上,主板北桥上连的内存都是交给kernel直接管理,或者都是用于软件执行,所以这部分内存不属于IORESOURCE_MEM。 IORESOURCE_MEM主要是指PCI设备的 memory address space。 但在嵌入式上,主板上的SDRAM一般是设备与CPU共享的, 故交给kernel直接管理的内存只是一部分。余下的内存以及寄存器空间都作为IORESOURCE_MEM来管理。
只所以需要管理,是因为像PCI总线设备的这些地址空间是设备向系统申请的,故是可配置的,并且设备并身可能热插拨或更换,故其变成一种可分配的资源。故内核用算法来管理分配与释放操作,防止冲突和便于查询维护。但实际PC中,BIOS一般会做分配操作,内核需要是把分配结果添加进来,故提供了注册(或者叫做添加)接口。 在嵌入式系统中,外设的地址也通常是固定的,只需要添加即可。
这两种资源,内核采用同样的管理算法--二叉树。相当于内核维护两个独立的二叉树。 按地址基地址与地址长度范围作为管理数据。可以添加,分配,释放节点。分配过程中可以避
免空间冲突,添加时可以识别空间冲突。根节点在kernel/resource.c中以全局变量方式定义。根节点用于限制地址空间的范围。
主要接口:
int insert_resource(struct resource *parent, struct resource *new)
int adjust_resource(struct resource *res, unsigned long start, unsigned long size)
int allocate_resource(struct resource *root, struct resource *new,
unsigned long size,
unsigned long min, unsigned long max,
unsigned long align,
void (*alignf)(void *, struct resource *,
unsigned long, unsigned long),
void *alignf_data)
int release_resource(struct resource *old)
int request_resource(struct resource *root, struct resource *new)
上面的接口对上述4种资源类型都有效,对于IORESOURCE_IO 和 IORESOURCE_MEM这两种资源,使用这两个接口更为方便。
request_region(start,n,name)
release_region(start,n)
io space
request_mem_region(start,n,name)
release_mem_region(start,n)
mem space
在 /proc 文件系统中,ioports和iomem分别显示系统当前这两种资源。
嵌入式设备的外设一般先作为platform device添加到platform bus上,其主要目的就是向系统注册资源,在platform_add_devices时做,
如:
/* Watchdog timer parameters */
static struct resource wdt_resource[] = {
/* Watchdog timer only needs a register address */
[0] = {
.start = 0xFFC00008,
.end = 0xFFC00010,
.flags = IORESOURCE_MEM,
}
};
struct platform_device wdt_device = {
.name = "wdt",
.id = -1,
.num_resources = ARRAY_SIZE(wdt_resource),
.resource = wdt_resource,
};
platform_add_devices(wdt_device,1);
故如已知设备的物理地址,并不一定需要request_mem_region,因为这个操作并没有涉及到任何硬件,与软件访问也没有任何关系..但一般还是推荐做.
I/O port 访问流程( 不用mem模拟方式):
request_region() #在设备驱动模块加载或opn() 函数中进行,物理i/o port地址由pci bios模块分配
inb(),outb()等 #在设备驱动初始化,write(),red(),ioctl() 等函数中进行
relase_region #在设备驱动模块卸载或release() 函数中进行
I/O port 访问流程( 用mem模拟方式):
request_region()
ioport_map #在设备驱动模块加载或opn() 函数中进行,物理i/o port地址由pci bios模块分配
ioread8,iowrite8等 #在设备驱动初始化,write(),red(),ioctl() 等函数中进行
ioport_unmap()
relase_region #在设备驱动模块卸载或release() 函数中进行
request_mem_region
ioremap #在设备驱动模块加载或opn() 函数中进行,物理i/o port地址由pci bios模块分配
ioread8,iowrite8等 #在设备驱动初始化,write(),red(),ioctl() 等函数中进行
iounmap()
relase_region #在设备驱动模块卸载或release() 函数中进行
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/18103/showart_1145482.html