输入子系统之典型源码分析
初始化函数中注册platform_driver结构体。这是个虚拟总线驱动。
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver); //注册 platform_driver
}
该platform_driver的定义如下。
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe, //probe函数,它是重点。
.remove = __devexit_p(gpio_keys_remove),
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
.pm = &gpio_keys_pm_ops,
.of_match_table = gpio_keys_of_match,
}
};
由platform总线框架可知,在调用platform_driver_register(&gpio_keys_device_driver);向platform_bus注册platform_driver时,会调用platform_match()函数来和总线中的设备链表一一匹配,如果有同名的platform_dev时,驱动程序会调用gpio_keys_probe()函数。
(platform_dev结构以\arch\arm\mach-s3c24xx\Mach-mini2440.c文件中的定义为例)
下面先来看下,Mach-mini2440.c文件中的platform_dev结构体。
static struct platform_device mini2440_button_device = {
.name = "gpio-keys", //名字和platform_driver中匹配
.id = -1,
.dev = {
.platform_data = &mini2440_button_data, //附加信息。开发者可自定义
}
};
static struct gpio_keys_platform_data mini2440_button_data = {
.buttons = mini2440_buttons, //表示按键的结构体
.nbuttons = ARRAY_SIZE(mini2440_buttons), //按键数=5
};
static struct gpio_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,
},
#if 0
/* this pin is also known as TCLK1 and seems to already
* marked as "in use" somehow in the kernel -- possibly wrongly */
{
.gpio = S3C2410_GPG(11), /* K6 */
.code = KEY_F6,
.desc = "Button 6",
.active_low = 1,
},
#endif
};
下面进入到probe函数中,它会用到platform_device结构体里面的私有数据
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; //获得platform_device结构体里面的附加数据
struct gpio_keys_drvdata *ddata;
struct device *dev = &pdev->dev; //获得设备结构体
struct gpio_keys_platform_data alt_pdata;
struct input_dev *input; //input_dev结构体,输入子系统的重点
int i, error;
int wakeup = 0;
if (!pdata) { //出错处理
error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
if (error)
return error;
pdata = &alt_pdata;
}
ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL); //分配一块内存空间,用来存放结构struct gpio_keys_drvdata数据和多个按键相关gpio_button_data结构体。
input = input_allocate_device(); //分配一个输入设备。
if (!ddata || !input) { //出错处理
dev_err(dev, "failed to allocate state\n");
error = -ENOMEM;
goto fail1;
}
ddata->input = input;
ddata->n_buttons = pdata->nbuttons; //记录附加信息中的按键数
ddata->enable = pdata->enable; //这个函数暂时不清楚
ddata->disable = pdata->disable; //这个函数暂时不清楚
mutex_init(&ddata->disable_lock); //互斥锁的初始化
platform_set_drvdata(pdev, ddata); //该函数的功能是
/*ddata是probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将ddata保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。*/
input_set_drvdata(input, ddata); //该函数功能同上,将ddata保存到input总线设备的私有数据。
input->name = pdata->name ? : pdev->name; //设置input_dev,重点
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;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit); //可以产生重复类事件
for (i = 0; i < pdata->nbuttons; i++) { //重点在gpio_keys_setup_key(pdev, input, bdata, button);设置定时器,申请中断等操作。
const struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_button_data *bdata = &ddata->data[i];
error = gpio_keys_setup_key(pdev, input, bdata, button);
if (error)
goto fail2;
if (button->wakeup)
wakeup = 1;
}
error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);//linux sysfs下创建文件给用户层使用。
if (error) {
dev_err(dev, "Unable to export keys/switches, error: %d\n",
error);
goto fail2;
}
error = input_register_device(input); //注册输入事件
if (error) {
dev_err(dev, "Unable to register input device, error: %d\n",
error);
goto fail3;
}
/* get current state of buttons that are connected to GPIOs */ //上报当前状态
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
if (gpio_is_valid(bdata->button->gpio))
gpio_keys_gpio_report_event(bdata);
}
input_sync(input);
device_init_wakeup(&pdev->dev, wakeup); //不清楚其作用
return 0;
fail3:
sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
fail2:
while (--i >= 0)
gpio_remove_key(&ddata->data[i]);
platform_set_drvdata(pdev, NULL);
fail1:
input_free_device(input);
kfree(ddata);
/* If we have no platform_data, we allocated buttons dynamically. */
if (!pdev->dev.platform_data)
kfree(pdata->buttons);
return error;
}
static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
struct input_dev *input,
struct gpio_button_data *bdata,
const struct gpio_keys_button *button)
{
const char *desc = button->desc ? button->desc : "gpio_keys";
struct device *dev = &pdev->dev;
irq_handler_t isr;
unsigned long irqflags;
int irq, error;
bdata->input = input;
bdata->button = button;
spin_lock_init(&bdata->lock);
if (gpio_is_valid(button->gpio)) {
error = gpio_request(button->gpio, desc); //申请GPIO管脚,暂时未明白
if (error < 0) {
dev_err(dev, "Failed to request GPIO %d, error %d\n",
button->gpio, error);
return error;
}
error = gpio_direction_input(button->gpio); //管脚方向设置为输入
if (error < 0) {
dev_err(dev,
"Failed to configure direction for GPIO %d, error %d\n",
button->gpio, error);
goto fail;
}
if (button->debounce_interval) {
error = gpio_set_debounce(button->gpio, //设置去抖动时间,并没有提供此函数,应该会导致程序错误
button->debounce_interval * 1000);
/* use timer if gpiolib doesn't provide debounce */
if (error < 0)
bdata->timer_debounce =
button->debounce_interval;
}
irq = gpio_to_irq(button->gpio); //将GPIO映射为IRQ中断:并没有提供此函数,应该会导致程序错误
if (irq < 0) {
error = irq;
dev_err(dev,
"Unable to get irq number for GPIO %d, error %d\n",
button->gpio, error);
goto fail;
}
bdata->irq = irq;
INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
setup_timer(&bdata->timer, //设置定时器
gpio_keys_gpio_timer, (unsigned long)bdata);
isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
} else {
if (!button->irq) {
dev_err(dev, "No IRQ specified\n");
return -EINVAL;
}
bdata->irq = button->irq;
if (button->type && button->type != EV_KEY) {
dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
return -EINVAL;
}
bdata->timer_debounce = button->debounce_interval;
setup_timer(&bdata->timer,
gpio_keys_irq_timer, (unsigned long)bdata);
isr = gpio_keys_irq_isr;
irqflags = 0;
}
input_set_capability(input, button->type ?: EV_KEY, button->code); //设置产生哪类事件
/*
* If platform has specified that the button can be disabled,
* we don't want it to share the interrupt line.
*/
if (!button->can_disable)
irqflags |= IRQF_SHARED;
error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata); //申请中断
if (error < 0) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
bdata->irq, error);
goto fail;
}
return 0;
fail:
if (gpio_is_valid(button->gpio))
gpio_free(button->gpio);
return error;
}