基于Tiny4412的Linux按键输入子系统驱动的实现(一)

﹏ヽ暗。殇╰゛Y 2022-06-17 05:11 1286阅读 0赞

本文主要包含的章节:
一、前期的准备工作
二、Linux输入子系统的简单介绍
三、基于输入子系统的按键驱动的实现

一、前期的准备工作
1、基本的开发环境

交叉开发环境 : Ubuntu12.04
Linux内核版本 : Linux-3.0.86
GUI系统 : Qtopia2.2.0
开发板 : 友善之臂的Tiny4412(Cortex-A9)
2、内核的配置
由于默认的内核在初始化时已经将按键对应的GPIO口添加到平台设备当中,每当内核启动的时候这几个按键都会自动去请求中断,导致中断被占用。所以在编写自己的按键输入子系统驱动之前,要注释掉这个平台设备。
这个平台设备对应的代码在内核中位置是:arch\arm\mach-exynos\mach-tiny4412.c,对应的行数为 :2968,代码如下所示:

  1. static struct platform_device tiny4412_input_device = {
  2. .name = GPIO_EVENT_DEV_NAME,
  3. .id = 0,
  4. .dev = {
  5. .platform_data = &tiny4412_input_data,
  6. },
  7. };
  8. 对这个文件进行修改,将 mach-tiny4412.c3315行注释掉即可。

二、Linux输入子系统的简单介绍
1、输入子系统

常见的输入设备有按键、键盘、鼠标、触摸屏等,它们的工作原理虽然都各不相同,但是工作机制却是相似的。一般来讲都是通过触发中断,然后让CPU来读取键值、坐标值等。显然,在这些工作中,只有中断、读键值、坐标值等是设备相关的,而输入事件的缓冲区管理以及字符设备驱动的VFS(Virtual File System)接口对输入设备是通用的。所以,内核把这些输入事件相似的地方给抽象了出来,设计出了输入子系统。
2、核心函数
核心函数在内核的位置:drivers\input\input.c 和 include\linux\input.h
2.1 分配、释放一个输入设备

  1. struct input_dev *input_allocate_device(void)
  2. void input_free_device(struct input_dev *dev)
  3. 2.2 注册、注销输入设备
  4. int input_register_device(struct input_dev *dev)
  5. void input_unregister_device(struct input_dev *dev)
  6. 2.3 报告输入事件的接口
  7. void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
  8. /* 报告键值 */
  9. static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
  10. /* 报告相对坐标 */
  11. static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
  12. /* 报告绝对坐标 */
  13. static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
  14. /* 报告同步事件 */
  15. static inline void input_sync(struct input_dev *dev)
  16. **3、输入子系统驱动的一般编写步骤**
  17. a、分配一个input\_dev结构体
  18. b、设置input\_dev结构体 支持哪类事件, 支持该类事件中的那些事件
  19. c、注册input\_dev结构体
  20. d、硬件相关的操作 中断申请,定时器的设置等

三、基于输入子系统的按键驱动的实现
3.1 Tiny4412开发板上的按键电路原理图

Center
通过对原理图进行分析,可以看出当按键松开的时候IO口对应的是高电平,当按键按下的时候对应的是低电平。按键对应的GPIO是 :
* XEINT26——GPX3_2

  1. \* XEINT27----GPX3\_3
  2. \* XEINT28----GPX3\_4
  3. \* XEINT29----GPX3\_5
  4. 如下图所示:

Center 1

  1. ** 3.2 按键输入子系统驱动的编写**
  2. 3.2.1 分配一个名为buttons\_devinput\_dev结构体
  3. buttons_dev = input_allocate_device();
  4. if(!buttons_dev)
  5. {
  6. printk("input_allocate_device error!\n");
  7. return -ENOMEM;
  8. }
  9. 3.2.2 设置buttons\_dev结构体
  10. /* 2.1、支持哪类事件 */
  11. set_bit(EV_KEY, buttons_dev->evbit);
  12. set_bit(EV_REP, buttons_dev->evbit);
  13. /* 2.2、支持该类事件中的那些事件 */
  14. for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
  15. {
  16. set_bit(buttons_desc[i].key_code, buttons_dev->keybit);
  17. }
  18. 3.2.3 注册这个结构体
  19. input_register_device(buttons_dev);
  20. 3.2.4 硬件相关的操作
  21. /* 4、硬件相关的操作
  22. * 为每个按键申请一个中断,共用中断处理函数
  23. * 按键触发方式为双边沿触发
  24. */
  25. for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
  26. {
  27. irq = gpio_to_irq(buttons_desc[i].gpio);
  28. request_irq(irq, yl_buttons_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, buttons_desc[i].name, (void*)&buttons_desc[i]);
  29. }
  30. 在硬件操作的核心是请求中断,中断的核心是 yl\_buttons\_irq 这个函数,这个函数的代码如下所示:
  31. /* 按键中断处理程序 */
  32. static irqreturn_t yl_buttons_irq(int irq, void *devid)
  33. {
  34. struct yl_buttons_desc *buttons_desc = (struct yl_buttons_desc *)devid;
  35. int pinval = gpio_get_value(buttons_desc->gpio);
  36. if(pinval == 1) /* 判断按键是按下还是松开 */
  37. {
  38. /* 松开 */
  39. input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 0);
  40. input_sync(buttons_dev);
  41. }
  42. else
  43. {
  44. /* 按下 */
  45. input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 1);
  46. input_sync(buttons_dev);
  47. }
  48. return IRQ_HANDLED;
  49. }
  50. 当发生按键中断时将触发按键中断程序,根据获得的对应按键的值来确定按键是按下还是松开,然后分别进行事件上报和事件同步。

