标签:
button platform driver 一般位于driver/input/keyboard/gpio_keys.c
/*用于按键事件的上报,它将在按键的中断发生后被调用。其中逻辑就是获取到按键类型和具体的按键,调用input_event()函数进行上报,上报的按键码就来自那个按键。*/
static void gpio_keys_report_event(struct gpio_button_data *bdata)   
{    
    struct gpio_keys_button *button = bdata->button;    //取出每一个键的结构体    
    struct input_dev *input = bdata->input;             //把该键的input设备也取出来    
    unsigned int type = button->type ?: EV_KEY;            //类型为key    
    
    int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;  //键值
    if (type == EV_ABS) {   
        if (state)    
            input_event(input, type, button->code, button->value);//报告键值    
    } else {    
        if ((button->lock_interval) &&    
            (get_jiffies_64() - bdata->lock_jiffies_64    
             > msecs_to_jiffies(button->lock_interval)) && state)    
                bdata->is_locked = 0;
        if (!bdata->is_locked)   
            input_event(input, type, button->code, !!state);//报告键值
            if (button->lock_interval && !bdata->is_locked && !state) {   
            bdata->is_locked = 1;    
            bdata->lock_jiffies_64 = get_jiffies_64();    
        }    
    }    
    input_sync(input);//同步事件    
}
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_timer(unsigned long _data)   
{    
    struct gpio_button_data *data = (struct gpio_button_data *)_data;
    schedule_work(&data->work);   
}
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)   
{    
    struct gpio_button_data *bdata = dev_id;    
    struct gpio_keys_button *button = bdata->button;
BUG_ON(irq != gpio_to_irq(button->gpio));
    if (bdata->timer_debounce)   
        mod_timer(&bdata->timer,    
            jiffies + msecs_to_jiffies(bdata->timer_debounce));    
    else    
        schedule_work(&bdata->work);
    return IRQ_HANDLED;   
}
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);//申请gpio   
    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);//设置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;    
    }
        bdata->is_locked = 0;   
        bdata->lock_jiffies_64 = get_jiffies_64();
    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;    
    //申请中断上下文    
    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 int gpio_keys_open(struct input_dev *input)   
{    
    struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
    return ddata->enable ? ddata->enable(input->dev.parent) : 0;   
}
static void gpio_keys_close(struct input_dev *input)   
{    
    struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
    if (ddata->disable)   
        ddata->disable(input->dev.parent);    
}
static int __devinit gpio_keys_probe(struct platform_device *pdev)   
{    
    struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;//由platform device传输    
    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);//  pdev->dev->p->driver_data = ddata   
    input_set_drvdata(input, ddata);//    input->dev->p->driver_data = 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;
        //相应gpio的参数设置(方向,中断等等)   
        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);//注册事件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;   
}
static int __devexit gpio_keys_remove(struct platform_device *pdev)   
{    
    struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;    
    struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);    
    struct input_dev *input = ddata->input;    
    int i;
sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
device_init_wakeup(&pdev->dev, 0);
    for (i = 0; i < pdata->nbuttons; i++) {   
        int irq = gpio_to_irq(pdata->buttons[i].gpio);    
        free_irq(irq, &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);    
    }
input_unregister_device(input);
    return 0;   
}
   
#ifdef CONFIG_PM    
static int gpio_keys_suspend(struct device *dev)    
{    
    struct platform_device *pdev = to_platform_device(dev);    
    struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);    
    struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;    
    int i;
    if (device_may_wakeup(&pdev->dev)) {   
        for (i = 0; i < pdata->nbuttons; i++) {    
            struct gpio_keys_button *button = &pdata->buttons[i];    
            struct gpio_button_data *bdata = &ddata->data[i];    
            if (button->wakeup) {    
                int irq = gpio_to_irq(button->gpio);    
                enable_irq_wake(irq);    
                if (button->lock_interval)    
                    bdata->is_locked = 0;    
            }    
        }    
    }
    return 0;   
}
static int gpio_keys_resume(struct device *dev)   
{    
    struct platform_device *pdev = to_platform_device(dev);    
    struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);    
    struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;    
    int i;
for (i = 0; i < pdata->nbuttons; i++) {
        struct gpio_keys_button *button = &pdata->buttons[i];   
        if (button->wakeup && device_may_wakeup(&pdev->dev)) {    
            int irq = gpio_to_irq(button->gpio);    
            disable_irq_wake(irq);    
        }
        gpio_keys_report_event(&ddata->data[i]);   
    }    
    input_sync(ddata->input);
    return 0;   
}
static const struct dev_pm_ops gpio_keys_pm_ops = {   
    .suspend    = gpio_keys_suspend,    
    .resume        = gpio_keys_resume,    
};    
#endif
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    
    }    
};
//注册gpio button platform driver
static int __init gpio_keys_init(void)   
{    
    return platform_driver_register(&gpio_keys_device_driver);    
}
//注销gpio button platform 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");
linux 输入子系统(3) button platform driver
标签:
原文地址:http://www.cnblogs.com/xuyh/p/4866883.html