[概述]
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匹配上后被调用。
staticint__devinit gpio_keys_probe(structplatform_device *pdev)
{
structgpio_keys_platform_data *pdata = pdev->dev.platform_data;
structgpio_keys_drvdata *ddata;
structinput_dev *input;
inti, error;
intwakeup = 0;
/*kzalloc 对kmalloc的封装,会清0分配的空间*/
ddata= kzalloc(sizeof(structgpio_keys_drvdata) +
pdata->nbuttons*sizeof(structgpio_button_data),
GFP_KERNEL);
/*分配一个input设备*/
input= input_allocate_device();
if(!ddata || !input) {
error= -ENOMEM;
gotofail1;
}
platform_set_drvdata(pdev,ddata);
/* 设置input设备属性 */
input->name= pdev->name;
input->phys="gpio-keys/input0";
input->dev.parent= &pdev->dev;
input->id.bustype= BUS_HOST;
input->id.vendor= 0x0001;
input->id.product= 0x0001;
input->id.version= 0x0100;
/*Enable auto repeat feature of Linux input subsystem */
if(pdata->rep)
__set_bit(EV_REP,input->evbit);
ddata->input= input;
for(i = 0; i nbuttons; i++) {
structgpio_keys_button *button = &pdata->buttons[i];
structgpio_button_data *bdata = &ddata->data[i];
intirq;
unsignedint type = button->type ?: EV_KEY;
bdata->input= input;
bdata->button= button;
setup_timer(&bdata->timer,
gpio_keys_timer, (unsignedlong)bdata);
/*初始化工作队列 */
INIT_WORK(&bdata->work,gpio_keys_report_event);
/*申请GPIO口*/
error = gpio_request(button->gpio, button->desc ?:"gpio_keys");
if(error
pr_err("gpio-keys:failed to request GPIO %d,"
"error %d\n", button->gpio, error);
gotofail2;
}
/* 把GPIO设为输入 */
error= gpio_direction_input(button->gpio);
if(error
pr_err("gpio-keys:failed to configure input"
"direction for GPIO %d, error %d\n",
button->gpio,error);
gpio_free(button->gpio);
gotofail2;
}
/*获取GPIO对应的中断*/
irq= gpio_to_irq(button->gpio);
if(irq
error= irq;
pr_err("gpio-keys:Unable to get irq number"
"for GPIO %d, error %d\n",
button->gpio,error);
gpio_free(button->gpio);
gotofail2;
}
/*注册中断 */
error= request_irq(irq, gpio_keys_isr,
IRQF_SHARED |
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
button->desc ? button->desc :"gpio_keys",
bdata);
if(error) {
pr_err("gpio-keys:Unable to claim irq %d; error %d\n",
irq,error);
gpio_free(button->gpio);
gotofail2;
}
if(button->wakeup)
wakeup= 1;
/*设置设备对事件的支持,比如设置对键1和键2的支持*/
input_set_capability(input,type, button->code);
}
/*注册input设备*/
error= input_register_device(input);
if(error) {
pr_err("gpio-keys:Unable to register input device, "
"error:%d\n", error);
gotofail2;
}
device_init_wakeup(&pdev->dev,wakeup);
return0;
……
returnerror;
}
[中断服务例程]
staticirqreturn_t gpio_keys_isr(intirq,void*dev_id)
{
structgpio_button_data *bdata = dev_id;
structgpio_keys_button *button = bdata->button;
BUG_ON(irq!= gpio_to_irq(button->gpio));
/*检测是否在platform device设置了去抖动www.linuxidc.com*/
if(button->debounce_interval)
mod_timer(&bdata->timer,/* 延迟msecs_to_jiffies(button->debounce_interval)个jiffies后执行schedule_work()*/
jiffies+ msecs_to_jiffies(button->debounce_interval));
else
schedule_work(&bdata->work);
returnIRQ_HANDLED;
}
这里的中断被分成了上半部和下半部,上半部(中断处理例程)对中断进行了快速的相应,马上调度work上报事件信息:
staticvoidgpio_keys_report_event(structwork_struct *work)
{
structgpio_button_data *bdata =
container_of(work,structgpio_button_data, work);
structgpio_keys_button *button = bdata->button;
structinput_dev *input = bdata->input;
unsignedint type = button->type ?: EV_KEY;
intstate = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
input_event(input,type, button->code, !!state);
input_sync(input);
}
[两个结构体]
structgpio_keys_button {
/*Configuration parameters */
intcode;/* 输入事件代码(KEY_*, SW_*) */
intgpio;/* gpio口 */
intactive_low;/* 低电平有效*/
char*desc;/* 功能描述 */
inttype;/* 输入事件类型(EV_KEY, EV_SW) */
intwakeup;/* configure thebutton as a wake-up source */
intdebounce_interval;/* 去抖动间隔,单位微秒*/
};
structgpio_keys_platform_data {
structgpio_keys_button *buttons;
intnbuttons;
unsignedint rep:1;/* enable inputsubsystem auto repeat */
};
到此基本上分析完了
[工程中的实现]
上面只是分析了gpio-keys的作用,但是我们要如何使用呢?下面是具体的platform device的实现:
staticstructgpio_keys_button sc_buttons[]= {
{
.gpio = S3C2410_GPG(0),/* K1 */
.code = KEY_1,
.desc ="KEY1",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(3),/* K2 */
.code = KEY_2,
.desc ="KEY2",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(5),/* K3 */
.code = KEY_3,
.desc ="KEY3",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(6),/* K4 */
.code = KEY_4,
.desc ="KEY4",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(7),/* K5 */
.code = KEY_5,
.desc ="KEY5",
.active_low = 1,
},
};
staticstructgpio_keys_platform_datasc_button_data = {
.buttons = sc_buttons,
.nbuttons =ARRAY_SIZE(sc_buttons),
};
staticstructplatform_device sc_button_device= {
.name ="gpio-keys",/* 与platform driver的名字要相同 */
.id = -1,
.dev = {
.platform_data =&sc_button_data,
}
};
staticstructplatform_device*mini2440_devices[] __initdata = {
……
&sc_button_device,
};
最后记得把内核中对应的配置选项选上就OK。