【嵌入式Linux驱动开发】四、输入设备驱动

来源:互联网 发布:自制二维码软件 编辑:程序博客网 时间:2024/06/05 07:19
输入设备驱动

1. 输入设备概述

输入设备是向计算机输入数据和信息的设备。像键盘、鼠标、触摸屏等设备,都属于输入设备。

2. 输入设备驱动

在内核***/include/linux/input.h中有个重要的结构体:struct  input_dev 。每一个input_dev对象就是一个输入设备。驱动程序就是完成input_dev对象的成员填充和input_dev对象的注册。

重要成员:

 unsigned long evbit[BITS_TO_LONGS(EV_CNT)];  //事件
 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];  //按键映射
 unsigned long relbit[BITS_TO_LONGS(REL_CNT)];  //相对坐标
 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];  //绝对坐标
 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];  //led
 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];  //beep
 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
 unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

不同的输入设备要使用上述对应的成员,比如按键输入就要操作 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; 

3. 重要函数

  • struct input_dev __must_check *input_allocate_device(void);    //创建输入设备对象
  • void input_free_device(struct input_dev *dev);    //释放设备对象
  • int __must_check input_register_device(struct input_dev *);    //注册设备
  • void input_unregister_device(struct input_dev *);    //注销设备

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_KEY, code, !!value);
}    //发送input_event

static inline void input_sync(struct input_dev *dev)
{
    input_event(dev, EV_SYN, SYN_REPORT, 0);
}
    //发送哨兵报告,即全0的event

4. qt210按键驱动实例

此驱动程序用qt210开发板上的4个按键模拟键盘“L”、“S”、“ENTER”、“BACKSPACE”。驱动中定时器用于滤波,防止按键按下时多次触发中断。

[objc] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/init.h>  
  4.   
  5. #include <linux/input.h> //输入设备结构体(struct input_dev)  
  6.   
  7. #include <linux/timer.h> //定时器  
  8.   
  9. #include <linux/interrupt.h> //函数request_irq()  
  10.   
  11. // arch/arm/mach-s5pv210/include/mach/regs-gpio.h  
  12. #include <mach/regs-gpio.h> //GPIO寄存器虚拟地址的基地址(S5P_VA_GPIO)  
  13.   
  14. // arch/arm/mach-s5pv210/include/mach/irqs.h  
  15. // arch/arm/plat-samsung/include/plat/irqs.h  
  16. #include <mach/irqs.h> //中断号  
  17.   
  18. #define GPH2CON (*(volatile u32*)(S5P_VA_GPIO + 0x0c40))  
  19. #define GPH2DAT (*(volatile u32*)(S5P_VA_GPIO + 0x0c44))  
  20. #define GPH2PUD (*(volatile u32*)(S5P_VA_GPIO + 0x0c48))  
  21.   
  22. #define GPH3CON (*(volatile u32*)(S5P_VA_GPIO + 0x0c60))  
  23. #define GPH3DAT (*(volatile u8*)(S5P_VA_GPIO + 0x0c64))  
  24.   
  25. /*按键结构体*/  
  26. struct key_info {  
  27.     int irqno;  
  28.     const charchar *name;  
  29.     unsigned int code;  
  30. };  
  31.   
  32. /*qt210用到的按键的信息*/  
  33. static struct key_info key[4] = {  
  34.     [0] = {IRQ_EINT(20), "COL4", KEY_L},  
  35.     [1] = {IRQ_EINT(21), "COL5", KEY_S},  
  36.     [2] = {IRQ_EINT(22), "COL6", KEY_BACKSPACE},  
  37.     [3] = {IRQ_EINT(23), "COL7", KEY_ENTER},  
  38. };  
  39.   
  40. /*模块结构体*/  
  41. struct ldm_info {  
  42.     struct input_dev *pdev;  
  43.     struct timer_list timer;  
  44. };  
  45. static struct ldm_info ldm;  
  46.   
  47. /*定时器触发函数*/  
  48. static void timer_handler(unsigned long data)  
  49. {  
  50.     struct key_info *p = (struct key_info *)data;  
  51.   
  52.     //检查到底是哪个按键,根据不同的按键,发送相应的input_event报告  
  53.     input_report_key(ldm.pdev, p->code, !(GPH2DAT & (1 << (p-key+4)))); //按下时value为1,抬起时value为0  
  54.   
  55.     //发送哨兵报告  
  56.     input_sync(ldm.pdev);  
  57. }  
  58.   
  59. /*中断处理函数*/  
  60. static irqreturn_t key_isr(int irqno, voidvoid *arg)  
  61. {  
  62.     ldm.timer.data = (unsigned long)arg;  
  63.     mod_timer(&ldm.timer, jiffies + HZ / 10);  
  64.   
  65.     return IRQ_HANDLED;  
  66. }  
  67.   
  68. /*按键相关的硬件初始化和中断注册*/  
  69. static int key_init(void)  
  70. {  
  71.     int ret = 0;  
  72.   
  73.     //col4-7,GPH2_4-7设为输入,内部上拉  
  74.     GPH2CON &= ~(0xffff << 16);  
  75.     GPH2PUD = (GPH2PUD & ~(0xff << 8)) | 0b10101010 << 8;  
  76.   
  77.     //row1,GPH3_1设为输出  
  78.     GPH3CON = (GPH3CON & ~(0xf << 4)) | 1 << 4;  
  79.   
  80.     //中断注册,成功后可以通过cat /proc/interrupts来查看当前所有注册的中断信息  
  81.     ssize_t i = 0;  
  82.     for (i = 0; i < ARRAY_SIZE(key); ++i) {  
  83.         if ((ret = request_irq(key[i].irqno, key_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, key[i].name, (void*)(key + i))) < 0) {  
  84.             printk("request irq %s failed\n", key[i].name);  
  85.             goto err_request_irq;  
  86.         }  
  87.     }  
  88.   
  89.     return 0;  
  90.   
  91. err_request_irq:  
  92.     for (i = i - 1; i >= 0; --i) {  
  93.         free_irq(key[i].irqno, (void*)(key + i));  
  94.     }  
  95.     return ret;  
  96. }  
  97.   
  98. static int __init ldm_init(void)  
  99. {  
  100.     printk("%s\n", __FUNCTION__);  
  101.   
  102.     int ret = 0;  
  103.   
  104.     //1 创建输入设备对象  
  105.     ldm.pdev = input_allocate_device();  
  106.     if (!ldm.pdev) {  
  107.         printk("input_allocate_device failed\n");  
  108.         ret = -ENOMEM;  
  109.         goto err_input_allocate_device;  
  110.     }  
  111.   
  112.     //2 填充input_dev的成员  
  113.     //2.1 注册输入设备类型,支持按键类型的输入,支持连发  
  114.     ldm.pdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);  
  115.   
  116.     //2.2 注册键盘映射的键位  
  117.     ldm.pdev->keybit[BIT_WORD(KEY_BACKSPACE)] = BIT_MASK(KEY_BACKSPACE);  
  118.     ldm.pdev->keybit[BIT_WORD(KEY_ENTER)] |= BIT_MASK(KEY_ENTER);  
  119.     ldm.pdev->keybit[BIT_WORD(KEY_L)] |= BIT_MASK(KEY_L);  
  120.     ldm.pdev->keybit[BIT_WORD(KEY_S)] |= BIT_MASK(KEY_S);  
  121.   
  122.     //3 硬件初始化  
  123.     ret = key_init();  
  124.     if (ret < 0) {  
  125.         printk("key_init failed\n");  
  126.         goto err_key_init;  
  127.     }  
  128.     init_timer(&ldm.timer);  
  129.     ldm.timer.function = timer_handler;  
  130.   
  131.     //5 注册设备  
  132.     ret = input_register_device(ldm.pdev);  
  133.     if (ret < 0) {  
  134.         printk("input_register_device failed\n");  
  135.         goto err_input_register_device;  
  136.     }  
  137.   
  138.     return 0;  
  139.   
  140. err_input_register_device:  
  141. err_key_init:  
  142.     input_free_device(ldm.pdev);  
  143. err_input_allocate_device:  
  144.     return ret;  
  145. }  
  146.   
  147. static void __exit ldm_exit(void)  
  148. {  
  149.     printk("%s\n", __FUNCTION__);  
  150.   
  151.     input_unregister_device(ldm.pdev);  
  152.   
  153.     ssize_t i = 0;  
  154.     for (i = ARRAY_SIZE(key) - 1; i >= 0; --i) {  
  155.         free_irq(key[i].irqno, (void*)(key + i));  
  156.     }  
  157.   
  158.     input_free_device(ldm.pdev);  
  159. }  
  160.   
  161. module_init(ldm_init);  
  162. module_exit(ldm_exit);  
  163.   
  164. MODULE_LICENSE("GPL");  
  165.   
  166. //将该输入设备设置为当前默认的输入设备  
  167. //cat /proc/console //显示当前默认的终端设备  
  168. //cat /proc/bus/input/devices //查看当前注册的所有输入设备的信息  
  169. //exec 0</dev/ttyX //将目标终端设备重定向成默认终端设备  
0 0
原创粉丝点击