Linux 设备驱动之 UIO 机制(基本概念)
一个设备驱动的主要任务有两个:
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 框架以及内核内部函数的关系。
struct uio_portio {
struct kobject kobj;
struct uio_port *port;
};
/**
* struct uio_port - description of a UIO port region
* @name: name of the port region for identification
* @start: start of port region
* @size: size of port region
* @porttype: type of port (see UIO_PORT_* below)
* @portio: for use by the UIO core only.
*/
struct uio_port {
const char *name;
unsigned long start;
unsigned long size;
int porttype;
struct uio_portio *portio;
};
/* defines for uio_port->porttype */
#define UIO_PORT_NONE 0
#define UIO_PORT_X86 1
#define UIO_PORT_GPIO 2
#define UIO_PORT_OTHER 3
/*
* struct uio_mem - description of a UIO memory region
* @name: name of the memory region for identification
* @addr: address of the device's memory
* @size: size of IO
* @memtype: type of memory addr points to
* @internal_addr: ioremap-ped version of addr, for driver internal use
* @map: for use by the UIO core only.
*/
struct uio_mem {
const char *name;// 内存映射的名字
unsigned long addr; // 内存块的地址
unsigned long size; //addr所指向的内存块的大小
int memtype; //UIO_MEM_PHYS,UIO_MEM_LOGICAL(kmalloc()),UIO_MEM_VIRTUAL( virtual memory)
void __iomem *internal_addr; // If you have to access this memory region from within your kernel module,
// you will want to map it internally by using something like ioremap().
struct uio_map *map;
};
struct uio_map {
struct kobject kobj;
struct uio_mem *mem;
};
static const struct vm_operations_struct uio_vm_ops = {
.open = uio_vma_open,
.close = uio_vma_close,
.fault = uio_vma_fault,
};
static struct device_attribute uio_class_attributes[] = {
__ATTR(name, S_IRUGO, show_name, NULL),
__ATTR(version, S_IRUGO, show_version, NULL),
__ATTR(event, S_IRUGO, show_event, NULL),
{}
};
/* UIO class infrastructure */
static struct class uio_class = {
.name = "uio",// /sys/class/uio
.dev_attrs = uio_class_attributes,
};
static const struct file_operations uio_fops = {
.owner = THIS_MODULE,
.open = uio_open,
.release = uio_release,
.read = uio_read,
.write = uio_write,
.mmap = uio_mmap,
.poll = uio_poll,
.fasync = uio_fasync,
.llseek = noop_llseek,
};
/* Protect idr accesses */
static DEFINE_MUTEX(minor_lock);
static DEFINE_IDR(uio_idr);
//关于idr机制。參见 http://blog.csdn.net/ganggexiongqi/article/details/6737389
struct uio_device {
struct module *owner;
struct device *dev; //在__uio_register_device中初始化
int minor; // 次设备id号,uio_get_minor
atomic_t event; //中断事件计数
struct fasync_struct *async_queue;//该设备上的异步等待队列//
// 关于 “异步通知“ //參见LDD3第六章
wait_queue_head_t wait; //该设备上的等待队列,在注冊设备时(__uio_register_device)初始化
int vma_count;
struct uio_info *info;// 指向用户注冊的uio_info,在__uio_register_device中被赋值的
struct kobject *map_dir;
struct kobject *portio_dir;
};
/*
* struct uio_info - UIO device capabilities
* @uio_dev: the UIO device this info belongs to
* @name: device name
* @version: device driver version
* @mem: list of mappable memory regions, size==0 for end of list
* @port: list of port regions, size==0 for end of list
* @irq: interrupt number or UIO_IRQ_CUSTOM
* @irq_flags: flags for request_irq()
* @priv: optional private data
* @handler: the device's irq handler
* @mmap: mmap operation for this uio device
* @open: open operation for this uio device
* @release: release operation for this uio device
* @irqcontrol: disable/enable irqs when 0/1 is written to /dev/uioX
*/
struct uio_info {
struct uio_device *uio_dev; // 在__uio_register_device中初始化
const char *name; // 调用__uio_register_device之前必须初始化
const char *version; //调用__uio_register_device之前必须初始化
struct uio_mem mem[MAX_UIO_MAPS];
struct uio_port port[MAX_UIO_PORT_REGIONS];
long irq; //分配给uio设备的中断号,调用__uio_register_device之前必须初始化
unsigned long irq_flags;// 调用__uio_register_device之前必须初始化
void *priv; //
irqreturn_t (*handler)(int irq, struct uio_info *dev_info); //uio_interrupt中调用。用于中断处理
// 调用__uio_register_device之前必须初始化
int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); //在uio_mmap中被调用,
// 运行设备打开特定操作
int (*open)(struct uio_info *info, struct inode *inode);//在uio_open中被调用,运行设备打开特定操作
int (*release)(struct uio_info *info, struct inode *inode);//在uio_device中被调用。运行设备打开特定操作
int (*irqcontrol)(struct uio_info *info, s32 irq_on);//在uio_write方法中被调用,运行用户驱动的
//特定操作。
};
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_class3、函数:static void release_uio_class(void)
功能:注销uio_class,注销字符设备编号,删除设备
运行流程:
注销uio_class//class_unregister
注销字符设备编号。删除设备 //uio_major_cleanup4、函数: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结构体 //kfree9、 函数: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_helper10、函数: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
//uid=20746501&do=blog&cuid=1820175">http://blog.chinaunix.net/space.php?
uid=20746501&do=blog&cuid=1820175
如有,把当前进程设置为TASK_RUNNING状态,
并将当前进程从uio设备的等待队列wait上删除
并返回 -ERESTARTSYS
运行调度 //schedule
JMP REP12、uio_register_device
功能: 调用uio_info中注冊的handler中断处理函数,对设备的中断事件计数器增一并通知各读进程。有数据可读
运行流程:
从filep->private_data中获得uio_open中保存的listener指针
调用 uio_device类型指针的info字段(uio_info类型)的handler
假设属于本设备的中断,而且在handler中已经处理过
那么对设备的中断事件计数器增一,
并通知各读进程,有数据可读 //uio_event_notify13、函数:void uio_event_notify(struct uio_info *info)
功能:“触发“ 一个中断事件。对设备的中断事件计数器增一,并通知各读进程,有数据可读
运行流程:
从filep->private_data中获得uio_open中保存的listener指针
对中断事件计数器增一
唤醒堵塞在设备等待队列wait上的读进程 //wake_up_interruptible
// 该队列上的进程在uio_read中加入
向异步等待队列async_queue发出可读信号 //kill_fasync14、 函数: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
Linux 设备驱动之 UIO 机制(基本概念)的更多相关文章
- Linux 设备驱动之 UIO 机制
一个设备驱动的主要任务有两个: 1. 存取设备的内存 2. 处理设备产生的中断 对于第一个任务.UIO 核心实现了mmap()能够处理物理内存(physical memory),逻辑内存(logica ...
- Linux设备驱动之semaphore机制【转】
转自:http://blog.csdn.net/xiao229404041/article/details/7031776 Linux设备驱动之semaphore机制在Linux系统中,信号号是一种重 ...
- linux设备驱动编写_tasklet机制
在编写设备驱动时, tasklet 机制是一种比较常见的机制,通常用于减少中断处理的时间,将本应该是在中断服务程序中完成的任务转化成软中断完成. 为了最大程度的避免中断处理时间过长而导致中断丢失,有时 ...
- linux设备驱动编写_tasklet机制(转)
在编写设备驱动时, tasklet 机制是一种比较常见的机制,通常用于减少中断处理的时间,将本应该是在中断服务程序中完成的任务转化成软中断完成. 为了最大程度的避免中断处理时间过长而导致中断丢失,有时 ...
- 【转】Linux设备驱动--块设备(一)之概念和框架
原文地址:Linux设备驱动--块设备(一)之概念和框架 基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时 ...
- linux设备驱动归纳总结(二):模块的相关基础概念【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59415.html linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10 ...
- linux设备驱动归纳总结(一)内核的相关基础概念【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59413.html linux设备驱动归纳总结(一):内核的相关基础概念 xxxxxxxxxxxxxx ...
- 【Linux高级驱动】linux设备驱动模型之平台设备驱动机制
[1:引言: linux字符设备驱动的基本编程流程] 1.实现模块加载函数 a.申请主设备号 register_chrdev(major,name,file_operations); b.创 ...
- (转载)小白的linux设备驱动归纳总结(一):内核的相关基础概念---学习总结
1. 学习总结 小白的博客讲的linux内核驱动这一块的东西比较基础,因此想通过学习他的博客,搭配着看书的方式来学习linux内核和驱动.我会依次更新在学习小白的博客的过程的感悟和体会. 2.1 内核 ...
随机推荐
- CF1042F Leaf Sets (贪心+树上构造)
题目大意:给你一棵树,让你对叶节点分组,保证每组中,任意两个叶节点之间的距离不大于K,求最小的组数 手动yy的贪心竟然对的 对于每个节点,维护一个$ma[i]$,表示在$i$节点的子树内 未被分组的叶 ...
- 论文阅读笔记“Attention-based Audio-Visual Fusion for Rubust Automatic Speech recognition”
关于论文的阅读笔记 论文的题目是“Attention-based Audio-Visual Fusion for Rubust Automatic Speech recognition”,翻译成中文为 ...
- spring boot启动原理步骤分析
spring boot最重要的三个文件:1.启动类 2.pom.xml 3.application.yml配置文件 一.启动类->main方法 spring boot启动原理步骤分析 1.spr ...
- github上Devstack的一些变动,截至8.20
从github下直接clone下来的代码在执行之前须要对一些文件进行改动,否则会出现关于REQUIREMENTS的错误 说明:代码前边是"-"号的,须要删除,代码前边是" ...
- C++基础学习教程(三)
承接上一讲. 2.7文件I/O 关于读写文件,C++中有一个专门的头文件<fstream>. 首先是读文件演示样例,例如以下: </pre><pre> /***** ...
- Go语言核心之美 1.1-命名篇
命名篇 1.Go的函数.变量.常量.自己定义类型.包(Package)的命名方式遵循以下规则: 1)首字符能够是随意的Unicode字符或者下划线 2)剩余字符能够是Unicode字符.下划线.数字 ...
- 广播BroadcastReceiver(2)
有序广播的优先级: 发送有序广播的方法有: public void sendOrderedBroadcast(Intent intent,String receiverPermis ...
- Codeforces Round #252 (Div. 2)-C,D
C题就是一个简单的模拟.首先给每一个人两个.然后把剩下的都给一个人就好了. 给的时候蛇形给. #include<stdio.h> #include<string.h> #inc ...
- (插播)关于使用jenkins + unity +Xcode 来进行自己主动打包的处理。
近期了解了下jenkins流程化服务的东西,个人感觉jenkins是一个非常方便的工具.主要是方便.设置好流程性得命令.仅仅需确定下就能够达到自己主动化. 减轻了错误得发生和简化了带来的复杂得步骤.今 ...
- Forms authentication timeout vs sessionState timeout
https://stackoverflow.com/questions/17812994/forms-authentication-timeout-vs-sessionstate-timeout Th ...