Linux驱动子系统之输入子系统

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

你可能感兴趣的:(Linux驱动子系统之输入子系统)