/**************************************************************************** * I.MX6 gpio-keys driver hacking * 说明: * 1. 本文解读gpio-keys驱动是如何注册,最终处理函数在哪里。 * 2. 从最后生成的设备节点来看,我们直接可以通过操作该设备节点来来让系统 * 进行相关操作,譬如关机、挂起等操作。 * * 2016-3-17 深圳 南山平山村 曾剑锋 ***************************************************************************/ 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 | | } | | }; | | | | 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); | | MODULE_LICENSE("GPL"); | MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>"); | MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs"); | MODULE_ALIAS("platform:gpio-keys"); | | 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(); 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_set_drvdata(input, ddata); 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; /* Enable auto repeat feature of Linux input subsystem */ if (pdata->rep) __set_bit(EV_REP, input->evbit); 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); | } | | 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; | } | | 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; | } | V static int __devinit gpio_keys_setup_key(struct platform_device *pdev, struct gpio_button_data *bdata, struct gpio_keys_button *button) { const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; unsigned long irqflags; int irq, error; setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata); INIT_WORK(&bdata->work, gpio_keys_work_func); --------------------+ | error = gpio_request(button->gpio, desc); | if (error < 0) { | dev_err(dev, "failed to request GPIO %d, error %d\n", | button->gpio, error); | goto fail2; | } | | 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 fail3; | } | | 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); | if (irq < 0) { | error = irq; | dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", | button->gpio, error); | goto fail3; | } | | 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; | /* | * Resume power key early during syscore instead of at device | * resume time. | * Some platform like Android need to konw the power key is pressed | * then to reume the other devcies | */ | if (button->wakeup) | irqflags |= IRQF_NO_SUSPEND | IRQF_EARLY_RESUME; | | error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata); | if (error < 0) { | dev_err(dev, "Unable to claim irq %d; error %d\n", | irq, error); | goto fail3; | } | | return 0; | | fail3: | gpio_free(button->gpio); | fail2: | return error; | } | | static void gpio_keys_work_func(struct work_struct *work) <-------------+ { struct gpio_button_data *bdata = container_of(work, struct gpio_button_data, work); gpio_keys_report_event(bdata); -----------+ } | | static void gpio_keys_report_event(struct gpio_button_data *bdata) <---------+ { struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; printk("zengjf check gpio-keys positon: %s in line %d.\n", __func__, _LINE__); if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); } else { input_event(input, type, button->code, !!state); } input_sync(input); } /** * root@android:/ # cat /proc/bus/input/devices * I: Bus=0019 Vendor=0001 Product=0001 Version=0100 * N: Name="gpio-keys" * P: Phys=gpio-keys/input0 * S: Sysfs=/devices/platform/gpio-keys/input/input0 * U: Uniq= * H: Handlers=event0 * B: PROP=0 * B: EV=3 * B: KEY=100000 0 0 0 * ...... */
时间: 2024-10-23 01:24:11