2440 Linux按键驱动及测试程序

来源:互联网 发布:APP登录HTML源码 编辑:程序博客网 时间:2024/04/30 01:19

原文地址:http://www.cnblogs.com/nick123/archive/2010/03/26/1696966.html

 以下是驱动程序:

[cpp] view plaincopy
  1. <p>/*本驱动程序运行于TQ2440开发板,内核版本2.6.31.6*/</p>#include <linux/module.h>   
  2. #include <linux/kernel.h>   
  3. #include <linux/fs.h>   
  4. #include <linux/init.h>   
  5. #include <linux/delay.h>   
  6. #include <linux/poll.h>   
  7. #include <linux/irq.h>   
  8. #include <asm/irq.h>   
  9. #include <linux/io.h>  
  10. #include <linux/interrupt.h>   
  11. #include <asm/uaccess.h>   
  12. #include <mach/regs-clock.h>  
  13. #include <plat/regs-adc.h>   
  14. #include <mach/hardware.h>   
  15. #include <linux/platform_device.h>   
  16. #include <linux/cdev.h>   
  17. #include <linux/miscdevice.h>   
  18.   
  19. #define DEVICE_NAME        "tq2440-buttons"  
  20. #define DEV_COUNT          1  
  21. #define BUTTON_MAJOR    0  
  22. #define BUTTON_MINOR    0  
  23.   
  24. #define MIN(A,B)        (A)>(B)?(B):(A)  
  25.   
  26. /*等待队列: 
  27. *当没有按键被按下时,如果有进程调用tq2440_buttons_read函数 
  28. *它将休眠*/  
  29. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  
  30.   
  31. /*中断事件标志,中断服务程序将它置1,tq2440_buttons_read将它清0 */  
  32. static volatile int ev_press = 0;  
  33.   
  34. /* 按键被按下的次数(准确地说,是发生中断的次数) */  
  35. static volatile int  press_cnt[] = {0,0,0,0};  
  36.   
  37. static struct class *button_class;   
  38. struct button_irqs_desc      {  
  39. int irq;   //中断号  
  40. unsigned long flags; //中断标志,用来定义中断的触发方式   
  41. char *name;        //中断名称  
  42. };  
  43.   
  44. /*用来指定按键所用的外部中断引脚及中断触发方式,名字*/  
  45. static struct button_irqs_desc button_irqs[] = {  
  46. {IRQ_EINT0,IRQ_TYPE_EDGE_FALLING,"KEY1"},  //K1  
  47. {IRQ_EINT1,IRQ_TYPE_EDGE_FALLING,"KEY2"},      //K2  
  48. {IRQ_EINT2,IRQ_TYPE_EDGE_FALLING,"KEY3"},  //K3  
  49. {IRQ_EINT4,IRQ_TYPE_EDGE_FALLING,"KEY4"},  //K4  
  50. };  
  51.   
  52. dev_t dev_num;  
  53. static struct cdev * buttons_cdev_p;  //cdev结构体指针  
  54.   
  55. /*卸载驱动程序*/  
  56. static void __exit tq2440_buttons_exit(void)  
  57. {  
  58.     cdev_del(buttons_cdev_p);//注销cdev  
  59.     kfree(buttons_cdev_p);//释放设备结构体内存  
  60.     unregister_chrdev_region(dev_num,DEV_COUNT);//释放设备号  
  61. }  
  62.   
  63. static irqreturn_t buttons_interrupt(int irq,void *dev_id)  
  64. {  
  65.      volatile int *press_cnt = (volatile int *)dev_id;  
  66.   
  67.     *press_cnt = *press_cnt + 1;     /*按键计数器加1  */  
  68.     ev_press = 1;                    /*表示中断发生了*/  
  69.     wake_up_interruptible(&button_waitq);  /*唤醒休眠的进程*/  
  70.   
  71.     printk(" IRQ:%d\n",irq);  
  72.       
  73.     return IRQ_RETVAL(IRQ_HANDLED);  
  74. }  
  75.   
  76. /*应用程序执行open("/dev/buttons",...)系统调用时,tq2440_buttons_open函数将 
  77.  *被调用,它用来注册4个按键的中断处理程序*/  
  78. static int tq2440_buttons_open(struct inode *inode,struct file *file)  
  79. {  
  80.     int i;  
  81.     int err;  
  82.   
  83.     for(i =0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++)  {  
  84.         //注册中断处理函数  
  85.         err = request_irq(button_irqs[i].irq,buttons_interrupt,button_irqs[i].flags,  
  86.                         button_irqs[i].name,(void *)&press_cnt[i]);  
  87.   
  88.         if(err)      
  89.             break;  
  90.         }  
  91.       if(err) {  
  92.           //如果出错,释放已经注册的中断  
  93.           i--;  
  94.         for(;i>=0;i--)  
  95.             free_irq(button_irqs[i].irq,(void *)&press_cnt[i]);  
  96.         return -EBUSY;  
  97.           }  
  98.       return 0;  
  99. }  
  100.   
  101. /*应用程序对设备文件/dev/buttons执行close(...)时, 
  102.  *就会调用tq2440_buttons_close函数*/  
  103.  static int tq2440_buttons_close(struct inode *inode,struct file *file)  
  104. {  
  105.     int i;  
  106.   
  107.     for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++)  {  
  108.         //释放已经注册的中断  
  109.         free_irq(button_irqs[i].irq,(void *)&press_cnt[i]);  
  110.         }  
  111.     return 0;  
  112. }  
  113.   
  114. /*应用程序对设备文件/dev/buttons执行read(...)时, 
  115.  *就会调用tq2440_buttons_read函数 
  116.  */  
  117.  static int tq2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)  
  118. {  
  119.     unsigned long err;  
  120.     /*如果ev_press等于0,休眠*/  
  121.     wait_event_interruptible(button_waitq,ev_press);  
  122.   
  123.     /*执行到这里时ev_press肯定等于1,将它清0 */  
  124.     ev_press = 0;  
  125.   
  126.     /*将按键状态复制给用户,并请0  */  
  127.     err = copy_to_user(buff,(const void *)press_cnt,MIN(sizeof(press_cnt),count));  
  128.   
  129.     memset((void *)press_cnt,0,sizeof(press_cnt));  
  130.   
  131.     return err? -EFAULT:0;  
  132. }  
  133.   
  134. /*这个结构是字符驱动设备程序的核心 
  135.  *当应用程序操作设备文件时所调用的open,read,write等函数 
  136.  *最终会调用这个结构中的对应函数 
  137.  */  
  138.   
  139. static struct file_operations tq2440_buttons_fops = {  
  140.     .owner =    THIS_MODULE,/*这是一个宏,指向编译模块时自动创建的__this_module变量*/  
  141.     .open  = tq2440_buttons_open,  
  142.     .release = tq2440_buttons_close,  
  143.     .read   = tq2440_buttons_read,  
  144. };  
  145.   
  146. /*  初始化并注册cdev*/  
  147. static void buttons_cdev_setup(void)  
  148. {  
  149.     int err;  
  150.     cdev_init(buttons_cdev_p,&tq2440_buttons_fops);  
  151.     buttons_cdev_p->owner = THIS_MODULE;  
  152.     buttons_cdev_p->ops = &tq2440_buttons_fops;  
  153.     err = cdev_add(buttons_cdev_p,dev_num,1);  
  154.     if(IS_ERR(&err))  
  155.         printk(KERN_NOTICE "Error %d adding buttons",err);  
  156. }  
  157. static int __init tq2440_buttons_init(void)  
  158. {  
  159.     int ret;  
  160.   
  161.     /*注册字符设备驱动程序 
  162.         *参数为主设备号,设备名字,file_operations结构体 
  163.         *这样,主设备号就和具体的file_operations结构体联系起来了, 
  164.         *操作主设备号为BUTTON_MAJOR的设备文件时,就会调用 
  165.         *tq2440_buttons_fops中的相关成员函数,BUTTON_MAJOR可以设为0, 
  166.         *表示由内核自动分配主设备号 
  167.         */  
  168.    
  169.        if(BUTTON_MAJOR)   //手动分配设备号  
  170.            {    
  171.              dev_num=MKDEV(BUTTON_MAJOR, BUTTON_MINOR);  
  172.                ret=register_chrdev_region(dev_num,DEV_COUNT,DEVICE_NAME);  
  173.             }  
  174.     else  {          //动态分配设备号  
  175.          ret = alloc_chrdev_region(&dev_num,BUTTON_MINOR,DEV_COUNT,DEVICE_NAME);          
  176.     }  
  177.     if(ret<0)     {  
  178.                 printk(DEVICE_NAME " can't register major number \n");  
  179.                 return ret;  
  180.         }  
  181.     /*动态申请cdev结构体的内存*/  
  182.     buttons_cdev_p = kmalloc(sizeof(struct cdev),GFP_KERNEL);  
  183.     if(!buttons_cdev_p)   //申请失败  
  184.         {  
  185.             ret = -ENOMEM;  
  186.             goto fial_malloc;  
  187.         }  
  188.     memset(buttons_cdev_p,0,sizeof(struct cdev));  
  189.     buttons_cdev_setup();  
  190.     //注册一个类,使mdev可以在"/dev/"目录下面  
  191.     //建立设备节点  
  192.     button_class = class_create(THIS_MODULE,DEVICE_NAME);  
  193.     if(IS_ERR(button_class))  
  194.         {  
  195.         printk("Error:Failed to creat button_class \n");  
  196.         return -1;  
  197.         }  
  198.     //创建一个设备节点,节点名为DEVICE_NAME  
  199.       
  200.     device_create(button_class,NULL,dev_num,NULL,DEVICE_NAME);  
  201.     printk(DEVICE_NAME " initialized \n");  
  202.     return 0;  
  203.     fial_malloc:unregister_chrdev_region(dev_num, 1);  
  204. }      
  205.           
  206. /*这两行制定驱动程序的初始化函数和卸载函数*/  
  207. module_init(tq2440_buttons_init);  
  208. module_exit(tq2440_buttons_exit)  

