(本文所有内容全部在linux-2.6.36内核基础上进行)
学习SoC留下的习惯,从基本IO入手。如果自己动手写字符驱动就违背了linux的platform device and driver 的精神,如果自己写我又没那水平,就从内核自带的gpio_keys驱动入手,了解输入子系统的应用。
Gpio输入子系统的初始化和注销是从platform_driver_register(&gpio_keys_device_driver)和platform_driver_unregister(&gpio_keys_device_driver)实现的:
/drivers/input/keyboard/gpio_key.c(line632-line643)
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver);
}
static void __exit gpio_keys_exit(void)
{
platform_driver_unregister(&gpio_keys_device_driver);
}
module_init(gpio_keys_init);
module_exit(gpio_keys_exit);
在看到正文之前再看看struct platform_driver的设定:
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &gpio_keys_pm_ops,
#endif
}
};
从上面struct platform_driver可以看出来:
probe是从static int __devinit gpio_keys_probe(struct platform_device *pdev)函数开始的;
remove函数是从static int __devexit gpio_keys_remove(struct platform_device *pdev)函数开始的;
__devexit_p 的定义:#define __devexit_p(x) x
下面进入正题, gpio_keys_probe函数
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata;
struct device *dev = &pdev->dev;
struct input_dev *input;
int i, error;
int wakeup = 0;
ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL);
input = input_allocate_device(); //申请input_dev结构
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); //把input_dev结构放好(以后方便调用)
input_set_drvdata(input, ddata); // input_dev结构初始化并创建链接
//对input结构赋值
input->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;
//到此,对变态的input_dev结构初始化完成,input_dev结构异常的庞大,定义在include/linux/input.h中,具体成员说明在函数定义中有介绍,还是需要的时候再自行查找吧。
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
//下面的循环对需要的每一个按键进行gpio相关的初始化并标记event为可用
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;
error = gpio_keys_setup_key(pdev, bdata, button);
if (error)
goto fail2;
if (button->wakeup)
wakeup = 1;
input_set_capability(input, type, button->code);
}
// This function creates a group for the first time. It will explicitly warn and error if any of the attribute files being created already exist.
error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
if (error) {
dev_err(dev, "Unable to export keys/switches, error: %d\n",
error);
goto fail2;
}
// This function registers device with input core. The device must be allocated with input_allocate_device() and all it's capabilities set up before registering.(原来上面那么多动作并未进行真正的input_dev的注册,直到此处才进行。。。)
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 */
for (i = 0; i < pdata->nbuttons; i++)
gpio_keys_report_event(&ddata->data[i]);
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) {
free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
if (ddata->data[i].timer_debounce)
del_timer_sync(&ddata->data[i].timer);
cancel_work_sync(&ddata->data[i].work);
gpio_free(pdata->buttons[i].gpio);
}
platform_set_drvdata(pdev, NULL);
fail1:
input_free_device(input);
kfree(ddata);
return error;
}
基本注册流程了解完之后看两个比较值得关注的功能函数
1.void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
/**
* input_set_capability - mark device as capable of a certain event
* In addition to setting up corresponding bit in appropriate capability
* This function handle the corresponding event types.
*/
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
switch (type) {
case EV_KEY:
__set_bit(code, dev->keybit);
break;
case EV_REL:
__set_bit(code, dev->relbit);
break;
case EV_ABS:
__set_bit(code, dev->absbit);
break;
case EV_MSC:
__set_bit(code, dev->mscbit);
break;
case EV_SW:
__set_bit(code, dev->swbit);
break;
case EV_LED:
__set_bit(code, dev->ledbit);
break;
case EV_SND:
__set_bit(code, dev->sndbit);
break;
case EV_FF:
__set_bit(code, dev->ffbit);
break;
case EV_PWR:
/* do nothing */
break;
default:
printk(KERN_ERR
"input_set_capability: unknown type %u (code %u)\n",
type, code);
dump_stack();
return;
}
__set_bit(type, dev->evbit);
}
2. static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
struct gpio_button_data *bdata,
struct gpio_keys_button *button)
/**
*这个函数的主要功能就是初始化gpio、申请中断;
*该函数的所有gpio功能相关函数定义在板文件功能或是通用功能库中,
*通用功能库driver/gpio/gpiolib.c
**/
static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
struct gpio_button_data *bdata,
struct gpio_keys_button *button)
{
…
error = gpio_request(button->gpio, desc);
…
error = gpio_direction_input(button->gpio);
…
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);
…
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
/*
* 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_irq(irq, gpio_keys_isr, irqflags, desc, bdata);
…
}
gpio的初始化以及端口的申请工作到此就结束了,下面再对gpio的处理工作event的工作流程进行简单的梳理。。。