I.MX6 gpio-keys driver hacking

/****************************************************************************
 *                   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

I.MX6 gpio-keys driver hacking的相关文章

I.MX6 PWM buzzer driver hacking with Demo test

/***************************************************************************** * I.MX6 PWM buzzer driver hacking with Demo test * 声明: * 1. I.MX6和OK335xS实现PWM驱动函数是不一样的: * 2. 通过分析PWM驱动,了解有哪些驱动函数可以用: * 3. 使用I.MX6提供的PWM函数,编写测试用例buzzer驱动: * 4. 使用C编写测试程序.

I.MX6 ar1020 SPI device driver hacking

/************************************************************************************ * I.MX6 ar1020 SPI device driver hacking * 声明: * 1. 本文主要是解读I.MX6中ar1020 SPI设备注册,以及驱动调用流程: * 2. 本文主要使用了vim+ctags进行代码跟踪,所以几乎都是函数原型之间的调用: * * 2015-9-5 晴 深圳 南山平山村 曾剑锋 *

I.MX6 AD7606-4 device driver registe hacking

/********************************************************************** * I.MX6 AD7606-4 device driver registe hacking * 说明: * 看一下AD7606的驱动注册上是否存在一些问题. * * 2017-8-4 深圳 龙华樟坑村 曾剑锋 *********************************************************************/ /

I.MX6 Linux I2C device&amp; driver hacking

/******************************************************************************************* * I.MX6 Linux I2C device& driver hacking * 声明: * 1. 本文主要是对Linux I2C驱动进行代码跟踪,主要是为了能够对I2C驱动框架有个全面的了解: * 2. 本文源代码来自myzr_android4_2_2_1_1_0.tar.bz2: * 3. 如果你有兴趣,

I.MX6 bq27441 driver hacking

/************************************************************************* * I.MX6 bq27441 driver hacking * 声明: * 本文主要是记录对电池计量芯片bq27441芯片驱动注册过程进行代码跟踪. * * 2016-2-19 深圳 南山平山村 曾剑锋 ************************************************************************

OK335xS LAN8710 phy driver hacking

/******************************************************************** * OK335xS LAN8710 phy driver hacking * 说明: * 本文主要是对OK335xS中的phy的驱动进行代码跟踪,并解决当前遇到 * LAN8710上电后插入网线,会导致LAN8710无法自动握手,Link灯不亮,内核 * 也检测不到LAN8710有状态发生了改变,最终问题定位于LAN8710的驱动初 * 始化部分,本文解决办

OK335xS knob driver hacking

/************************************************************************* * OK335xS knob driver hacking * 说明: * 本文主要是为了分析knob设备的创建,驱动层如何注册,发送信息. * * 2015-11-18 晴 深圳 南山平山村 曾剑锋 ************************************************************************/

I.MX6 Ar8031 device register hacking

/***************************************************************************** * I.MX6 Ar8031 device register hacking * 声明: * 主要是为了知道网卡的注册流程,如果需要对网卡中的一些需求进行修改时,能够 * 能够快速的对需求进行分析.修改. * * 2015-8-15 雨 深圳 南山区平山村 曾剑锋 **************************************

OK335xS pwm buzzer Linux driver hacking

/**************************************************************************** * OK335xS pwm buzzer Linux driver hacking * 声明: * 本文仅仅是为了知道如何使用pwm来控制buzzer,已达到控制不同声音的频率. * * 2015-10-7 雨 深圳 南山平山村 曾剑锋 *****************************************************