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的更多相关文章

  1. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  2. linux输入子系统概念介绍

    在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥.非阻塞.定时器去抖动. 上一节文章链接:http://blo ...

  3. linux输入子系统(input subsystem)之evdev.c事件处理过程

    1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...

  4. 7.Linux 输入子系统分析

    为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...

  5. Linux输入子系统(转)

    Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中 ...

  6. Linux输入子系统(Input Subsystem)

    Linux输入子系统(Input Subsystem) http://blog.csdn.net/lbmygf/article/details/7360084 input子系统分析  http://b ...

  7. Linux输入子系统框架分析(1)

    在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...

  8. linux输入子系统

    linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputCore)和输入子系统设备驱 ...

  9. linux输入子系统简述【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/7678035 1,linux输入子系统简述 其实驱动这部分大多还是转载别人的,linux ...

随机推荐

  1. 通过日志动态查看正在执行的mysql语句

    通过日志动态查看正在执行的mysql语句 :tail -f /tmp/general_log.log

  2. hdu2043

    #include <stdio.h> #include <string.h> char sign[]={'A','B','C','D','E','F','G','H','I', ...

  3. 注册苹果开发者账号和iOS9打包上线

    链接: http://www.jianshu.com/p/507ca4e5fde0 http://blog.csdn.net/a283127993/article/details/45828175 i ...

  4. 如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2'

    如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2' 大家在做真机调试的时候,或许会遇到这样的问题,那如何 ...

  5. BZOJ 2134 单选错位 ——期望DP

    发现概率是∑1/两道题答案相同的概率, 稍加化简 #include <map> #include <ctime> #include <cmath> #include ...

  6. URAL Formula 1 ——插头DP

    [题目分析] 一直听说这是插头DP入门题目. 难到爆炸. 写了2h,各种大常数,ural垫底. [代码] #include <cstdio> #include <cstring> ...

  7. 跟着xiaoxin巨巨做cf

    cf 385 C. Bear and Prime Numbers 题目大意:有一个数列{xi},每次给出一个询问[l, r],即问 S(l ,r)是l和r之间的素数,f(p)表示数列{xi}中整除p的 ...

  8. 刷题总结——子串(NOIP2015提高组)

    题目: 题目背景 NOIP2015 提高组 Day2 T2 题目描述 有两个仅包含小写英文字母的字符串 A 和 B .现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在 ...

  9. Laravel 之Artisan

    简介: Artisan是Laravel中自带的命令行工具的名称: 由强大的Symfony Console组件驱动的: 提供了一些对应用开发有帮助的命令: 查看所有可用的Artisan的命令 php a ...

  10. 自定义header参数时的命名要求

    HTTP头是可以包含英文字母([A-Za-z]).数字([0-9]).连接号(-)hyphens, 也可义是下划线(_).在使用nginx的时候应该避免使用包含下划线的HTTP头.主要的原因有以下2点 ...