参考资料:
Uio即是user space I/O,用户空间驱动的简称, 可以运行在用户空间写硬件驱动。
硬件设备可以根据功能分为网络设备,块设备,字符设备,或者根据与CPU相连的方式
分为PCI设备,USB设备等。它们被不同的内核子系统支持。这些标准的设备的驱动编写
较为容易而且容易维护。很容易加入主内核源码树。但是,又有很多设备难以划分到这些子系统中,比如I/O卡,现场总线接口或者定制的FPGA。
通常这些非标准设备的驱动被实现为字符驱动。这些驱动使用了很多内核内部函数和宏。
而这些内部函数和宏是变化的。这样驱动的编写者必须编写一个完全的内核驱动,而且一直维护这些代码。而且这些驱动进不了主内核源码。于是就出现了用户空间I/O框架(Userspace I/O framework)。
UIO 的工作原理:
UIO分成2个部分,主要是内核部分和用户空间部分, 在内核部分主要是实现硬件寄存器的内存映射(struct uio_info -> struct uio_mem)及读写操作, 在用户空间部分,将uio设备的uio_mem映射到本地(mmap), 这样就可以实现在用户空间访问硬件设备寄存器的目的, 再通过设备的控制逻辑,就可以实现硬件设备的驱动。
一个UIO设备驱动的主要任务有两个:
1. 存取设备的内存
UIO 核心实现了mmap()可以处理物理内存(physical memory),逻辑内存(logical memory),虚拟内存(virtual memory)。UIO驱动的编写是就不需要再考虑这些繁琐的细节。
如果有些设备的总线不是PCI总线, 那么仍需要做相关的处理
2. 处理设备产生的中断
对于设备中断的应答必须在内核空间进行。所以在内核空间有一小部分代码
用来应答中断和禁止中断,但是其余的工作全部留给用户空间处理。
如果用户空间要等待一个设备中断,它只需要简单的阻塞在对 /dev/uioX的read()操作上。当设备产生中断时,read()操作立即返回。UIO 也实现了poll()系统调用,你可以使用 select()来等待中断的发生。select()有一个超时参数可以用来实现有限时间内等待中断。
对设备的控制还可以通过/sys/class/uio下的各个文件的读写来完成。你注册的uio设备将会出现在该目录下。假如你的uio设备是uio0那么映射的设备内存文件出现在 /sys/class/uio/uio0/maps/mapX,对该文件的读写就是对设备内存的读写。
如下的图描述了uio驱动的内核部分,用户空间部分,和uio 框架以及内核内部函数的关系。
UIO的具体实现
UIO内核部分
一个uio驱动的注册过程简单点说有两个步骤:
1. 初始化设备相关的 uio_info结构。
uio_info包括了name, open(),release(), mmap(), struct uio_mem等
2. 调用uio_register_device 分配并注册一个uio设备。
uio核心字符设备注册的函数有:
uio_open,uio_fasync,uio_release,uio_poll,uio_read, uio_write
当用户空间访问该uio设备的时候, 就会调用这些接口
3, Uio核心除了完成相关的维护工作外,还调用了注册在uio_info中的相关方法进行初始化。
Uio用户空间部分
通过open,mmap, write read等函数与内核空间交互, 间接和硬件的寄存器交互。
在内核空间驱动中所描述的内存区域uio_mem可以通过mmap()调用映射到用户空间中。
同时根据设备的使用规则,就可以编写驱动了
设备中断处理
在__uio_register_device中,为uio设备注册了统一的中断处理函数uio_interrupt,
在该函数中,调用了uio设备自己提供的中断处理函数handler(uio_info结构中)。
并调用了uio_event_notify函数对uio设备的中断事件计数器(listener)增一, 通知各个读进程
“有数据可读”。每个uio设备的中断处理函数都是单独注册的。
用户空间调用poll/(uio_poll)操作判断是否有数据可读的依据就是 listener中的中断事件计数值(event_count)和uio设备中的中断事件计数器值不一致(前者小于后者)。因为listener的值除了在执行文件打开操作时被置为被赋值外,只在uio_read操作中被更新为uio设备的中断事件计数器值。
阅读(5710) | 评论(0) | 转发(0) |