1. 存取设备的内存
2. 处理设备产生的中断
对于第一个任务,UIO 核心实现了mmap()可以处理物理内存(physical memory),逻辑内存(logical memory),
虚拟内存(virtual memory)。UIO驱动的编写是就不需要再考虑这些繁琐的细节。
第二个任务,对于设备中断的应答必须在内核空间进行。所以在内核空间有一小部分代码
用来应答中断和禁止中断,但是其余的工作全部留给用户空间处理。
如果用户空间要等待一个设备中断,它只需要简单的阻塞在对 /dev/uioX的read()操作上。
当设备产生中断时,read()操作立即返回。UIO 也实现了poll()系统调用,你可以使用
select()来等待中断的发生。select()有一个超时参数可以用来实现有限时间内等待中断。
对设备的控制还可以通过/sys/class/uio下的各个文件的读写来完成。你注册的uio设备将会出现在该目录下。
假如你的uio设备是uio0那么映射的设备内存文件出现在 /sys/class/uio/uio0/maps/mapX,对该文件的读写就是
对设备内存的读写。
如下的图描述了uio驱动的内核部分,用户空间部分,和uio 框架以及内核内部函数的关系。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
-
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_delfile_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)
功能:从而调用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->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)
功能:复制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.chinaunix.net/space.php?uid=20746501&do=blog&cuid=1820175
如有,把当前进程设置为TASK_RUNNING状态,
并将当前进程从uio设备的等待队列wait上删除
并返回 -ERESTARTSYS
执行调度 //schedule
JMP REP -
12、uio_register_device
功能: 调用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
-
顶
- 2
-
踩