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);
    }
    
  • 相关阅读:
    Python 学习日记 第七天
    Python 学习日记 第六天
    Python 学习日记 第五天
    Python 学习日记 第四天
    Redis 中的数据类型及基本操作
    Asp.net mvc 中View 的呈现(二)
    Asp.net mvc 中View的呈现(一)
    Asp.net mvc 中Action 方法的执行(三)
    Asp.net mvc 中Action 方法的执行(二)
    Asp.net mvc 中Action 方法的执行(一)
  • 原文地址:https://www.cnblogs.com/schips/p/linux_driver_gpio-key.html
Copyright © 2011-2022 走看看