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上报按键事件
按键状态发生变化时,会触发中断,在中断子服务函数中,先通过消抖参数值判断是否消抖,如果消抖就启用定时器上报,若无需消抖就就直接上报按键事件。
中断子服务函数:
- 小案例(四):销售额下滑(python)
- 【最新TensorFlow1.4.0教程01】TF1.4.0介绍与动态图机制 Eager Execution使用
- 把插入的数据自动备份到另一个表中 ~ 语境:本地和服务器自动同步
- 数据分析小案例(三):调查问卷(python)
- CTF---Web入门第十六题 天下武功唯快不破
- 数据分析小案例(二):面包是不是变轻了(python)
- 数据分析小案例(一):商业街抽奖(python)
- Bagging算法(R语言)
- iOS @property探究(一): 基础详解你要知道的@property都在这里
- 在创建带输出参数和返回值的存储过程时---犯下的一个低级错误
- iOS block探究(二): 深入理解你要知道的block都在这里
- 使用开源人脸特征提取器进行脸部颜值评分
- iOS @property探究(二): 深入理解你要知道的@property都在这里
- iOS block探究(一): 基础详解你要知道的block都在这里
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Centos 7 下 docker 导入导出镜像 实践笔记
- linux(centos)搭建.net core 运行环境
- 11g RAC 在线存储迁移实现 OCR 磁盘组完美替换
- MAC下 Centos7 下 免账号免密码便捷登录服务器的正确姿势 实践笔记
- AnimatedList 介绍及使用
- Flutter之SliverAppBar
- OpenGL ES 环境搭建
- Asp.Net Core 程序部署到Linux(centos)生产环境(一):普通部署
- Asp.Net Core 程序部署到Linux(centos)生产环境(二):docker部署
- docker-compose 安装jenkins的正确姿势 实践笔记
- windows安装nginx注册为服务的正确姿势 并设置开机自启 实践笔记
- windows navicat连接oracle11G 自用 实践笔记
- docker安装官方redis集群并集群连接测试 的正确姿势 自用 实践笔记
- Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(一):Jenkins安装
- Centos7安装轻量级TCP转发工具rinetd注册为服务的正确姿势 并设置开机自启 实践笔记 自用