下面是测试程序:

[html] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/ioctl.h>  
  5.   
  6. int main(int argc,char **argv)  
  7. {  
  8.     int i;  
  9.     int ret;  
  10.     int fd;  
  11.     int press_cnt[4];  
  12.   
  13.     fd=open("/dev/tq2440-buttons",0);  
  14.     if(fd<0)  {  
  15.         printf("Can't open /dev/tq2440-buttons \n");  
  16.         return -1;  
  17.         }  
  18.     //这是个无限循环,进程有可能在read函数中休眠,当有按键按下时,  
  19.     //它才返回  
  20.     while(1) {  
  21.         ret = read(fd,press_cnt,sizeof(press_cnt));  
  22.         if(ret<0)  {  
  23.             printf("read err !\n");  
  24.             continue;  
  25.             }  
  26.     //如果被按下的次数不为0,打印出来   
  27.     for(i=0;i<sizeof(press_cnt)/sizeof(press_cnt[0]);i++)    {  
  28.         if(press_cnt[i])  
  29.                 printf("Key%d has been pressed %d times \n",i+1,press_cnt[i]);  
  30.         }  
  31.           
  32.    }//while              
  33. }  

进入根文件系统dev目录,发现已经成功动分配设备,并自动创建文件节点

[nick@TQ2440 /dev]# ls -l tq*
crw-rw----    1 0        0        232,   0 Jan  1 00:00 tq2440-adc
crw-rw----    1 0        0        254,   0 Jan  1 00:00 tq2440-buttons
crw-rw----    1 0        0        231,   0 Jan  1 00:00 tq2440-leds
crw-rw----    1 0        0        204,  64 Jan  1 00:00 tq2440_serial0
crw-rw----    1 0        0        204,  65 Jan  1 00:00 tq2440_serial1
crw-rw----    1 0        0        204,  66 Jan  1 00:00 tq2440_serial2

