Gpio-keys是基于input架构实现的一个通用GPIO按键驱动。该驱动基于platform_driver架构,实现了驱动和设备分离,符合Linux设备驱动模型的思想。工程中的按键驱动我们一般都会基于gpio-keys来写,所以我们有必要对gpio_keys进行分析。
[gpio-keys驱动分析]
Gpio-keys的代码在drivers/input/keyboard/gpio_keys.c中,具体分析一下probe函数,probe函数会在platformdriver和platform device匹配上后被调用。
1.static int __devinit gpio_keys_probe(structplatform_device *pdev)
2.{
3. structgpio_keys_platform_data *pdata = pdev->dev.platform_data;
4. structgpio_keys_drvdata *ddata;
5. structinput_dev *input;
6. inti, error;
7. intwakeup = 0;
8.
9. /*kzalloc 对kmalloc的封装,会清0分配的空间*/
10. ddata= kzalloc(sizeof(struct gpio_keys_drvdata) +
11. pdata->nbuttons* sizeof(struct gpio_button_data),
12. GFP_KERNEL);
13.
14. /*分配一个input设备*/
15. input= input_allocate_device();
16. if(!ddata || !input) {
17. error= -ENOMEM;
18. gotofail1;
19. }
20.
21. platform_set_drvdata(pdev,ddata);
22.
23./* 设置input设备属性 */
24. input->name= pdev->name;
25. input->phys= "gpio-keys/input0";
26. input->dev.parent= &pdev->dev;
27.
28. input->id.bustype= BUS_HOST;
29. input->id.vendor= 0x0001;
30. input->id.product= 0x0001;
31. input->id.version= 0x0100;
32.
33. /*Enable auto repeat feature of Linux input subsystem */
34. if(pdata->rep)
35. __set_bit(EV_REP,input->evbit);
36.
37. ddata->input= input;
38.
39. for(i = 0; i < pdata->nbuttons; i++) {
40. structgpio_keys_button *button = &pdata->buttons[i];
41. structgpio_button_data *bdata = &ddata->data[i];
42. intirq;
43. unsignedint type = button->type ?: EV_KEY;
44.
45. bdata->input= input;
46. bdata->button= button;
47. setup_timer(&bdata->timer,
48. gpio_keys_timer, (unsigned long)bdata);
49. /*初始化工作队列 */
50. INIT_WORK(&bdata->work,gpio_keys_report_event);
51.
52. /*申请GPIO口*/
53.error = gpio_request(button->gpio, button->desc ?:"gpio_keys");
54. if(error < 0) {
55. pr_err("gpio-keys:failed to request GPIO %d,"
56. "error %d\n", button->gpio, error);
57. gotofail2;
58. }
59. /* 把GPIO设为输入 */
60. error= gpio_direction_input(button->gpio);
61. if(error < 0) {
62. pr_err("gpio-keys:failed to configure input"
63. "direction for GPIO %d, error %d\n",
64. button->gpio,error);
65. gpio_free(button->gpio);
66. gotofail2;
67. }
68. /*获取GPIO对应的中断*/
69. irq= gpio_to_irq(button->gpio);
70. if(irq < 0) {
71. error= irq;
72. pr_err("gpio-keys:Unable to get irq number"
73. "for GPIO %d, error %d\n",
74. button->gpio,error);
75. gpio_free(button->gpio);
76. gotofail2;
77. }
78. /*注册中断 */
79. error= request_irq(irq, gpio_keys_isr,
80. IRQF_SHARED |
81. IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
82. button->desc ? button->desc :"gpio_keys",
83. bdata);
84. if(error) {
85. pr_err("gpio-keys:Unable to claim irq %d; error %d\n",
86. irq,error);
87. gpio_free(button->gpio);
88. gotofail2;
89. }
90.
91. if(button->wakeup)
92. wakeup= 1;
93.
94. /*设置设备对事件的支持,比如设置对键1和键2的支持*/
95. input_set_capability(input,type, button->code);
96. }
97.
98. /*注册input设备*/
99. error= input_register_device(input);
100. if(error) {
101. pr_err("gpio-keys:Unable to register input device, "
102. "error:%d\n", error);
103. gotofail2;
104. }
105.
106. device_init_wakeup(&pdev->dev,wakeup);
107.
108. return0;
109. ……
110. returnerror;
111.}
[中断服务例程]
1.static irqreturn_t gpio_keys_isr(int irq,void *dev_id)
2.{
3. structgpio_button_data *bdata = dev_id;
4. structgpio_keys_button *button = bdata->button;
5.
6. BUG_ON(irq!= gpio_to_irq(button->gpio));
7.
8. /*检测是否在platform device设置了去抖动www.linuxidc.com*/
9.if(button->debounce_interval)
10. mod_timer(&bdata->timer, /* 延迟msecs_to_jiffies(button->debounce_interval)个jiffies后执行schedule_work()*/
11. jiffies+ msecs_to_jiffies(button->debounce_interval));
12. else
13. schedule_work(&bdata->work);
14.
15. returnIRQ_HANDLED;
16.}
这里的中断被分成了上半部和下半部,上半部(中断处理例程)对中断进行了快速的相应,马上调度work上报事件信息:
1.static void gpio_keys_report_event(structwork_struct *work)
2.{
3. structgpio_button_data *bdata =
4. container_of(work,struct gpio_button_data, work);
5. structgpio_keys_button *button = bdata->button;
6. structinput_dev *input = bdata->input;
7. unsignedint type = button->type ?: EV_KEY;
8. intstate = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
9.
10. input_event(input,type, button->code, !!state);
11. input_sync(input);
12.}
[两个结构体]
1.struct gpio_keys_button {
2. /*Configuration parameters */
3. intcode; /* 输入事件代码(KEY_*, SW_*) */
4. intgpio; /* gpio口 */
5. intactive_low; /* 低电平有效*/
6. char*desc; /* 功能描述 */
7. inttype; /* 输入事件类型(EV_KEY, EV_SW) */
8. intwakeup; /* configure thebutton as a wake-up source */
9. intdebounce_interval; /* 去抖动间隔,单位微秒*/
10.};
11.
12.struct gpio_keys_platform_data {
13. structgpio_keys_button *buttons;
14. intnbuttons;
15. unsignedint rep:1; /* enable inputsubsystem auto repeat */
16.};
到此基本上分析完了
[工程中的实现]
上面只是分析了gpio-keys的作用,但是我们要如何使用呢?下面是具体的platform device的实现:
1.static struct gpio_keys_button sc_buttons[]= {
2. {
3. .gpio = S3C2410_GPG(0), /* K1 */
4. .code = KEY_1,
5. .desc = "KEY1",
6. .active_low = 1,
7. },
8. {
9. .gpio = S3C2410_GPG(3), /* K2 */
10. .code = KEY_2,
11. .desc = "KEY2",
12. .active_low = 1,
13. },
14. {
15. .gpio = S3C2410_GPG(5), /* K3 */
16. .code = KEY_3,
17. .desc = "KEY3",
18. .active_low = 1,
19. },
20. {
21. .gpio = S3C2410_GPG(6), /* K4 */
22. .code = KEY_4,
23. .desc = "KEY4",
24. .active_low = 1,
25. },
26. {
27. .gpio = S3C2410_GPG(7), /* K5 */
28. .code = KEY_5,
29. .desc = "KEY5",
30. .active_low = 1,
31. },
32.};
33.
34.static struct gpio_keys_platform_datasc_button_data = {
35. .buttons = sc_buttons,
36. .nbuttons =ARRAY_SIZE(sc_buttons),
37.};
38.
39.static struct platform_device sc_button_device= {
40. .name = "gpio-keys", /* 与platform driver的名字要相同 */
41. .id = -1,
42. .dev = {
43. .platform_data =&sc_button_data,
44. }
45.};
46.
47.static struct platform_device*mini2440_devices[] __initdata = {
48. ……
49. &sc_button_device,
50.};
最后记得把内核中对应的配置选项选上就OK。
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-11/47650p3.htm