gpio_key按键驱动

时间:2022-07-22
本文章向大家介绍gpio_key按键驱动,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1.gpio_key.c介绍

1.1 功能介绍

Linux内核下的 drivers/input/keyboard/gpio_keys.c实现了一个体系结构无关的GPIO按键驱动,使用此按键驱动,只需在设备树gpio-key节点添加需要的按键子节点即可。驱动的实现非常简单,但是较适合于实现独立式按键驱动。

1.2 架构介绍

gpio-keys是基于input架构实现的一个通用GPIO按键驱动。该驱动基于platform_driver架构,实现了驱动和设备分离,符合Linux设备驱动模型的思想。本文以自己的理解介绍gpio_keys.c驱动实现原理及代码技巧。

2.设备树

设备树节点转化成device_node.gpio_keys. c与其他驱动一样采用platform总线架构,在设备树获取硬件相关属性。

Show me the code:

从上述代码可看到,gpio_keys.c节点内定义两个按键节点: “key_power”、”key_headset”。每个按键节点包括一个gpio所用到的所有硬件属性。拿第一个设备节点解析:

        key_power {

                label = "Power Key";      //按键描述性名称

                linux,code =<116>;            //键值,即中断触发上报的键值与内核定义的保持一致。

                gpios = <&pmic_eic 1="">;  //按键gpio  &pmic_eic: gpio组由dtsi定义 1: gpio号 1:有效电平

                debounce-interval =<5>;  //去抖间隔 单位ms

                gpio-key,wakeup;        //可唤醒系统  

                gpio-key,level-trigger;     //中断触发方式level-trigger: 条件触发 edge-trigger: 边缘触发

        };

3.数据结构

优秀的代码必然离不开优秀的数据结构,gpio_key.c之所以能够做到完全脱离板级芯片,实现通用与其数据结构的建立不无关系。以下列举使用到的数据结构:

device_node //设备树节点结构体

device //设备类型

platform_device //platform总线设备

platform_data

input_dev

gpio_keys_platform_data

注:其中device_node、device 、platform_device 、三个结构体,是在驱动编程中经常被用到较为重要的数据结构。

以我自己的理解来解释这三种结构体的关系:

device_node : 用于采集一个设备树节点信息的结构体。因此需要具备设备树属性的成员,例如:硬件设备名(const char *name)、属性指针(struct property *property)、父节点(struct device_node *parent)、子节点(struct device_node *child )等。

device : 指相同类型设备的一种集合结构体。包含了这种类型的所有信息,例如:设备类型(const struct device_type *type;)、该设备基于的总线类型(struct bus_type *bus)、具体的设备树节点信息(struct device_node *of_node)等。

platform_device: 基于platform总线的设备结构体。即挂在虚拟platform总线架构的设备,其包含的成员:设备结构体(struct device dev)、虚拟platform设备名(const char *name)等。

列举以下这三种最常见的结构体,之间的关系:

device_node = device->of_node

device = platform_device->dev

platform_device = of_find_device_by_node(device_node)

由上述关系可知道,只要有一个参数已知,其余两个参数都可以获取到。列举出他们建的关系:

platform_device --> device --> device_node --> platform_device

4.驱动实现

4.1获取设备树属性

按道理,能成功进入probe,就说明设备树platform_device和驱动platform_driver匹配成功,传进来的platform_device *pdev就包含设备树硬件信息。

依然是熟悉的配方!设备节点拿属性。

先从传来的参数里拿设备:

struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;

struct device *dev = &pdev->dev;

然后判断有没有数据, pdata若没数据,我就继续拿:

error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);

pdata = &alt_pdata;

看一下gpio_keys_get_devtree_pdata里是怎么拿的:

注:容易进入误区的一点,驱动设备树of _xx API并不都是直接从设备树里直接拿到属性的,而是先将设备树节点属性值转化为device_node数据结构体成员值,然后驱动通过of_xx API从device_node结构体中获取到相应属性的值。因此device_node的转化就非常重要了!

通过上述代码,发现还是从传参struct device dev里拿struct device_node node。

(1) 先拿设备节点: node = dev->of_node;(也可以通过of_find_node_by_path(); 或of_find_compatible_node() 拿设备节点。) 然后拿父节点属性值 “input-name”,对应设备树

(2) 再通过of_xx API 拿node对应的属性值。

这里,源码通过遍历检测node下存在的子节点,实现对每一个按键属性初始化。贴出遍历循环宏。

(3) 在遍历里拿每个gpio默认电平。

即设备树中gpios = <&pmic_eic 1 1> 第三个参数。此属性还有另一种写法例如gpios = <&pmic 1 GPIO_ACTIVE_HIGH>;意义是一样的。

of_get_gpio_flags(pp, 0, &flags); 返回gpios项里的第三个有效电平参数。

(4) 在遍历里获取中断号

(5) 然后通过of_property_read_u32 可在device_node拿到指定属性名的属性值。

注:其实在节点属性值获取时,根据属性值的格式都可以选择of_property_xx函数获取,代码里所谓的特定属性获取API大都是对of_property_xx的一层封装,例如

附上驱动遍历获取设备树节点属性值代码:

4.2使用input架构

(1) 申请input设备

input = input_allocate_device();

(2) 填充input结构体成员

(3) 设置GPIO按键

主要负责申请GPIO管脚,设置状态,输出方向,中断申请等

API: gpio_keys_setup_key(pdev, input, bdata, button);

(4) 注册input设备

input_register_device(input);

4.3上报按键事件

按键状态发生变化时,会触发中断,在中断子服务函数中,先通过消抖参数值判断是否消抖,如果消抖就启用定时器上报,若无需消抖就就直接上报按键事件。

中断子服务函数: