• linux 输入子系统(3) button platform driver


    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 ",
                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 ",
                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 ",
                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 ",
                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 ");
            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 ",
                error);
            goto fail2;
        }

        error = input_register_device(input);//注册该输入设备
        if (error) {
            dev_err(dev, "Unable to register input device, error: %d ",
                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");

  • 相关阅读:
    python操作Excel表格
    Spring的AntPathMatcher(路径匹配)
    【纪中受难记】——Day17:本来能AK
    PAT (Basic Level) Practice (中文)1009 说反话 (20 分)
    PAT (Basic Level) Practice (中文)1008 数组元素循环右移问题 (20 分)
    PAT (Basic Level) Practice (中文)C++ & python 语言实现 —— 题解目录
    PAT (Basic Level) Practice (中文)C++ & python 语言实现 —— 题解目录
    PAT (Basic Level) Practice (中文)1007 素数对猜想 (20 分)
    PAT (Basic Level) Practice (中文)1007 素数对猜想 (20 分)
    PAT (Basic Level) Practice (中文)1006 换个格式输出整数 (15 分)
  • 原文地址:https://www.cnblogs.com/xuyh/p/4866883.html
Copyright © 2020-2023  润新知