在linux下编写字符型io设备驱动程序,Linux高级字符设备驱动程序

设备ioctl控制

大部分驱动除了需要具备读写设备的能力外,还需要具备对硬件控制的能力。例如,要求硬件设备报告错误信息,改变波特率,这些操作常常通过ioctl方法来实现。

用户使用方法:在用户空间,使用ioctl系统调用来控制设备,原型如下:int ioctl(int fd,unsigned long cmd,...)

原型中的点表示这是一个可选参数,存在与否依赖于控制命令(第二个参数)是否涉及到与设备交互。

驱动ioctl方法:ioctl驱动方法有和用户空间版本不同的原型:int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg),cmd从用户空间传下来,可选的参数arg以一个unsigned long的形式传递,不管他是一个整数还是一个指针。如果cmd命令不涉及数据传输,则第三个参数arg的值毫无任何意义。

如何实现ioctl方法?步骤:定义命令,实现命令。

定义命令:在编写ioctl代码之前,首先需要定义命令。为了防止对错误的设备使用正确的命令,命令号应该在系统范围内是唯一的。ioctl命令编码被划分为几个位段,include/asm/ioctl.h中定义了这些位字段:类型(幻数),序号,传送方向,参数的大小。Documentation/ioctl-number.txt文件中罗列了在内核中已经使用了得幻数。

定义命令:定义ioctl命令的正确方法是使用4个位段,这个列表中介绍的符号定义在中:

(1)Type:幻数(类型),表明哪个设备的命令,在参考了ioctl-number.txt之后选出,8位宽。

(2)Number:序号,表明设备命令中的第几个,8位宽。

(3)Direction:数据传送的方向,可能的值使_IOC_NONE(没有数据传输),_IOC_READ,_IOC_WRITE。数据传送石从应用程序的观点来看的,_IOC_READ的意思是从设备读。

(4)Size:用户数据的大小。(13/14位宽,视处理器而定)

内核提供了下列宏来帮助定义命令:

(1)_IO(type,nr),没有参数命令

(2)_IOR(type,nr,datatype),从驱动中读数据

(3)_IOW(type,nr,datatype),写数据到驱动

(4)_IOWR(type,nr,datatype),双向传输,type和number成员作为参数被传递

6a3f6b5f0b29857d4ad92918607fef37.png

实现命令,Ioctl函数实现:

定义好了命令,下一步就是有要实现Ioctl函数了,Ioctl函数的实现包括如下3个技术环节:(1)返回值(2)参数使用(3)命令操作

(1)返回值:Ioctl函数的实现通常是根据命令执行的一个switch语句。但是,命令号不能通过匹配任何一个设备所支持的命令时,通常返回-EINVAL(“非法参数”)

(2)参数:如何使用Ioctl中的参数?如果是一个整数,可以直接使用。如果是指针,我们必须确保这个用户地址是有效的,因此使用前需进行正确的检查。

不需要检测:copy_from_user,copy_to_user,get_user,put_user

需要检测:__get_user,__put_user

用这个函数进行检测:int access_ok(int type,const void *addr,unsigned long size)

第一个参数是VERIFY_READ或者VERIFY_WRITE,用来表明是读用户内存还是写用户内存。addr参数是要操作的用户内存地址,size是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数等于sizeof(int),access返回一一个布尔值:1是成功(存取没有问题)和0是失败(存取有问题),如果该函数返回失败,则Ioctl应当返回-EFAULT。

67b822c0b7291c547d41336d9ff50415.png

146aec5da8e9f73d9380d50e93e803ae.png

43f429df724209c8e322831313ea1380.png

定义命令

95cdba18253dde7c2373a4aa2b48f914.png

命令实现

1286869d58d680414bfd238bb273bf10.png

内核等待序列

等待队列:在Linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列可看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,从等待队列中取出进程。

Linux2.6内核提供了如下等待队列的操作:

(1)定义等待队列:wait_queue_head_t my_queue

(2)初始化等待队列:init_waitqueue_head(&my_queue)

(3)定义并初始化等待队列:DECLARE_WAIT_QUEUE_HEAD(my_queue)

(4)有条件睡眠:wait_event(queue,condition),当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上;

wait_event_interruptible(queue,condition),当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数所指定的等待队列上。

int wait_event_killable(wait_queue_t queue,condition),当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。

(5)无条件睡眠(老版本,不建议使用)

sleep_on(wait_queue_head_t *q),让进程进入不可中断睡眠,并把它放入等待队列q。

interruptible_sleep_on(wait_queue_head_t *q),让进程进入可中断睡眠,并把它放入等待队列q。

(6)从等待队列中唤醒进程

wake_up(wait_queue_t *q),从等待队列q中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE的所有进程。

wake_up_interruptible(wait_queue_t *q),从等待队列q中唤醒状态为TASK_INTERRUPTIBLE的进程。

阻塞型设备驱动

功能:前一节我们在设计简单字符驱动程序时,跳过了一个重要的问题,当一个设备无法立刻满足用户的读写请求时应当如何处理?例如,调用read时没有数据可读,但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。应用程序通常不关心这种问题,应用程序只是调用read或write并得到返回值。驱动程序应当(缺省地)阻塞进程,使他进入睡眠,直到请求得到满足。

阻塞方式:在阻塞型驱动程序中,Read实现方式如下:如果进程调用read,但设备没有数据或数据不足,进程阻塞。当新数据到达之后,唤醒被阻塞进程。

非阻塞方式:阻塞方式是文件读写操作的默认模式,但应用程序可以通过使用O_NONBLOCK标志来人为的设置读写操作为非阻塞方式(该标志定义在中,在打开文件时指定)。如果设置了O_NONBLOCK标志,read和write的行为是不同的。如果进程在没有数据就绪时调用了read,或者在缓冲区没有空间时调用了write,系统只是简单的返回-EAGAIN,而不会阻塞进程。

实例演示:阻塞型字符驱动

838499b114b3eb6fa5a49e4d8fee06bb.png

Poll设备操作

Poll方法

什么是Poll方法?功能是什么?

de39efb33f0a0b1c77d255ed23f2876f.png

Select系统调用

Select系统调用用于多路监控系统当没有一个文件满足要求时,Select将阻塞调用进程。

e0863f5ad213631d3a3460aea2738e58.png

29d42e1fe539e014a61e5723de7eb411.png

2fbba979f8edf09696d01dc557404589.png

4f0d8069833a982b393ac58ece4db48a.png

Select系统调用(使用方法)

1.将要监控的文件添加到文件描述符集

2.调用Select开始监控

3.判断文件是否发生变化

b0c5d83913357496384f280e5888a9c2.png

3e65a303d773dc61862bb9a6088b7c14.png

Poll方法:应用程序常常使用select系统调用,他可能会阻塞进程。这个调用由驱动poll方法实现,原型为

1edd473a5a9ab76b4a5508ca0deac5d5.png

b11529be0d6a0dc4aa56ad70b1c6bc82.png

bd11a0b5b36070d119ed5e292f13474d.png

01fd4838b3517c188324f6a488979f03.png

自动创建设备文件

6a527d7d8e155fe97629e09e76a0d8e7.png

0ae1d2cee675485f9577e8416324a6ea.png

04c219707a4c61455d4866f9889aa0ba.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值