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. js 格式化 时间插件

    // 对Date的扩展,将 Date 转化为指定格式的String // 月(M).日(d).小时(h).分(m).秒(s).季度(q) 可以用 1-2 个占位符, // 年(y)可以用 1-4 个占 ...

  2. Flask+ Angularjs 实例: 创建博客

    允许任何用户注册 允许注册的用户登录 允许登录的用户创建博客 允许在首页展示博客 允许登录的用户退 后端 Flask-RESTful - Flask 的 RESTful 扩展 Flask-SQLAlc ...

  3. 【Android】SharedPreference存储数据

    SharedPreference存储数据 使用SharedPreference保存数据  putString(key,value) 使用SharedPreference读取数据  getString( ...

  4. 【Luogu】P2759奇怪的函数(二分)

    题目链接 看了题解之后突然发现这题简直是水题.然而不看题解就想不出来.为什么呢? len(x)=log10(x)+1 于是二分寻找x. #include<iostream> #includ ...

  5. PAT天梯赛练习题——L3-008. 喊山(邻接表+BFS)

    L3-008. 喊山 时间限制 150 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 喊山,是人双手围在嘴边成喇叭状,对着远方高山发出“喂—喂喂 ...

  6. BZOJ 2005 [Noi2010]能量采集 ——Dirichlet积

    [题目分析] 卷积一卷. 然后分块去一段一段的求. O(n)即可. [代码] #include <cstdio> #include <cstring> #include < ...

  7. charts 画饼图

    统计某一天某类物体的百分比 新知识点:aggregate https://blog.csdn.net/congcong68/article/details/51619882 主要的 $group $m ...

  8. H5 折线图插件

    一.可以使用Highcharts,参考网址:https://api.hcharts.cn/highcharts: 二.可以使用Echarts,参考网址:http://echarts.baidu.com ...

  9. android自定义Activity窗体大小

    先给大家看图吧: 看,是不是很酷呢,呵呵. 这里我说关键的地方,就是自定义Activity的窗体大小. 这个登录框它不是一个Dialog,而是一个Activity. 如何定义,即把Activity的主 ...

  10. js动态适配移动端font-size,单位:rem

    比如:目前我手中有一张psd图,大小为640*1010,适配苹果5的手机. 方法步骤: 1.我采用font-size=10px为640*1010手机的初始像素大小:  1rem=10px: 此时psd ...