zoukankan      html  css  js  c++  java
  • Linux Driver : gpio-keys

    Linux Driver : gpio-keys的解析

    背景

    在阅读高通设备树配置一个按键的时候,没有找到按键是在什么时候进行处理的。因此根据仅有的线索gpio-key.c进行分析,发现根据之前的学习积累,很快就看懂了。

    介绍

    gpio-keys是基于platform来实现实现的一个通用的GPIO按键驱动,对上可以提供input子系统的event。

    源码位置:drivers/input/keyboard/gpio_keys.c

    这个文件是硬件无关的,而硬件有关的需要我们自己来注册。

    整体流程:

    0、指定硬件注册

    1、初始化、解析硬件属性

    2、注册中断、workqueue

    3、处理中断、延迟消抖

    过程解析

    设备树

    随便拿一段设备树来作为例子。

    gpio_keys { 
        compatible = "gpio-keys";
        label = "gpio-keys";
        pinctrl-names = "default";
        pinctrl-0 = <&key_vol_up_default &key_sidekey1
            		&key_sidekey2 &key_sidekey3 &hall_dev_key>;
    
        vol_up { 
            label = "volume_up"; 
            gpios = <&pm6125_gpios 5 GPIO_ACTIVE_LOW>; 
            linux,input-type = <1>;
            linux,code = <KEY_VOLUMEUP>;
            linux,can-disable;
            debounce-interval = <15>;
            gpio-key,wakeup;
        };
    
        userkey1 {
            label = "userkey1";
            gpios = <&tlmm 107 GPIO_ACTIVE_HIGH>;
            linux,input-type = <1>;
            linux,code = <KEY_F22>;
            linux,can-disable;
            debounce-interval = <15>;
            gpio-key,wakeup;
        };
    };
    

    注册

    第一步就是在初始化时注册platform_driver:

    static const struct of_device_id gpio_keys_of_match[] = {
        { .compatible = "gpio-keys", },
        { }, 
    };
    
    MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
    
    static struct platform_driver gpio_keys_device_driver = {
        .probe      = gpio_keys_probe,
        .driver     = {
            .name   = "gpio-keys",
            .pm = &gpio_keys_pm_ops,
            .of_match_table = gpio_keys_of_match,
        }
    };
    
    static int __init gpio_keys_init(void)
    {
    	return platform_driver_register(&gpio_keys_device_driver);
    }
    

    当发现有设备匹配时(compatible = "gpio-keys"),执行gpio_keys_probe函数。

    适配

    在适配的时候,就会用到下面的对象。

    struct gpio_keys_button_data {
        struct gpio_desc *gpiod;
        int last_state;
        int count;
        int threshold;
    };
    
    struct gpio_button_data {
        const struct gpio_keys_button *button;
        struct input_dev *input;
        struct gpio_desc *gpiod;
    
        unsigned short *code;
    
        struct timer_list release_timer;
        unsigned int release_delay; /* in msecs, for IRQ-only buttons */
    
        struct delayed_work work;
        unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */
    
        unsigned int irq; 
        unsigned int wakeup_trigger_type;
        spinlock_t lock;
        bool disabled;
        bool key_pressed;
        bool suspended;
    };
    
    struct gpio_keys_drvdata {
        const struct gpio_keys_platform_data *pdata;
        struct input_dev *input;
        struct mutex disable_lock;
        unsigned short *keymap;
        struct gpio_button_data data[0];
    };
    

    在适配的时候,完成了设备数据的处理以及获取、中断的注册

    static int gpio_keys_probe(struct platform_device *pdev)
    {
        struct device *dev = &pdev->dev;
        const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
        struct fwnode_handle *child = NULL;
        struct gpio_keys_drvdata *ddata;
        struct input_dev *input;
        size_t size;
        int i, error;
        int wakeup = 0; 
    
        if (!pdata) {
            // 获取设备以及子节点的属性值,并保存下来,用于在后续的功能中使用
            pdata = gpio_keys_get_devtree_pdata(dev);
            if (IS_ERR(pdata))
                return PTR_ERR(pdata);
        }    
    
        size = sizeof(struct gpio_keys_drvdata) +
                pdata->nbuttons * sizeof(struct gpio_button_data);
        ddata = devm_kzalloc(dev, size, GFP_KERNEL);
        if (!ddata) {
            return -ENOMEM;
        }    
    
        ddata->keymap = devm_kcalloc(dev,
                         pdata->nbuttons, sizeof(ddata->keymap[0]),
                         GFP_KERNEL);
        if (!ddata->keymap)
            return -ENOMEM;
    
        input = devm_input_allocate_device(dev);
        if (!input) {
            return -ENOMEM;
        }    
    
        ddata->pdata = pdata;
        ddata->input = input;
        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 = 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;
    
        input->keycode = ddata->keymap;
        input->keycodesize = sizeof(ddata->keymap[0]);
        input->keycodemax = pdata->nbuttons;
    
        /* Enable auto repeat feature of Linux input subsystem */
        if (pdata->rep)
            __set_bit(EV_REP, input->evbit);
    
        for (i = 0; i < pdata->nbuttons; i++) {
            const struct gpio_keys_button *button = &pdata->buttons[i];
    
            if (!dev_get_platdata(dev)) {
                child = device_get_next_child_node(dev, child);
                if (!child) {
                    return -EINVAL;
                }
            }
    		// 设置按键,注册中断(gpiod_to_irq)、workqueue、
            // 设置input子系统有关参数(input_set_capability),还设置了定时器(timer_setup,用于消抖)。
            error = gpio_keys_setup_key(pdev, input, ddata,
                            button, i, child);
            if (error) {
                fwnode_handle_put(child);
                return error;
            }
    
            if (button->wakeup)
                wakeup = 1;
        }
    
        fwnode_handle_put(child);
    
        error = devm_device_add_group(dev, &gpio_keys_attr_group);
        if (error) {
            return error;
        }
        
    	// 设为唤醒属性(用于电源管理)
        device_init_wakeup(dev, wakeup);
    
        return 0;
    }
    

    中断响应

    中断上半文:

    static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
    {
        struct gpio_button_data *bdata = dev_id;
    
        if (bdata->button->wakeup) {
            const struct gpio_keys_button *button = bdata->button;
    
            pm_stay_awake(bdata->input->dev.parent);
            if (bdata->suspended  &&
                (button->type == 0 || button->type == EV_KEY)) {
                /*
                 * Simulate wakeup key press in case the key has
                 * already released by the time we got interrupt
                 * handler to run.
                 */
                // 报告热键
                input_report_key(bdata->input, button->code, 1);
            }
        }
    	// 延迟、消抖
        mod_delayed_work(system_wq,
                 &bdata->work,
                 msecs_to_jiffies(bdata->software_debounce));
    
        return IRQ_HANDLED;
    }
    

    中断下半文:

    static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
    {
        const struct gpio_keys_button *button = bdata->button;
        struct input_dev *input = bdata->input;
        unsigned int type = button->type ?: EV_KEY;
        int state;
        
    	// 如果按键在超时时间内已经释放,则返回
        state = gpiod_get_value_cansleep(bdata->gpiod);
        if (state < 0) {
            return;
        }
    
        if (type == EV_ABS) {
            if (state)
                input_event(input, type, button->code, button->value);
        } else {
            input_event(input, type, *bdata->code, state);
        }
        input_sync(input);
    }
    
    static void gpio_keys_gpio_work_func(struct work_struct *work)
    {
        struct gpio_button_data *bdata =
            container_of(work, struct gpio_button_data, work.work);
    
        gpio_keys_gpio_report_event(bdata);
    
        if (bdata->button->wakeup)
            pm_relax(bdata->input->dev.parent);
    }
    
  • 相关阅读:
    「学习笔记」min_25筛
    HNOI2019游记
    【SDOI2017】数字表格
    【APIO2016】烟火表演
    【SCOI2015】小凸想跑步
    java Thread源码分析
    java ThreadGroup源码分析
    bean获取Spring容器
    spring 管理bean
    thinkphp5.0.19 request
  • 原文地址:https://www.cnblogs.com/schips/p/linux_driver_gpio-key.html
Copyright © 2011-2022 走看看