6.1 概述
l Gpio-keys是基于input子系统实现的一个通用的GPIO按键驱动。工程中我们常常会利用它来写按键驱动。
l gpio-keys驱动是基于platform来实现,platform driver(通用的部分)位于drivers/input/keyboard/gpio_keys.c,platform device通常和硬件有关,需要我们在BSP中实现。
6.2 通用驱动部分(platform driver)分析
l 初始化和卸载模块
static int__init gpio_keys_init(void)
{
returnplatform_driver_register(&gpio_keys_device_driver);
}
static void__exit gpio_keys_exit(void)
{
platform_driver_unregister(&gpio_keys_device_driver);
}
l Platform driver结构体
static structplatform_driver gpio_keys_device_driver = {
.probe =gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
.driver ={
.name ="gpio-keys", /*platform driver和platform device通过name来匹配 */
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &gpio_keys_pm_ops,
#endif
}
};
l Gpio_key_probe函数,Probe函数在platform driver和platform device匹配上时调用
static int__devinit gpio_keys_probe(struct platform_device *pdev)
{
input = input_allocate_device();
/*设置相关属性*/
input->name = pdata->name ? :pdev->name;
input->phys ="gpio-keys/input0";
input->dev.parent =&pdev->dev;
input->open = gpio_keys_open;
input->close = gpio_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
for (i = 0; i < pdata->nbuttons;i++) {
struct gpio_keys_button*button = &pdata->buttons[i];
struct gpio_button_data*bdata = &ddata->data[i];
unsigned int type =button->type ?: EV_KEY;
bdata->input = input;
bdata->button = button;
/*申请中断和timer、工作队列初始化*/
error = gpio_keys_setup_key(pdev, bdata, button);
input_set_capability(input, type, button->code);
}
error = sysfs_create_group(&pdev->dev.kobj,&gpio_keys_attr_group);
error = input_register_device(input);
}
Probe主要完成注册输入设备input_dev,在sys目录下产生相应的文件。
l gpio_keys_isr中断服务程序
staticirqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata =dev_id;
struct gpio_keys_button *button =bdata->button;
if (bdata->timer_debounce) /* 检测是否设置了去抖动 */
/*添加一个定时器,延迟msecs_to_jiffies(button->debounce_interval)个jiffies后执行schedule_work()*/
mod_timer(&bdata->timer, jiffies +msecs_to_jiffies(bdata->timer_debounce));
else
schedule_work(&bdata->work);
return IRQ_HANDLED;
}
中断分为了上半部和下半部,下半部使用工作队列来实现
l 中断下半部
static voidgpio_keys_work_func(struct work_struct *work)
{
struct gpio_button_data *bdata =
container_of(work, structgpio_button_data, work);
/* 上报事件信息 */
gpio_keys_report_event(bdata);
}
6.3 与板相关的部分(platform device)
这里以s3c2440的BSP为例,首先需要了解两个结构体,用来GPIO的硬件信息:
l Gpio_keys_buttons
structgpio_keys_button {
/* Configuration parameters */
unsigned int code; /*input event code (KEY_*, SW_*) */
int gpio; /* GPIO口号 */
int active_low; /* 低电平有效 */
const char *desc; /*功能描述 */
unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS)*/
int wakeup; /* configure the button as a wake-up source */
int debounce_interval; /* 去抖动间隔*/
bool can_disable;
int value; /* axis value for EV_ABS */
};
l Gpio_keys_platform_data
structgpio_keys_platform_data {
struct gpio_keys_button *buttons; /* 指向定义好的gpio_keys_button*/
int nbuttons; /* buttons成员个数 */
unsigned int poll_interval; /* polling interval in msecs -
for polling driver only */
unsigned int rep:1; /* enable input subsystem auto repeat*/
int (*enable)(struct device *dev);
void (*disable)(struct device *dev);
const char *name; /* input device name */
};
l BSP中实现代码
n 定义gpio_keys_button,填充GPIO的硬件信息
static structgpio_keys_button mini2440_buttons[] = {
{
.gpio = S3C2410_GPG(0), /*K1 */
.code = KEY_F1,
.desc = "Button 1",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(3), /*K2 */
.code = KEY_F2,
.desc = "Button 2",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(5), /*K3 */
.code = KEY_F3,
.desc = "Button 3",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(6), /*K4 */
.code = KEY_POWER,
.desc = "Power",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(7), /*K5 */
.code = KEY_F5,
.desc = "Button 5",
.active_low = 1,
},
};
n 构造gpio_keys_platform_data
static structgpio_keys_platform_data mini2440_button_data = {
.buttons = mini2440_buttons,
.nbuttons = ARRAY_SIZE(mini2440_buttons),
};
n 构造platform_device
static structplatform_device mini2440_button_device = {
.name ="gpio-keys",
.id =-1,
.dev ={
.platform_data = &mini2440_button_data,
}
};
n 注册platform_device
static structplatform_device *mini2440_devices[] __initdata = {
……
&mini2440_button_device,
};
static void__init mini2440_init(void)
{
……
platform_add_devices(mini2440_devices,ARRAY_SIZE(mini2440_devices));
}