Chinaunix首页 | 论坛 | 博客
  • 博客访问: 71158
  • 博文数量: 13
  • 博客积分: 302
  • 博客等级: 二等列兵
  • 技术积分: 210
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-27 18:51
文章分类

全部博文(13)

文章存档

2012年(1)

2011年(12)

我的朋友

分类: 嵌入式

2011-03-29 12:44:07

这个文档重在阐述TQ2440开发板上的按键驱动,该驱动是作为字符设备的.我们将从思路阐述.典型的数据/函数结构分析和一些注意事项三个方面阐述这个问题,最后附录上源代码.该代码是在开发板上调试通过的。
硬件环境:TQ2440
内核版本:linux-2.6.32
文件系统:busybox-1.13.1
交叉工具:arm-2009q1,codesource出品
效果图:

一、作为字符设备的按键驱动
在这里,先陈述几个观点,如果不赞同这几个观点的,这个文档没必要认真砍下去了,因为接下来的code就只基于这几个观点的,当然本人的观点未必正确,欢迎大家发表讨论。
a. 按键驱动,必须具备去除噪声的能力,通俗一点,就是必须具备去抖动的能力。
对于这个,我们可以通过定时器实现,本文将提供3种方案,实现该功能,由于处在Linux下,因此因此都是和定时器相关的,但其中有一种并未直接用到定时器,算是个人的一点点创新,有缺点,有优点,也一并附录上,欢迎大家评论。这些都是经过调试通过的---当然这些不会是单片机上这种被人鄙视的延时
  1. uchar i=0xff;
  2. while(i-->0){};
b. 按键驱动,必须能够如实反应按键值
所谓按键值,可以是一组数据结构,包含一些被我们认为有意义的数据成员.个人认为一个典型的按键值VALUE应该包含按键ID,按键事件EV,按键的数据VAR
所谓按键ID很好理解,就是我们给按键设置的代号;按键事件,就是放松或者按下等;按键数据,就是按下去对应的pin脚电平值.在我们的这个文档中,相关定义如下
  1. //00000000稳定的按下的pin value
  2. //00000001稳定的释放状态pin value
  3. #define VAR_KEY_DN (0x00<<0)
  4. #define VAR_KEY_UP (0x01<<0)
  5. //稳定的按下状态
  6. #define EV_KEY_DN (0x01<<2)
  7. //定义按键id
  8. #define ID_KEY_1 (0x01<<4)
  9.  //value=ID_KEY_1 | EV_KEY_DN | VAR_KEY_DN
c. 按键驱动,必须能够记忆按键值的能力
按键驱动,不单要能记录某一次按键的值,还需要记录过去多少次的按键的值,这个,可以通过数组实现,在实现过程中,我们采用了环形数据结构,能够非常轻巧地识别按键值的先后顺序

d. 按键驱动,应该自从加载以后就开始工作,而不是等待调用open方法才开始工作
这个和时下很多按键驱动有写向左,欢迎大家一起参加探讨。个人认为,按键驱动工作,记录按键事件的时间,应该是在从init开始,而不是open
无论打开,还是关闭,这个按键事件在那里,不移不异,所以,按键驱动的工作时间,与open应该无关   o/\o

好了,以上就是基本的观点,现在进入code阶段