下载内核后,运行测试程序

[nick@TQ2440 /app]# ./buttons_test &

查看中断已经注册成功

[nick@TQ2440 /proc]# cat interrupts
           CPU0
 16:          0    s3c-ext0  KEY1
 17:          0    s3c-ext0  KEY2
 18:          0    s3c-ext0  KEY3
 30:      27496         s3c  S3C2410 Timer Tick
 32:          0         s3c  s3c2410-lcd
 43:          0         s3c  s3c2440-i2c
 48:          0     s3c-ext  KEY4
 51:       2587     s3c-ext  eth0
 70:        156   s3c-uart0  s3c2440-uart
 71:       1839   s3c-uart0  s3c2440-uart
 83:          0           -  s3c2410-wdt
Err:          0

第一列表示中断号

第二列表示这个中断发生的次数

第三列表示这个中断的硬件访问结构“struct irq_chip *chip"的名字,它在初始化中断体系结构时指定

第四列表示中断的名称

这时按下按键,输出如下

 IRQ:48
Key4 has been pressed 1 times
 IRQ:16
Key1 has been pressed 1 times
 IRQ:18
Key3 has been pressed 1 times
 IRQ:48
Key4 has been pressed 1 times
 IRQ:16
Key1 has been pressed 1 times
 IRQ:17
Key2 has been pressed 1 times

更多0

0 0
原创粉丝点击