AUTHOR: Joseph Yang (杨红刚) <ganggexiongqi@gmail.com>
CONTENT: uio.c source code notes NOTE: linux-3.0 LAST MODIFIED:09-04-2011 -------------------------------------- Distributed and Embedded System Lab (分布式嵌入式系统实验室,兰州大学) ===============================================================================
uio.c 导出的可以在您的uio驱动中使用的函数: EXPORT_SYMBOL_GPL(uio_event_notify); EXPORT_SYMBOL_GPL(__uio_register_device); EXPORT_SYMBOL_GPL(uio_unregister_device); ------------------------------------------------------------------------------------------------- uio.c 函数列表 |- function || map_name_show || map_addr_show || map_size_show || map_offset_show || map_release || map_type_show || portio_name_show || portio_start_show || portio_size_show || portio_porttype_show || portio_release || portio_type_show || show_name || show_version || show_event || uio_dev_add_attributes || uio_dev_del_attributes || uio_get_minor || uio_free_minor || uio_event_notify || uio_interrupt || uio_open || uio_fasync || uio_release || uio_poll || uio_read || uio_write || uio_find_mem_index || uio_vma_open || uio_vma_close || uio_vma_fault || uio_mmap_physical || uio_mmap_logical || uio_mmap || uio_major_init || uio_major_cleanup || init_uio_class || release_uio_class || __uio_register_device || uio_unregister_device || uio_init || uio_exit ---------------------------一些重要的数据结构----------------------------------------------
------------------------------------ 函数的详细解释 --------------------------------------------- 1. 函数: static int __init uio_init(void) 功能:申请字符设备号,设备,并注册到系统中,注册uio_class到系统中 调用模块:init_uio_class() 执行流程: 申请字符设备号,设备,并注册到系统中,注册uio_class到系统中 //init_uio_class //创建"/sys/class/uio" 2. 函数:uio_exit 参数: 返回值: 功能:注销uio_class,注销字符设备编号,删除设备 调用模块:release_uio_class 执行流程: 注销uio_class,注销字符设备编号,删除设备 //release_uio_class 3. 函数:static void release_uio_class(void) 参数: 返回值: 功能:注销uio_class,注销字符设备编号,删除设备 调用模块: 执行流程: 注销uio_class//class_unregister 注销字符设备编号,删除设备 //uio_major_cleanup ------------------------------------------------- 4. 函数:static int init_uio_class(void) 参数: 返回值: 功能:申请字符设备号,设备,并注册到系统中,注册uio_class到系统中 调用模块: uio_major_init() class_register() 执行流程: 申请字符设备编号,设备,并初始化//uio_major_init 注册class 类型全局变量uio_class到系统//class_register //ls -l /sys/class 查看 5. 函数: static int uio_major_init(void) 参数: 返回值: 功能:申请字符设备编号,设备,并初始化 调用模块: alloc_chrdev_region() cdev_alloc() kobject_set_name() cdev_add() 执行流程: 申请字符设备编号(多个)//alloc_chrdev_region //2^UIO_MAX_DEVICES个从设备 //设备的名字为"uio" 分配一个表示字符设备的cdev结构//cdev_alloc 初始化cdev结构的file_operations类型字段//控制cdev设备的各种操作, // 如 open, close, read, write... 设置cdev结构的kobj字段的name为uio //kobject_set_name 添加字符设备到系统中 //cdev_add,调用成功后,我们的设备就“活了” // cat /proc/devices ,可以查看到分配到主设备号 保存主设备号到全局变量uio_major 保存设备指针到全局变量uio_cdev 返回 6. 函数:static void uio_major_cleanup(void) 参数: 返回值: 功能:注销字符设备编号,删除设备 调用模块:unregister_chrdev_region 执行流程: 注销字符设备编号//unregister_chrdev_region 删除设备uio_cdev //cdev_del ===== file_operations 7. 函数:static int uio_open(struct inode *inode, struct file *filep) 参数:inode: filep: 返回值: 功能:获得和次设备号关联的uio_device指针,创建一个辅助变量listener, 并调用 info指向的uio_info结构中的open方法 调用模块: 执行流程: 获得保护uio_idr的锁 //mutex_lock 从inode 结构中获取次编号 //iminor 获得和次编号关联的uio_device指针 //idr_find <----------<<<< 在那里进行地设置呢??? // 在 uio_get_minor 中分配的次设备编号并设置的关联 放弃锁 //mutex_unlock 增加uio_device类型指针指向的模块的引用计数 //try_module_get 分配一个uio_listener类型的listener //kmalloc 关联listener和 uio_device 指针 获得uio_device 指向设备的事件计数值,并存入listener //atomic_read 把listener指针保存到filep->private_data字段 调用uio_device的info字段指向的uio_info中的open方法//***** 8. 函数:static int uio_release(struct inode *inode, struct file *filep) 参数:inode filep 返回值: 功能:从而调用uio_device的字段info指向的uio_info中的release方法 释放辅助结构体listener 调用模块: 执行流程: 从filep->private_data中获得uio_open中保存的listener指针。 利用listener指针找到指向uio_device类型结构指针 从而调用uio_device的字段info指向的uio_info中的release方法。 减少uio_device类型指针指向的模块的引用计数//module_put 释放listener结构体 //kfree 9. 函数:static int uio_fasync(int fd, struct file *filep, int on) 参数: fd filep on : 0, 删除;非零,添加 返回值: 功能: 管理uio_device的async_queue 调用模块:fasync_helper() 执行流程: 从filep->private_data中获得uio_open中保存的listener指针。 利用listener指针找到指向uio_device类型结构指针 设置uio_device的async_queue//fasync_helper 10. 函数:static unsigned int uio_poll(struct file *filep, poll_table *wait) 参数: filep wait 返回值: 功能: 使进程在传递到该系统调用的所有文件描述符对应的等待队列上等待, 并返回一个是否可以立即无阻塞执行的位掩码 调用模块: 执行流程: 从filep->private_data中获得uio_open中保存的listener指针。 利用listener指针找到指向uio_device类型结构指针 判断用uio_device类型指针的info字段(uio_info类型)的irq成员不为0,则继续, 否则,返回IO错误 向poll_table类型的wait表中添加uio_device类型指针指向结构的wait等待队列//poll_wait //!!!! 注意poll_wait并不阻塞 如果listener中的事件计数值event_count和uio_device的 事件计数值count不一致时// uio_interrupt调用了uio_event_notify对 //中断事件计数器增一 返回“通常”的数据可读的位掩码 11. 函数:static ssize_t uio_read(struct file *filep, char __user *buf, size_t count, loff_t *ppos) 参数: filep buf count ppos 返回值: 功能:复制uio设备中断事件计数器的值到用户空间 调用模块: 执行流程: 从filep->private_data中获得uio_open中保存的listener指针 利用listener指针找到指向uio_device类型结构指针 创建一个等待队列的项 //DECLARE_WAITQUEUE 检查确认uio设备的设备info的中断号(0)不为零 添加本进程到uio设备的等待队列wait上 // add_wait_queue //由uio_interrupt调用uio_event_notify唤醒 REP: 设置当前进程的 "可中断标志" 检查是否有中断事件发生, 如果有(listener中的中断事件计数值event_count)和uio设备中的中断事件 计数器值不一致),则将设备中断计数器的值复制到用户空间 并将listener中的中断事件计数值更新为设备的中断事件计数值 把当前进程设置为TASK_RUNNING状态, 并将当前进程从uio设备的等待队列wait上删除 如果文件读时设置了O_NONBLOCK标志, 那么,把当前进程设置为TASK_RUNNING状态, 并将当前进程从uio设备的等待队列wait上删除 返回 -EAGAIN 检查当前进程是否有信号处理 //signal_pending //http://blog./space.php?uid=20746501&do=blog&cuid=1820175 如有,把当前进程设置为TASK_RUNNING状态, 并将当前进程从uio设备的等待队列wait上删除 并返回 -ERESTARTSYS 执行调度 //schedule JMP REP 12. 函数:static irqreturn_t uio_interrupt(int irq, void *dev_id) // <---------------<<<<<<<<<<被谁调用呢??? __uio_register_device中设定 参数: irq dev_id 返回值: 功能: 调用uio_info中注册的handler中断处理函数,对设备的中断事件计数器增一 并通知各读进程,有数据可读 调用模块: 执行流程: 从filep->private_data中获得uio_open中保存的listener指针 调用 uio_device类型指针的info字段(uio_info类型)的handler 如果属于本设备的中断,并且在handler中已经处理过 那么对设备的中断事件计数器增一, 并通知各读进程,有数据可读 //uio_event_notify 13. 函数:void uio_event_notify(struct uio_info *info) 参数: 返回值: 功能:“触发“ 一个中断事件,对设备的中断事件计数器增一,并通知各读进程,有数据 可读 调用模块: 执行流程: 从filep->private_data中获得uio_open中保存的listener指针 对中断事件计数器增一 唤醒阻塞在设备等待队列wait上的读进程 //wake_up_interruptible // 该队列上的进程在uio_read中添加 向异步等待队列async_queue发出可读信号 //kill_fasync 14. 函数:static ssize_t uio_write(struct file *filep, const char __user *buf, size_t count, loff_t *ppos) 参数: 返回值: 功能: 读取用户空间的值,并调用uio_device注册的irqcontrol函数 调用模块: 执行流程: 从filep->private_data中获得uio_open中保存的listener指针 调用 uio_device类型指针的info字段(uio_info类型)的handler 检验info字段(uio_info类型)的中断号irq 读取从用户空间传过来的32位的值//copy_from_user 调用info字段(uio_info类型)的irqcontrol函数,将用户空间传递过来的32位值 作为参数传入。 15. 函数:static int uio_mmap(struct file *filep, struct vm_area_struct *vma) 参数: 返回值: 功能: 调用模块: 执行流程: 从filep->private_data中获得uio_open中保存的listener指针 调用 uio_device类型指针的info字段(uio_info类型)的handler 保存uio_device类型指针到 vma 的vm_private_data 返回映射区域的索引(比如 mapX,的X)//uio_find_mem_index 计算实际的页数和请求的页数 如果实际的页数小于请求的页数那么,返回-EINVAL 如果uio设备注册有mmap函数,那么就调用它 当内存区域的类型为UIO_MEM_PHYS时, //uio_mmap_physical 当内存区域的类型为UIO_MEM_LOGICAL/UIO_MEM_VIRTUAL时, 为虚拟内存区域设置操作,和告诉内存不要将 该区域交换出去,访问计数器增一//uio_mmap_logical 备注:
16. 函数:static int uio_find_mem_index(struct vm_area_struct *vma) 参数: 返回值: 功能: 返回映射区域的索引(比如 mapX,的X) 调用模块: 执行流程: 返回虚拟内存区域在文件中以页为单位的偏移 //即vma->vm_pgoff // 其实 vma->vm_pgoff 被内核自动填充为了用户空间调用mmap时的最后一个参数offset>>page_size // 而uio用户空间编程要求mmap最后一个参数为N*getsizeofpage(),从而 vma->vm_pgoff为映射区域的索引 17. 函数:static int uio_mmap_logical(struct vm_area_struct *vma) 参数: 返回值: 功能: 为虚拟内存区域设置操作,和告诉内存不要将该区域交换出去,访问计数器增一 调用模块: 执行流程: 设置vma 标志VM_RESERVED告诉内存管理系统不要将VMA交换出去 设置vma的vm_ops // 对虚拟内存区的操作集合的设置 uio设备的虚拟内存区域访问计数器(vma_count)增一 // uio_vma_open 18. 函数:static void uio_vma_open(struct vm_area_struct *vma) 参数: 返回值: 功能:uio设备的虚拟内存区域访问计数器(vma_count)增一 调用模块: 执行流程: uio设备的虚拟内存区域访问计数器(vma_count)增一 19. 函数:static void uio_vma_close(struct vm_area_struct *vma) 参数: 返回值: 功能: 调用模块: 执行流程: uio设备的虚拟内存区域访问计数器(vma_count)减一 20. 函数:static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 参数: 返回值: 功能: 为失效的虚拟地址查找正确的page结构,并增加它的引用计数 调用模块: 执行流程: 从虚拟内存区域结构的私有数据字段获得uio设备描述结构的指针 返回映射区域的索引(比如 mapX,的X)//uio_find_mem_index 如果是UIO_MEM_LOGICAL类型的虚地址 由内核逻辑地址得到该页的描述结构的指针 //virt_to_page 如果是UIO_MEM_VIRTUAL类型的虚地址 有vmalloc得到的虚拟地址得到该页的描述结构//vmalloc_to_page 增加内存页的使用计数 //get_page 保存页结构的指针 备注: /* * We need to subtract mi because userspace uses offset = N*PAGE_SIZE * to use mem[N]. */ offset = (vmf->pgoff - mi) << PAGE_SHIFT; / 设备可以有多个内存映射块,offset为块内偏移。 21. 函数:static int uio_mmap_physical(struct vm_area_struct *vma) 参数: 返回值: 功能: 把内核空间的物理内存区域映射到用户空间 调用模块: 执行流程: 从虚拟内存区域结构的私有数据字段获得uio设备描述结构的指针 返回映射区域的索引(比如 mapX,的X) //uio_find_mem_index 设置虚拟内存区域的标志VM_IO ,VM_RESERVED //VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系 // 统将该区域包含在进程的存放转存(core dump)中, // VM_RESERVED标志内存区域不能被换出。 设置页的存取权限 //pgprot_nocached禁止了相关页的CACHE 和写缓冲( //位于处理器和主存之间,将处理器和cache从及 //较慢的主存写操作解脱出来的FIFO存储器) 把内核空间的内存区域映射到用户空间 //remap_pfn_range 22. 函数:static int uio_get_minor(struct uio_device *idev) 参数: 返回值: 功能:分配id号,并将id号和指针关联 调用模块: 执行流程: 获得保护uio_idr的锁 //mutex_lock 为idr分配内存 //idr_pre_get 分配id号,并将id号和指针关联 //idr_get_new // 在uio_open中用到了 将分配到的id保存到uio设备结构变量的minor字段 释放锁 //mutex_unlock 23. 函数:static void uio_free_minor(struct uio_device *idev) 参数: 返回值: 功能:从idr表中删除id与指针的关联 调用模块: 执行流程: 获得保护uio_idr的锁 //mutex_lock 从idr表中删除id与指针的关联 //idr_remove 释放uio_idr的锁 //mutex_unlock 24. 函数:static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) 参数: 返回值: 功能:显示uio设备的名字 调用模块: 执行流程: 获得保存在driver_data中的uio_device指针 //driver_data <-------------<<<<在那里设定??? 把uio设备的名字字符串写到buf中,将在用户空间看到它的值 25. 函数:static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf) 参数: 返回值: 功能:显示 uio设备的版本 调用模块: 执行流程: 26. 函数:static ssize_t show_event(struct device *dev, struct device_attribute *attr, char *buf) 参数: 返回值: 功能:显示uio设备中断计数器的值 调用模块: 执行流程: 27,28,29,30 //map <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 功能: 显示kobject对象的属性 map_name_show map_addr_show map_size_show map_offset_show 31. 函数:static void map_release(struct kobject *kobj) 参数: 返回值: 功能:释放uio_map结构 调用模块: 执行流程: 得到包含kobj的 uio_map 结构的指针 // to_map 释放uio_map结构 32. 函数: static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr, char *buf) 参数: 返回值: 功能:调用 map_name_show, map_addr_show, map_size_show,map_offset_show方法 显示mem map的各种属性 调用模块: 执行流程: 得到包含kobj的 uio_map 结构的指针 // to_map 得到uio_map的uio_mem类型成员的指针 得到包含attribute 的map_sysfs_entry结构的指针// 调用map_sysfs_entry的show函数 33, 34, 35, 36 功能: 显示 io 区域的属性 portio_name_show, portio_start_show, portio_size_show, portio_porttype_show 37. 函数:static void portio_release(struct kobject *kobj) 参数: 返回值: 功能: 释放uio_portio结构 调用模块: 执行流程: 得到包含kobj的 uio_portio 结构的指针 // to_map 释放uio_portio结构 38. 函数:static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr, char *buf) 参数: 返回值: 功能: 调用 portio_name_show, 显示 io region 的属性 调用模块:执行流程: 得到包含kobj的 uio_portio 结构的指针 // to_map 得到uio_portio内嵌的uio_port结构体的指针 得到包含attr 的portio_sysfs_entry类型的指针 调用show方法 39. 函数:static int uio_dev_add_attributes(struct uio_device *idev) 参数: 返回值: 功能:在sysfs中注册 内存映射和端口io区域 相关的文件和文件夹,以及相关属性。 调用模块: 执行流程: 内存映射 在sysfs中的注册: create a struct kobject “maps”dynamically and register it with sysfs//kobject_create_and_add // "uio/maps" directory created 分配uio_map 类型变量map 初始化map的obj成员,并为之添加属性// kobject_init 关联 uio_map类型变量map和uio_mem类型变量mem //把mem地址保存在uio_map类型变量的成员中 //把map地址保存在mem类型变量的成员中 在uio/uioX/maps下再创建mapX 对象//kobject_create_and_add,"uio/uioX/maps/mapX" 通知用户空间"uio/uioX/maps/mapX"可用 //kobject_uevent 端口io区域在 sysfs中的注册: create a struct kobject “portio”dynamically and register it with sysfs//kobject_create_and_add // "uio/uioX/portio" directory created 分配uio_portio 类型变量portio 初始化map的portio成员,并为之添加属性// kobject_init 关联 uio_portio类型变量portio和uio_port类型变量port //把port地址保存在uio_portio类型变量的成员中 //把portio地址保存在port类型变量的成员中 在uio/uioX/portio下再创建portX对象//kobject_create_and_add,"uio/uioX/portio/portX" 通知用户空间"uio/uioX/portio/portX"可用 //kobject_uevent 40. 函数:static void uio_dev_del_attributes(struct uio_device *idev) 参数: 返回值: 功能: 删除在uio_dev_add_attributes中,添加的kobject对象, 从而删除在uio目录下的各个文件和文件夹 调用模块: 执行流程:... 41. 函数:int __uio_register_device(struct module *owner, struct device *parent, struct uio_info *info) 参数:owner: module that creates the new device parent: parent device info: UIO device capabilities 返回值: 功能:register a new userspace IO device 调用模块: 执行流程: 检查 parent,和info的name,和version,确保不为空 分配uio_device型变量的空间 初始化uio设备的等待队列 初始化uio设备的中断事件计数器event为0//atomic_set 分配id号,并将id号和指针关联 //uio_get_minor creates a device and registers it with sysfs//device_create //在uio class目录下创建 uioX,即'uio/uioX' 在sysfs中注册 内存映射和端口io区域 相关的文件和文件夹, 以及相关属性。 //uio_dev_add_attributes 将完成初始化的uio_device的指针保存在uio_info类型变量的uio_dev中 为uio_device注册中断函数uio_interrupt //request_irq 42. 函数:void uio_unregister_device(struct uio_info *info) 参数: 返回值: 功能:卸载uio设备 调用模块: 执行流程: 从idr表中删除uio_device 的id 与指针的关联//uio_free_minor 删除在uio_dev_add_attributes中,添加的kobject对象,// uio_dev_del_attributes 从而删除在uio目录下的各个文件和文件夹 删除设备//device_destroy 释放uio_device 类型结构的空间//kfree =================================================================== 参考: 1. Documentation/DocBook/uio-howto.tmpl 2. drivers/uio/uio.c 3. vm_area_struct http://blog.csdn.net/ganggexiongqi/article/details/6746248 4. 浅析linux内核中的idr机制 http://blog.csdn.net/ganggexiongqi/article/details/6737389 |
|
来自: lifei_szdz > 《UIO子系统》