设备驱动中的iomem(kernel-4.7)
来源:互联网 发布:ios6.1.3可用软件 编辑:程序博客网 时间:2024/04/30 14:30
对于外设的访问,最终都是通过读写设备上的寄存器实现的,寄存器不外乎:控制寄存器、状态寄存器和数据寄存器,这些外设寄存器也称为“IO端口”,并且一个外设的寄存器通常是连续编址的。
不同的CPU体系对外设IO端口物理地址的编址方式也不同,分为I/O映射方式(I/O-mapped)和内存映射方式(Memory-mapped)。
Linux设计了一个通用的数据结构resource来描述各种I/O资源(如:I/O端口、外设内存、DMA和IRQ等)。该结构定义在include/linux/ioport.h头文件中。Linux是以一种倒置的树形结构来管理每一类I/O资源。每一类I/O资源都对应有一颗倒置的资源树,树中的每一个节点都是个resource结构。基于上述这个思想,Linux将基于I/O映射方式的I/O端口和基于内存映射方式的I/O端口资源统称为“I/O区域”(I/O Region)
以pci设备为例,硬件的拓扑结构就决定了硬件在内存映射到CPU的物理地址,由于内存访问都是虚拟地址,所有就需要ioremap,此时物理内存是存在的,所以不用再分配内存,只需要做映射即可。
resource结构体:
/* * Resources are tree-like, allowing * nesting etc.. */struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; unsigned long desc; struct resource *parent, *sibling, *child;};
申请I/O端口的函数是request_region
, 申请I/O内存的函数是request_mem_region
。request_mem_region
函数并没有做实际性的映射工作,只是告诉内核要使用一块内存地址,声明占有,也方便内核管理这些资源。重要的还是ioremap函数,ioremap主要是检查传入地址的合法性,建立页表(包括访问权限),完成物理地址到虚拟地址的转换。
I/O Region的分配
在函数__request_resource()
的基础上,Linux实现了用于分配I/O区域的函数__request_region()
,如下:
/** * __request_region - create a new busy resource region * @parent: parent resource descriptor * @start: resource start address * @n: resource region size * @name: reserving caller's ID string * @flags: IO resource flags */struct resource * __request_region(struct resource *parent, resource_size_t start, resource_size_t n, const char *name, int flags){ DECLARE_WAITQUEUE(wait, current); struct resource *res = alloc_resource(GFP_KERNEL);//分配一个resource结构 if (!res) return NULL; res->name = name; res->start = start; res->end = start + n - 1; write_lock(&resource_lock); for (;;) { struct resource *conflict; res->flags = resource_type(parent) | resource_ext_type(parent); res->flags |= IORESOURCE_BUSY | flags; res->desc = parent->desc; conflict = __request_resource(parent, res);//调用__request_resource()函数进行资源分配 if (!conflict) break; if (conflict != parent) { //分配不成功,则进一步判断所返回的冲突资源节点是否就是父资源节点parent if (!(conflict->flags & IORESOURCE_BUSY)) { parent = conflict; continue; } } if (conflict->flags & flags & IORESOURCE_MUXED) { add_wait_queue(&muxed_resource_wait, &wait); write_unlock(&resource_lock); set_current_state(TASK_UNINTERRUPTIBLE); schedule(); remove_wait_queue(&muxed_resource_wait, &wait); write_lock(&resource_lock); continue; } /* Uhhuh, that didn't work out.. */ free_resource(res); res = NULL; break; } write_unlock(&resource_lock); return res;}
用一个for循环开始进行资源分配,循环体的步骤如下:
首先,调用__request_resource()
函数进行资源分配。如果返回NULL,说明分配成功,因此就执行break语句退出for循环,返回所分配的resource结构的指针,函数成功地结束。
如果__request_resource()
函数分配不成功,则进一步判断所返回的冲突资源节点是否就是父资源节点parent。如果不是,则将分配行为下降一个层次,即试图在当前冲突的资源节点中进行分配(只有在冲突的资源节点没有设置IORESOURCE_BUSY
的情况下才可以),于是让 parent指针等于conflict,并在conflict->flags&IORESOURCE_BUSY
为0的情况下执行 continue语句继续for循环。否则如果相冲突的资源节点就是父节点parent,或者相冲突资源节点设置了IORESOURCE_BUSY
标志位,则宣告分配失败。于是调用free_resource
函数释放所分配的resource结构,并将res指针置为NULL,最后用break语句推出for循环。
最后,返回所分配的resource结构的指针。
I/O Region的释放
函数__release_region()
实现在一个父资源节点parent中释放给定范围的I/O Region。实际上该函数的实现思想与__release_resource()
相类似。其源代码如下:
/** * __release_region - release a previously reserved resource region * @parent: parent resource descriptor * @start: resource start address * @n: resource region size * * The described resource region must match a currently busy region. */void __release_region(struct resource *parent, resource_size_t start, resource_size_t n){ struct resource **p; resource_size_t end; p = &parent->child; end = start + n - 1; write_lock(&resource_lock); for (;;) { struct resource *res = *p; if (!res) break; if (res->start <= start && res->end >= end) { if (!(res->flags & IORESOURCE_BUSY)) { p = &res->child; continue; } if (res->start != start || res->end != end) break; *p = res->sibling; write_unlock(&resource_lock); if (res->flags & IORESOURCE_MUXED) wake_up(&muxed_resource_wait); free_resource(res); return; } p = &res->sibling; } write_unlock(&resource_lock); printk(KERN_WARNING "Trying to free nonexistent resource " "<%016llx-%016llx>\n", (unsigned long long)start, (unsigned long long)end);}
类似地,该函数也是通过一个for循环来遍历父资源parent的child链表。为此,它让指针res指向当前正被扫描的子资源节点,指针p指向前一个子资源节点的sibling成员变量,p的初始值为指向parent->child
。For循环体的步骤如下:
①让res指针指向当前被扫描的子资源节点(res=*p)。
②如果res指针为NULL,说明已经扫描完整个child链表,所以退出for循环。
③如果res指针不为NULL,则继续看看所指定的I/O区域范围是否完全包含在当前资源节点中,也即看看[start,start+n-1]是否包含在res->[start,end]中。如果不属于,则让p指向当前资源节点的sibling成员,然后继续for循环。如果属于,则执行下列步骤:
先看看当前资源节点是否设置了IORESOURCE_BUSY
标志位。如果没有设置该标志位,则说明该资源节点下面可能还会有子节点,因此将扫描过程下降一个层次,于是修改p指针,使它指向res->child
,然后执行continue语句继续for循环。
如果设置了IORESOURCE_BUSY
标志位。则一定要确保当前资源节点就是所指定的I/O区域,然后将当前资源节点从其父资源的child链表中去除。这可以通过让前一个兄弟资源节点的sibling指针指向当前资源节点的下一个兄弟资源节点来实现(即让*p=res->sibling
),最后调用free_resource
函数释放当前资源节点的resource结构。然后函数就可以成功返回了。
管理I/O端口资源
Linux是基于“I/O Region”这一概念来实现对I/O端口资源(I/O-mapped 或 Memory-mapped)的管理的。
Linux在kernel/resource.c文件中定义了全局变量ioport_resource
和iomem_resource
,来分别描述基于I/O映射方式的整个I/O端口空间和基于内存映射方式的I/O内存资源空间(包括I/O端口和外设内存)。其定义如下:
struct resource ioport_resource = { .name = "PCI IO", .start = 0, .end = IO_SPACE_LIMIT, .flags = IORESOURCE_IO,};struct resource iomem_resource = { .name = "PCI mem", .start = 0, .end = -1, .flags = IORESOURCE_MEM,};
显然,I/O内存空间的大小是4GB。
未完待续。。。。
- 设备驱动中的iomem(kernel-4.7)
- 设备驱动中的device(kernel-4.7)
- 设备驱动中的device_driver(kernel-4.7)
- 设备驱动中的bus(kernel-4.7)
- 设备驱动中的kobject(kernel-4.7)
- 设备驱动中的kset(kernel-4.7)
- 设备驱动中的pinctrl(kernel-4.7)
- 设备驱动中的platform(kernel-4.7)
- 设备驱动中的class(kernel-4.7)
- 设备驱动中的i2c(kernel-4.7)
- 设备驱动中的tty(kernel-4.7)
- 设备驱动中的gadget(kernel-4.7)
- 设备驱动中的mutex(kernel-4.7)
- 设备驱动中的inode(kernel-4.7)
- 设备驱动中的spin_lock(kernel-4.7)
- 设备驱动中的misc(kernel-4.7)
- 设备驱动中的mmc(kernel-4.7)
- 设备驱动中的pci(kernel-4.7)
- magento 相关xml功能的介绍
- sign符号的含义
- Unity 物体根据鼠标移动而转动(可用于物体的360度展示)
- Eclipse部署Maven web项目到tomcat服务器时,没有将lib下的jar复制过去的解决办法
- CentOS7关闭防火墙和selinux
- 设备驱动中的iomem(kernel-4.7)
- spark pairRDD基本操作(三)——附带wordcount程序
- 直播源码选云豹,少走弯路是王道!
- Android如何判断NavigationBar是否显示(获取屏幕真实的高度)
- MyBatis 完全使用指南
- 安装db2数据库
- java中的Class类与Class对象
- mysqldump命令用法
- NSE脚本扫描