二 、 Code

  1. /*

  2. @按键驱动程序

  3. @针对S3C2440的驱动程序

  4. @2011.3.20,keytounix

  5. @hi.baidu.com/andio

  6. */

  7. //包含重要的和常用的头文件

  8. #include <linux/module.h>
  9. #include <linux/kernel.h>
  10. #include <linux/init.h>
  11. #include <linux/sched.h>
  12. #include <linux/poll.h>
  13. #include <linux/timer.h>
  14. #include <linux/irq.h>
  15. #include <linux/cdev.h>
  16. #include <linux/wait.h>//waitqueue
  17. #include <linux/errno.h>
  18. #include <linux/interrupt.h>//irq
  19. #include <linux/io.h>
  20. #include <linux/types.h>
  21. #include <linux/delay.h>
  22. #include <linux/semaphore.h>//sem
  23. #include <linux/fs.h>
  24. #include <linux/platform_device.h>//
  25. #include <linux/miscdevice.h>

  26. #include <linux/device.h>
  27. #include <linux/workqueue.h>//workqueue

  28. #include <asm/uaccess.h>
  29. #include <asm/irq.h>

  30. //soc相关

  31. #include <mach/gpio.h>
  32. #include <mach/regs-clock.h>
  33. #include <mach/regs-gpio.h>
  34. #include <mach/hardware.h>
  35. #include <mach/irqs.h>





  36. //////////////////////////////

  37. #define u8 unsigned char

  38. #define u16 unsigned short

  39. #define uint unsigned int

  40. #define SZ_BUF 16

  41. #define VAR_INIT 0xff

  42. #define CMD_RESET 0xff

  43. #define TM1000MS 100

  44. //这个 宏获得t[n]的n数目
  45. #define get_elem_num(t) (sizeof(t)/sizeof(t[0]))

  46. ////////////////////////////////////
  47. //debug info

  48. //这是一个debug宏,很有意思的

  49. #define _DEBUG_ 1


  50. #if _DEBUG_

  51. #define debug(format,msg...) printk(format,##msg)

  52. #else

  53. #define debug(format,msg...) (void *)(0)

  54. #endif



  55. //定义按键结构体

  56. typedef struct {
  57. //cdev 结构体
  58. struct cdev cdev;
  59. //缓冲区存放键值
  60. u8 buf_var[SZ_BUF];
  61. //当前指针,可以用来表征可用的键值的数目
  62. //初始化为 VAR_INIT_TAIL
  63. u8 tail;
  64. //按键状态,SZ_BUF表示满,满了的操作也不同
  65. u8 buf_status;
  66. } dev_key_t;
  67.  dev_key_t dev_key;


  68. // 定义设备的相关信息

  69. #define KEY_DEV_MAJOR 253

  70. #define DEV_NAME "usr_dev_key"

  71. dev_t dev_id;

  72. struct class *usr_dev_class;

  73. struct key_info

  74. {

  75. uint irq_no;//中断号

  76. uint pin;//管脚值

  77. uint pin_set;//管脚设置
  78. u8 value;    //值

  79. }key_info_tab[]=

  80. {

  81. [0]={

  82.     .irq_no=IRQ_EINT1,

  83.     .pin=S3C2410_GPF(1),
  84.         
  85.     .pin_set = S3C2410_GPF1_EINT1,

  86.     .value=0xff

  87.     },

  88. [1]={

  89.     .irq_no=IRQ_EINT4,

  90.     .pin=S3C2410_GPF(4),
  91.     .pin_set = S3C2410_GPF4_EINT4,

  92.     .value=0xff

  93.     },

  94. [2]={

  95.     .irq_no=IRQ_EINT2,

  96.     .pin=S3C2410_GPF(2),
  97.     .pin_set = S3C2410_GPF2_EINT2,

  98.     .value=0xff

  99.     },

  100. [3]={

  101.     .irq_no=IRQ_EINT0,
  102.     .pin_set = S3C2410_GPF0_EINT0,

  103.     .pin=S3C2410_GPF(0),

  104.     .value=0

  105.     }

  106. };

  107. //设备操作结构体
  108. extern struct file_operations file_ops;

  109. //这个宏直接获得 表x中的irq值
  110. #define irq(x) (key_info_tab[x].irq_no)
  111. //这个宏直接获得 表x中的pin值
  112. #define pin(x) (key_info_tab[x].pin)
  113. //这个宏直接获得 表x中的pin_set值
  114. #define set(x) (key_info_tab[x].pin_set)
  115. //这个宏直接获得 表x中的value值
  116. #define var(x) (key_info_tab[x].value)
  117. //这个宏直接获得dev_key.buf_var[x]的值
  118. #define buf(x) (dev_key.buf_var[x])
  119. //获得tail值得
  120. #define get_tail()     (dev_key.tail)
  121. //设置dev_key.tail=x
  122. #define set_tail_as(x)     dev_key.tail=x
  123. //设置dev_key.buf_var[index]=x
  124. #define set_buf_as(index,x) dev_key.buf_var[index]=x




  125. //dev_key.tail)%(SZ_BUF),保证dev_key.tail处于0到15之间
  126. #define inc_tail() dev_key.tail=((dev_key.tail+1)%(SZ_BUF))
  127. //inc_buf_num确保dev_key.buf_status值在0-SZ_BUF之间
  128.  #define inc_buf_num() dev_key.buf_status=(dev_key.buf_status==SZ_BUF?SZ_BUF:(dev_key.buf_status+1))
  129. //注意上面的inc_tail()这个宏,是实现环形状数据结构的关键


  130. //定义按键事件
  131. //
  132. #define EV_KEY_MASK (0x0f)
  133. //00000001稳定的释放状态
  134. #define EV_KEY_UP (0x01<<0)
  135. //00000010,稳定的按下状态
  136. #define EV_KEY_DN (0x02<<0)
  137. //0000,0011,按下,产生下降沿的那个状态
  138. #define EV_KEY_XIN (0x03<<0)
  139. //0000,0100,释放,产生上升沿的那个状态
  140. #define EV_KEY_XOUT (0x04<<0)
  141. //释放和按下,对应的GPIO的值
  142. #define VAR_KEY_DN 0x00
  143. #define VAR_KEY_UP 0x01

  144. //定义按键id,ID_KEY_MASK是为了获得键值
  145. #define ID_KEY_MASK (0xf0)
  146. //0001,0000
  147. #define ID_KEY_1 (0x01<<4)
  148. //0010,0000
  149. #define ID_KEY_2 (0x02<<4)
  150. //0011,0000
  151. #define ID_KEY_3 (0x03<<4)
  152. //0100,0000
  153. #define ID_KEY_4 (0x04<<4)


  154. //这是一个宏,通过键的值value获得EV
  155. #define get_key_ev(value) (value & EV_KEY_MASK)
  156. //这是一个宏,通过键的值value获得按键id
  157. #define get_key_id(value) (value & ID_KEY_MASK)>>4
  158. //下面这俩个宏,通过id,ev设置key_info_tab
  159. #define set_key_value(id,ev) (key_info_tab[id].value=((id+1) <<4|ev)&0xff)
  160. #define set_key_var(id,ev) (key_info_tab[id].value=(((id+1) <<4)|ev)&0xff)
  161.     
  162.     
  163.     
  164.     
  165. ////////////////////////////////////////////////////////////////////
  166. //@如下是实现中断下半部的机制,包含timer,workqueue,tasklet几种类型

  167. //定时器处理函数
  168. void my_timer_proc(long);
  169. //定义定时器
  170. DEFINE_TIMER(key_timer,my_timer_proc,0,0);

  171. //tasklet处理函数
  172. void my_task_proc(int);
  173. //定义tasklet
  174. DECLARE_TASKLET(my_task_let,my_task_proc,0);

  175. //工作队列结构体
  176. struct work_struct work_wq;
  177. //延时工作队列结构体,在linux/workqueue.h里面定义的
  178. struct delayed_work delayed_work_wq;
  179. //工作队列的处理函数
  180. void my_work_proc(struct work_struct * my_work);

  181. //按键等待队列头,这个是为了实现和等待队列相关的操作的
  182. static DECLARE_WAIT_QUEUE_HEAD(key_waitq);


  183. ///如下表示采用何种方法实现中断下半部
  184. //!=0表示采用此方法,注意,一次只能有一个为1,否则会重复保存数据
  185. //
  186. //采用工作队列的方法
  187. #define METHOD_WORK 0
  188. //采用延时工作队列的方法
  189. #define METHOD_DELAYED_WORK 1
  190. //采用定时器的方法,mod_timer()
  191. #define METHOD_TIMER 0
  192. //采用新旧jiffies的方法,个人原创
  193. #define METHOD_JIFFIES 0
  194. //采用tasklet
  195. #define METHOD_TASKLET 0
  196. //采用msleep()方法,尚未调试成功
  197. #define METHOD_MSLEEP 0

  198. //jiffies2保存最旧的jiffies
  199. //jiffies1保存上一次的jiffies
  200. //jiffies0保存此次中断的jiffies
  201. unsigned long volatile jiffies2=0;
  202. unsigned long volatile jiffies1=0;
  203. unsigned long volatile jiffies0=0;

  204. //注意down1,和down0,这个是为了实现jiffies方法而实现的
  205. int volatile down1 =0;
  206. int volatile down0 =0;

  207. //信号量,实现互斥,潮流,必须的
  208. struct semaphore sem;

  209. //注意这个,这是一个public glob变量,注意不用优化的volatile修饰符
  210. unsigned int volatile active_key;


  211. //采用工作队列的方法
  212. //#define METHOD_WORK 1
  213. void my_work_proc(struct work_struct * my_work)
  214. {
  215.     
  216.      debug("in %s ,do somthing------------[ok]\n",__func__);
  217.       return;
  218.     
  219. }

  220. ////这是一个函数,这个函数实现保存
  221. //按键id到当前可用buf的功能
  222. //当前可用buf是指buf[tail]
  223. void save_to_buf(int id)
  224. {
  225.     if(dev_key.buf_status==VAR_INIT)
  226.         {
  227.             set_tail_as(0);
  228.             dev_key.buf_status=0;
  229.         }
  230.     //set_buf_as(index,x);
  231.     set_buf_as(get_tail(),var(id));
  232.     
  233.     debug("in %s,buf[%d]=%x\n",__func__,get_tail(),buf(get_tail()));
  234.     
  235. //每保存一次都需要自增tail,buf数据量增加一次
  236.     inc_tail();
  237.     inc_buf_num();
  238.     return ;
  239. };
  240. /*
  241. //jiffies方法,个人原创的,
  242. //这个方法的主要特点是对应的
  243. // down0保存最新中断时的按键值
  244. //jiffies0如是
  245. //down1保存上一次中断的按键值,即上一个down0值
  246. //jiffies1如是
  247. //实现该功能的关键在于设置了如下的条件判断 if(jiffies1-jiffies0>TM1000MS/2)
  248. //这个判断是基于一个事实的:IRQ_TYPE_EDGE_BOTH类型的按键中断必然会产生>俩个的按键中断
  249. //一个是下降沿一个是上升边沿
  250. //如果俩个沿的时间差>500毫妙(因为我们认为每次按键的时间一般会大于500ms,低于500ms的我们认为是抖动)
  251. //并且俩个沿之间的电平为VAR_KEY_DN
  252. //我们则认为这是一个有效的按键具体可以
  253. // if(jiffies1-jiffies0>TM1000MS/2)
  254. */
  255. //#define METHOD_JIFFIES 1
  256. void my_jiffies_proc(int id)
  257. {

  258.     jiffies1=jiffies0;
  259.      jiffies0=jiffies;
  260.      down1=down0;
  261.      down0=s3c2410_gpio_getpin(pin(id));
  262.     if(jiffies1-jiffies0>TM1000MS/2){
  263.                  if(down0==VAR_KEY_DN)
  264.                      {
  265.                      set_key_var(id,EV_KEY_DN);
  266.                      save_to_buf(id);
  267.                      
  268.                      debug("in %s save a value [%x]to buf[%d]",__func__,buf(get_tail()-1),get_tail()-1);
  269.                     
  270.                      }
  271.          }

  272.     }
  273. /*
  274. #define METHOD_DELAY_WORK 1
  275. 这实际上是一个定时器,但比定时器有内涵多了
  276. 他的名字叫做延时工作队列方法
  277. 对应调用方式schedule_delayed_work();
  278. */
  279. void my_delay_work_proc(struct work_struct * my_work)
  280. {
  281.      int id= active_key;
  282.         u8 key_real_var=s3c2410_gpio_getpin(pin(id));
  283.          if(key_real_var==VAR_KEY_DN)
  284.          {
  285.     
  286.          set_key_var(id,EV_KEY_DN);
  287.          save_to_buf(id);
  288.  debug("in %s save a value [%x]to buf[%d]",__func__,buf(get_tail()-1),get_tail()-1);
  289.          wake_up_interruptible(&key_waitq);
  290.         
  291.                      
  292.          }
  293.      return;
  294. }

  295. /*
  296. 定时器处理线程,实现延时去抖动
  297. 这个是最经典的方法
  298. */
  299. void my_timer_proc(long key )
  300.     {
  301.      debug("in %s ,do somthing------------[ok]\n",__func__);
  302.       return;
  303.     }
  304.     
  305. /*
  306. tasklet方式,在这里说明一下,
  307. 这种方式虽然好,但是和    work_struct一样不能处理抖动问题
  308. */
  309. void my_task_proc(int key)
  310. {
  311.      debug("in %s ,do somthing------------[ok]\n",__func__);
  312.       return;

  313. }
  314. /*
  315. 利用msleep函数实现延时,调试没有通过
  316. */
  317. void my_msleep_proc(int key,int ms)
  318.     {
  319.         debug("in %s ,do somthing------------[ok]\n",__func__);
  320.         msleep_interruptible(ms);
  321.         return;
  322.     }
  323. /*

  324. @key_open,打开函数

  325. @

  326. */

  327. static int key_open(struct inode * pnode,struct file * pfile)
  328. {

  329.     int ret=0;
  330.     pfile->private_data=(dev_key_t *)&dev_key;
  331.     down(&sem);
  332.     return ret;

  333. };



  334. /*
  335. @key_release,关闭函数
  336. @关闭后
  337. */

  338. static int key_release(struct inode * pnode,struct file * pfile){

  339.     int ret=0;


  340.     //buf(0)=VAR_INIT;
  341.     var(0)=VAR_INIT;
  342.     var(1)=VAR_INIT;
  343.     var(2)=VAR_INIT;
  344.     var(3)=VAR_INIT;
  345.      // set_tail_as(0);
  346.      up(&sem);
  347.     return ret;

  348. };


  349. /*
  350. 这个函数用来干什么的呢?
  351. 这是为了实现一个理念:
  352. 排在列表头的或者列表尾的
  353. 一定要是最新的或者最旧的按键值
  354. 这个适合copy_to_user函数一起使用的
  355. */
  356. static int buf_sort(char a[])
  357. {
  358.     int i;
  359.     int j;
  360.     if(dev_key.buf_status==SZ_BUF)
  361.         {
  362.             for(j=0,i=SZ_BUF+get_tail()-1;j<SZ_BUF;i--,j++)
  363.             {
  364.                 a[j]=buf(i%SZ_BUF);
  365.             }    
  366.         }
  367.     else if(dev_key.buf_status<SZ_BUF)
  368.         {
  369.             for(i=0,j=dev_key.buf_status;j>=0;i++,j--)
  370.             {
  371.                 a[i]=buf(j);
  372.             }    
  373.         }
  374.         return;
  375. }

  376. /*
  377. 这个函数实现阻塞读
  378. */

  379. static int key_read(struct file * pfile,char * buf,size_t count, loff_t *offp)
  380. {

  381.     int ret=0;

  382.     int i=0;
  383.    int my_count=0;
  384.     char *msg;
  385.     u8 my_buf[SZ_BUF];

  386.     dev_key_t *dev=pfile->private_data;


  387.     DECLARE_WAITQUEUE(wq,current);
  388.     if(dev_key.buf_status==VAR_INIT)

  389.         {

  390.     if(pfile->f_flags & O_NONBLOCK)

  391.                 {

  392.             return -EAGAIN;    

  393.                 }

  394.             else

  395.                 {
  396.                 add_wait_queue(&key_waitq,&wq);
  397.                 wait_event_interruptible(key_waitq,buf(0)!=VAR_INIT);
  398.                 }    

  399.         }
  400.         
  401.         buf_sort(my_buf);
  402.         if(dev_key.buf_status==SZ_BUF)
  403.             {
  404.             count    = (count >SZ_BUF ? count :SZ_BUF);    
  405.             }
  406.         else if(dev_key.buf_status <SZ_BUF)
  407.             {
  408.             count = count >dev_key.buf_status ? dev_key.buf_status : count;
  409.             }    
  410.     ret=copy_to_user(buf,my_buf,count);    

  411.     msg=(ret==0?"OK":"NO");

  412.     debug("key_read:-------------------[%s],using count=%d,offp=%d\n",msg,i,(int)*offp);
  413.     remove_wait_queue(&key_waitq,&wq);
  414. return ret;

  415.     

  416.     

  417. };



  418. /*

  419. 中断处理函数,是枢纽

  420. */

  421. static irqreturn_t irq_proc(unsigned int irq,int _id)
  422. {    

  423.         active_key =_id;
  424.         #if METHOD_WORK
  425.         schedule_work(&work_wq);
  426.         #endif

  427.         #if METHOD_DELAYED_WORK
  428.         schedule_delayed_work(&delayed_work_wq,TM1000MS/10);
  429.         #endif


  430.         #if METHOD_TIMER
  431.         /*1HZ=1s=100个jiffies单位*/
  432.          key_timer.data=_id;
  433.         mod_timer(&key_timer,jiffies+TM1000MS/10);
  434.         #endif

  435.         #if METHOD_TASKLET
  436.         my_task_let.data=_id;
  437.         tasklet_schedule(&my_task_let);
  438.         #endif

  439.         #if METHOD_MSLEEP
  440.             my_msleep_proc(_id, TM1000MS/50);
  441.             
  442.         #endif
  443.         
  444.         
  445.     
  446.     
  447.     #if METHOD_JIFFIES
  448.         my_jiffies_proc(_id);
  449.     #endif
  450.      wake_up_interruptible(&key_waitq);
  451.     debug("extern from %s,active_key=%d\n",__func__,active_key);

  452.      return IRQ_RETVAL(IRQ_HANDLED);

  453.     };





  454. /*

  455. @dev_init,设备初始化函数,程式化的东西

  456. @

  457. */
  458. static int dev_init(void)
  459. {

  460.     int ret=0;

  461.     int i;

  462.     char * msg;

  463.     
  464.     
  465.     
  466.     ///cdev

  467.     cdev_init(&(dev_key.cdev),&file_ops);

  468.     dev_key.cdev.owner=THIS_MODULE;

  469.     dev_key.cdev.ops=&file_ops;

  470.     cdev_add(&(dev_key.cdev),dev_id,1);

  471.     

  472.     //register chr dev

  473.     #ifdef DEV_KEY_MAJOR

  474.         dev_id=MKDEV(DEV_KEY_MAJOR,0);

  475.         ret=register_chrdev_region(dev_id,1,DEV_NAME);

  476.         if(ret!=0)//!=0意味着注册失败
  477.     
  478.             goto alloc_method;

  479.         else

  480.             goto print_irq_msg;

  481.     #endif

  482.      alloc_method:

  483.          ret =alloc_chrdev_region(&dev_id,0,1,DEV_NAME);
  484.      
  485.      print_irq_msg:    

  486.          msg=(ret==0?"OK":"ERR");

  487.          debug("register devivice: %s -------[%s],using major=%d\n",DEV_NAME,msg,MAJOR(dev_id));

  488.      if(ret)
  489.           goto unregister_irq_handle;

  490.     

  491. //初始化中断

  492. for(i=0;i<get_elem_num(key_info_tab);i++)

  493.         {
  494.     ret=request_irq(key_info_tab[i].irq_no,irq_proc,IRQ_TYPE_EDGE_BOTH,DEV_NAME,i);

  495.         
  496.         
  497.         msg=(ret==0?"OK":"ERR");

  498.          debug("register %s.ext_irq[%d] -------[%s],using irq_no=%d\n",DEV_NAME,i,msg,key_info_tab[i].irq_no);
  499.         
  500.         if(ret!=0)
  501.             goto unregister_irq_handle;

  502.     
  503.         }
  504.       
  505.      init_MUTEX(&sem);
  506.         INIT_WORK(&work_wq,my_work_proc);
  507.         INIT_DELAYED_WORK(&delayed_work_wq,my_delay_work_proc);
  508.         
  509.         init_waitqueue_head(&key_waitq);
  510.        //setup_timer(&key_timer,&my_timer_proc,0);
  511.        init_timer(&key_timer);
  512.        key_timer.function=&my_timer_proc;
  513.        add_timer(&key_timer);
  514.        jiffies2=VAR_INIT;
  515.         jiffies1=VAR_INIT;
  516.          jiffies0=VAR_INIT;
  517.          
  518.          dev_key.buf_status=0;
  519.          set_tail_as(0);
  520.          usr_dev_class=class_create(THIS_MODULE,"usr_dev");
  521.          device_create(usr_dev_class,NULL,dev_id,NULL,"usr_dev_key");
  522.          
  523.      out:
  524.         
  525.          return ret;

  526. unregister_irq_handle:
  527.         for(i=i-1;i>=0;i--)
  528.         {
  529.             free_irq(key_info_tab[i].irq_no,dev_id);
  530.         }

  531.      return ret;
  532.     

  533. };



  534. /*

  535. @key_poll

  536. */

  537. static int key_poll(struct file *filep,poll_table *wt_poll)
  538. {

  539.     int mask=0;

  540.     dev_key_t *dev = filep->private_data;

  541.     poll_wait(filep,&key_waitq,wt_poll);

  542.     if(buf(0)!=VAR_INIT)
  543.         {
  544.             
  545.         mask=POLLIN    |    POLLRDNORM;
  546.         
  547.         }
  548.     return mask;

  549. };




  550. /*

  551. @dev_exit,exit dev

  552. @

  553. */

  554. static int dev_exit(void)
  555. {

  556.     int i;

  557.         unregister_chrdev(MAJOR(dev_id),DEV_NAME);
  558.         for(i=0;i<get_elem_num(key_info_tab);i++)

  559.         {

  560.         free_irq(irq(i),dev_id);
  561.        
  562.         
  563.         }
  564.         del_timer(&key_timer);
  565.         device_destroy(usr_dev_class,dev_id);
  566.         class_destroy(usr_dev_class);

  567.          return 0;

  568.     

  569.     

  570. };

  571. /*

  572. @key_ioctl

  573. */

  574. static int key_ioctl(struct inode *inodep,struct file *filep,unsigned int cmd, unsigned long arg )
  575. {

  576.     int ret;    
  577.     switch(cmd)
  578.         {

  579.             case CMD_RESET:

  580.                 dev_key.buf_status=0;
  581.                 set_tail_as(0);

  582.                 ret=1;    

  583.                 break;

  584.             default:

  585.                 ret =-EINVAL;

  586.                 break;

  587.                 

  588.         };

  589.     return ret;

  590. };





  591. /*

  592. file_operations结构体

  593. */


  594. struct file_operations file_ops={
  595. .owner     =    THIS_MODULE,

  596. .read        =     key_read,

  597. .ioctl        =    key_ioctl,

  598. .poll            =    key_poll,

  599. .open        =    key_open,

  600. .release    =    key_release

  601. };

  602. MODULE_AUTHOR("keytounix");

  603. MODULE_LICENSE("Dual BSD/GPL");

  604. MODULE_DESCRIPTION("driver for TQ2440 key");

  605. module_init(dev_init);
  606. module_exit(dev_exit);

阅读(2132) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~