附录:本节实现的按键输入子系统的完整代码如下所示。

  1. /* 包含的头文件 */
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <linux/fs.h>
  5. #include <linux/init.h>
  6. #include <linux/delay.h>
  7. #include <linux/poll.h>
  8. #include <linux/sched.h>
  9. #include <linux/irq.h>
  10. #include <asm/irq.h>
  11. #include <asm/io.h>
  12. #include <linux/interrupt.h>
  13. #include <asm/uaccess.h>
  14. #include <mach/hardware.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/cdev.h>
  17. #include <linux/miscdevice.h>
  18. #include <linux/gpio.h>
  19. #include <mach/map.h>
  20. #include <mach/gpio.h>
  21. #include <mach/regs-clock.h>
  22. #include <mach/regs-gpio.h>
  23. /* 输入子系统需要的头文件 */
  24. #include <linux/input.h>
  25. /** 按键映射:
  26. * XEINT26 ---- GPX3_2
  27. * XEINT27 ---- GPX3_3
  28. * XEINT28 ---- GPX3_4
  29. * XEINT29 ---- GPX3_5
  30. */
  31. /* 定义一个结构体用来对输入按键进行描述 */
  32. struct yl_buttons_desc{
  33. int gpio; // 表示对于的按键的引脚
  34. char *name; // 表示对应的按键请求中断时的中断名
  35. int key_code; // 表示按键在输入子系统中对应的键值
  36. };
  37. /* 定义一个描述按键的数组 */
  38. static struct yl_buttons_desc buttons_desc[] = {
  39. {EXYNOS4_GPX3(2), "yl_buttons_L", KEY_L},
  40. {EXYNOS4_GPX3(3), "yl_buttons_S", KEY_S},
  41. {EXYNOS4_GPX3(4), "yl_buttons_ENTER", KEY_ENTER},
  42. {EXYNOS4_GPX3(5), "yl_buttons_LEFTSHIFT", KEY_LEFTSHIFT},
  43. };
  44. /* 定义一个输入子系统的结构体指针变量 */
  45. static struct input_dev *buttons_dev;
  46. /* 按键中断处理程序 */
  47. static irqreturn_t yl_buttons_irq(int irq, void *devid)
  48. {
  49. struct yl_buttons_desc *buttons_desc = (struct yl_buttons_desc *)devid;
  50. int pinval = gpio_get_value(buttons_desc->gpio);
  51. if(pinval == 1) /* 判断按键是按下还是松开 */
  52. {
  53. /* 松开 */
  54. input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 0);
  55. input_sync(buttons_dev);
  56. }
  57. else
  58. {
  59. /* 按下 */
  60. input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 1);
  61. input_sync(buttons_dev);
  62. }
  63. return IRQ_HANDLED;
  64. }
  65. /* 入口函数 */
  66. static int __init yl_buttons_init(void)
  67. {
  68. int irq;
  69. int i;
  70. /* 1、分配一个input_dev结构体 */
  71. buttons_dev = input_allocate_device();
  72. if(!buttons_dev)
  73. {
  74. printk("input_allocate_device error!\n");
  75. return -ENOMEM;
  76. }
  77. /* 2、设置input_dev结构体 */
  78. /* 2.1、支持哪类事件 */
  79. set_bit(EV_KEY, buttons_dev->evbit);
  80. set_bit(EV_REP, buttons_dev->evbit);
  81. /* 2.2、支持该类事件中的那些事件 */
  82. for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
  83. {
  84. set_bit(buttons_desc[i].key_code, buttons_dev->keybit);
  85. }
  86. /* 3、注册input_dev结构体 */
  87. input_register_device(buttons_dev);
  88. /* 4、硬件相关的操作
  89. * 为每个按键申请一个中断,共用中断处理函数
  90. * 按键触发方式为双边沿触发
  91. */
  92. for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
  93. {
  94. irq = gpio_to_irq(buttons_desc[i].gpio);
  95. request_irq(irq, yl_buttons_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, buttons_desc[i].name, (void*)&buttons_desc[i]);
  96. }
  97. return 0;
  98. }
  99. /* 出口函数 */
  100. static void __exit yl_buttons_exit(void)
  101. {
  102. int irq;
  103. int i;
  104. /* 释放申请的按键中断 */
  105. for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
  106. {
  107. irq = gpio_to_irq(buttons_desc[i].gpio);
  108. free_irq(irq, (void*)&buttons_desc[i]);
  109. }
  110. input_unregister_device(buttons_dev);
  111. input_free_device(buttons_dev);
  112. }
  113. module_init(yl_buttons_init);
  114. module_exit(yl_buttons_exit);
  115. MODULE_LICENSE("GPL");

发表评论

表情:
评论列表 (有 0 条评论,1286人围观)

还没有评论,来说两句吧...

相关阅读

    相关 linux输入子系统编程,按键事件

    之前在编写字符设备驱动的时候,是从0开始编写的。然而在内核中有许多框架可以直接使用,不需要我们自己去从零编写,比如输入子系统。 输入子系统:当应用程序需要驱动输入设备,比如

    相关 Linux输入子系统

    输入子系统是对多种不同类别的输入设备(如键盘、鼠标、跟踪球、操作杆、触摸屏、加速计和手写板)进行统一处理(inputcore)的驱动程序。 输入子系统带来的好处: