linux键盘驱动注释.doc_第1页
linux键盘驱动注释.doc_第2页
linux键盘驱动注释.doc_第3页
linux键盘驱动注释.doc_第4页
linux键盘驱动注释.doc_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

键盘在所有的驱动之中最为简单的一种,但它却包含了驱动的基本框架,对以后继续深入学习其他复杂的驱动大有裨益,以下便为你逐步剖析驱动的开发。采用的是查询方式。转载请注明出处:一.内核模块的注册和撤销在加载模块的时候,首先运行的是内核模块的注册函数。它的功能包括内核注册设备以及变量的初始化。static int head,tail;int _init Keypad_init(void)int result;result=register_chrdev(KEY_LED_MAJOR,KEY_LED_NAME,&Keypad_fops);Keypad_clear();init_waitqueue_head(&queue);prink(%s %s initialized.n,KEY_LED_NAME,KEY_LED_VERSION);/不能用prinfreturn 0;module_init(Keypad_init);/加载模块void _exit Keypad_cleanup(void)del_timer(&timer);unregister_chrdev(KEY_LED_MAJOR,KEY_LED_NAME);prink(Keypad driver removed n);module_exit(Keypad_cleanup);/卸载该模块二.虚拟文件系统与硬件驱动的接口static struct file_operations Keypad_fops=open:Keypad_open,read:Keypad_read,poll:Keypad_poll,fasync:Keypad_fasync,release:Keypad_release,;该接口定义完之后一些便是对这几个具体函数的实现了!现在我们一起进入下一步吧,是不是觉得其实没什么难度的呢?别那么早开心着呢?这几个函数的实现时候,涉及到很多技术,包括内核定时器,*等待队列的具体实现(阻塞方式),异步方式的具体实现技巧,循环队列。看到这么多技术你是否感到很兴奋呢?以下本人将以通俗的方式为你讲解,希望你能理解。三.设备的打开操作接口函数具体实现(Keypad_open)设备打开一般包括两大操作,一是完成设备的初始化,二是设备引用计数器加1static int Keypad_open(struct inode *inode,struct file *filp)read_xy();try_module_get(THIS_MODULE);/此函数为Linux 2.6内核增加的,不同于2.4内核,功能是计数器的值加1return 0;static void read_xy(void)new_data();/获取键值函数keypad_starttimer();/开启内核定时器,在固定周期时间内获取键盘新的变化以下实现键盘键值获取函数read_xy()主要是从KEY_CS(对应的读入地址,之前可以根据具体的硬件设备定义,比如define kEY_CS(*(volatile unsigned short *)(0xf820000)此处应该根据具体的不同而不同!将读入的键值存入buf缓存中,环形缓冲的写指针是head,读指针是tail,前面已经定义过了/键盘事件的数据结构定义/typedef structulong status;/按键的值ulong click;/是否有按键按下,1表示有,0表示没有KEY_EVENTstatic KEY_EVENT cur_data,bufBUFSIZE;/BUFSIZE为宏定义,用于定义环形缓冲的大小static void new_data(void)if(KEY_CS & 0xff)!=0xff) /从KEY_CS地址读入数据,若有一个为0则表示有一个按键被按下了(此处硬件电路为低电平有效)switch(KEY_CS & 0xff)case KEY0 & 0xff:cur_data.status=1;/1被按下break;case KEY1 & 0xff:cur_data.status=2;/2被按下break;/其他一样添加,懂吗?cur_data.click=1;else if(KEY_CS & 0xff=0xff)cur_data.click=0;cur_data.status=0;if(head!=tail)/循环队列缓冲区的应用在此开始了_int last=head-;if(lastf_flags & O_NONBLOCK)/假如用户采用的是非堵塞方式读取return _EAGAIN;add_wait_queue(&queue,&wait);/将当前进程加入等待队列current-state=TASK_INTERRUPTIBLE;/设置当前进程的状态while(head=tail)&!signal_pending(current)/假若还没有数据到循环队列并且当前进程没有受到信号shedule();/进程调度current-state=TASK_INTERRUPTIBLE;current-state=TASK_RUNNING;remove_wait_queue(&queue,&wait);if(head=tail)return count;t=get_data();/调用get_data()函数,得到缓冲区中的数据,下面将给予详细的 介绍out_buf0=t.status;out_buf1=t.click;copy_to_user(buf,&out_buf,sizeof(out_buf);/将得到的键值拷贝到用户数据区return count;很自然我们就应该要介绍get_data()函数的实现了,该函数的功能就是从我们定义的循环队列缓冲区中读出我们要的键值,所以其实很简单的如果理解循环队列的原理,在此不多加解释,大家应该具备一般的数据结构相关的知识吧static KEY_EVENT get_data(void)int last=tailif(+tail=BUFSIZE)tail=0;return buflast;上面如果你看得懂得话,那么可以进入下面的学习了,主要介绍的是内核定时器的使用,利用等待队列实现阻塞型I/O,poll系统调用,异步通知方式,介绍完之后,我将给出一个应用实例,对于有使用过文件操作系统调用的来说,对我们所写的键盘驱动来说,他们基本上是一样的。废话少说,我们马上开始我们精彩的驱动开发!六.内核定时器的使用在该驱动中,我们假设对键盘的获取是以0.2s为周期执行。源代码如下static struct timer_list timer;/我们定义的定时器,也许你会问timer_list是什么来的,其实一看名称就应该就知道了,而为什么要用到list那么多定时器呢?其实在Linux中还有很多相同的定义,比如说信号,我们定义的也是信号集,你可以定义该list是一个元素的,也可以是多个的。所以对于timer_list就可以这样描述:在未来某一个特定时刻执行某一系列特定任务的功能。下面我们还会给出内核中timer_list的具体描述,_好像我的话又说多了static int Keypad_starttimer(void)init_timer(&timer);/初始化定时器结构timer.function=Keypad_timer;/超时服务程序timer.expires=jiffies+20;/当前时刻加0.2sadd_timer(&timer);return 0;/超时服务程序static void Keypad_timer(unsigned long data)read_xy();/接下来说下timer-list这个数据结构,如果你不感兴趣的话可以跳过,该结构在includeLinuxtimer.h中定义struct timer_liststruct list_head entry;unsigned long expries;spinlock_t lock;unsigned long magic;void (*function)(unsigned long);unsigner long data;struct tvec_t_base_s *base;七.利用等待队列实现阻塞型IO在用户程序执行读操作的时候有可能尚且没有数据可以读取,为此需要让read操作等待,直到有数据可以读取,这就是阻塞型io,阻塞型io可以通过使用进程休眠方法实现。在无数据可以读取的时候,采用等待队列让进程休眠,直到有数据到达的时候才唤醒进程完成数据的读操作。在本驱动中的read,若循环队列缓冲区中没有数据,则进程进入休眠态,定时器函数每隔0.2s读取键值一次,将按键状态放入缓冲并且适时唤醒进程读取数据。等待队列的使用流程如下:1.声明一个等待队列2.把当前进程加入到等待队列中3.把进程的状态设置为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE;4.调用schedule,以让出cpu5.检测所需要的资源是否可用,若是,把当前进程从等待队列中删除,否则转3循环接下来我们在对read中有关等待队列阻塞实现做具体的解释static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l)DECLEARE_WAITQUEUE(wait,current);/声明等待队列,将当前进程加入到等待队列中KEY_EVENT t;ulong out_buf2;if(head=tail)/当前循环队列中没有数据可以读取if(filp-f_flags & O_NONBLOCK)/假如用户采用的是非堵塞方式读取return _EAGAIN;add_wait_queue(&queue,&wait);/将当前进程加入等待队列current-state=TASK_INTERRUPTIBLE;/设置当前进程的状态while(head=tail)&!signal_pending(current)/假若还没有数据到循环队列并且当前进程没有受到信号(该类信号具体来说是未决的休眠)shedule();/进程调度current-state=TASK_INTERRUPTIBLE;current-state=TASK_RUNNING;/该进程恢复执行remove_wait_queue(&queue,&wait);/移出等待队列if(head=tail)return count;t=get_data();/调用get_data()函数,得到缓冲区中的数据,下面将给予详细的 介绍out_buf0=t.status;out_buf1=t.click;copy_to_user(buf,&out_buf,sizeof(out_buf);/将得到的键值拷贝到用户数据区return count;写得有些累了,等下再继续了,原来写这些东西真的是不容易得,平时看到网络上人家写了很多东西,不知道原来是那么累的,但是我还是会继续的八.poll系统调用操作接口函数当程序需要进行对多个文件读写时,如果某个文件没有准备好,则系统就会处于读写阻塞的状态,这影响了其他文件的读写,为了避免读写阻塞,一般可以在应用程序中使用poll或者select函数。当poll函数返回时,会给出一个文件是否可读写的标志,应用程序根据不同的标志读写相应的文件,实现非阻塞的读写,poll()函数通过poll系统调用,调用对应设备驱动的poll()接口函数,poll返回不同的标志,告诉主进程文件是否可以读写,这些返回标志存放在includeasmpoll.h中标志含义POLLIN如果设备无阻塞的读,就返回该值POLLRDNORM通常的数据已经准备好,可以读了,就返回该值。通常的做法是会返回(POLLLIN|POLLRDNORA)POLLRDBAND如果可以从设备读出带外数据,就返回该值,它只可在Linux内核的某些网络代码中使用,通常不用在设备驱动程序中POLLPRI如果可以无阻塞的读取高优先级(带外)数据,就返回该值,返回该值会导致select报告文件发生异常,以为select八带外数据当作异常处理POLLHUP当读设备的进程到达文件尾时,驱动程序必须返回该值,依照select的功能描述,调用select的进程被告知进程时可读的。POLLERR如果设备发生错误,就返回该值。POLLOUT如果设备可以无阻塞地些,就返回该值POLLWRNORM设备已经准备好,可以写了,就返回该值。通常地做法是(POLLOUT|POLLNORM)POLLWRBAND于POLLRDBAND类似在本章地驱动程序中,Keypad_poll()函数在缓冲区有新数据时(当head!tail),返回一个POLLIN|POLLRDNORM,告诉主进程有新的九.在设备驱动中实现异步通知虽然大多数时候阻塞型和非阻塞型操作的组合及poll方法可以有效查询设备是否可以读写,但是如果驱动程序能避免主动的查询,改主动为被动的信号通知触发,则可以提高程序的效率,这也就是异步通知的目的。异步通知向进程发送SIGIO信号,通知访问设备的进程,表示该设备已经准备好IO读写了。之后就是如何实现异步通知的问题了,要启动异步通知,必须执行两个步骤:首先,须要制定某个作为文件的“属主”。文件属主的进程ID保存在filp-f_owner中,这可以通过fcntl()系统调用执行F_SETOWN命令设置。此外,用户程序还必须曙色之设备的FASYNC标志,以真正启动异步通知机制。这里的FASYNC标志也使用fcntl()设置。在完成这两个步骤之后,当新数据到达时就会产生一个SIGNO信号,此信号发送到存放在filp-owner中的进程。从驱动的角度看,则主要时通过调用两个内核提供的函数来实现就是了。他们分别是:int fasync_helper()和void kill_fasync();这两个函数定义在:includeLinuxfsfcntl.h要实现异步,驱动中只要如下编写即可static struct fasync_struct *fasync;/首先是定义一个结构体static int Keypad_release(struct inode *inode,struct file *filp)Keypad_fasync(-1,filp,0);/这是一个异步通知。static int Keypad_fasync(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论