使用gpio子系统实现按键驱动(二)

一,gpio_keys.c介绍

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

gpio-keys是基于input架构实现的一个通用gpio按键驱动,该驱动基于platform_driver架构,实现了驱动和设备分离,符合linux设备驱动模型的思想。

二,主要结构体及其关系

首先大致看下代码实现搞清楚结构体之间的关系,然后根据结构体之前的关系再看代码细节。

1,主要的结构体

struct gpio_keys_drvdata:

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[];
};

struct gpio_keys_platform_data:

struct gpio_keys_platform_data {const struct gpio_keys_button *buttons;int nbuttons;unsigned int poll_interval;unsigned int rep:1;int (*enable)(struct device *dev);void (*disable)(struct device *dev);const char *name;
};

struct gpio_button_data:

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_button:

struct gpio_keys_button {unsigned int code;int gpio;int active_low;const char *desc;unsigned int type;int wakeup;int wakeup_event_action;int debounce_interval;bool can_disable;int value;unsigned int irq;
};
2,结构体之间的关系

三,关键代码分析

以Android volumn up key为例。

1,设备树配置
gpio_keys {compatible = "gpio-keys";label = "gpio-keys";pinctrl-names = "default";pinctrl-0 = <&key_vol_up_default &google_key_default>;vol_up {label = "volume_up";gpios = <&pm7325_gpios 6 GPIO_ACTIVE_LOW>;linux,input-type = <1>;linux,code = <KEY_VOLUMEUP>;gpio-key,wakeup;debounce-interval = <15>;linux,can-disable;};google_key {label = "google_key";gpios = <&tlmm 41 GPIO_ACTIVE_LOW>;linux,input-type = <1>;linux,code = <KEY_SEARCH>;gpio-key,wakeup;debounce-interval = <15>;linux,can-disable;};... ... ...
};
2,

probe函数

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;int i, error;int wakeup = 0;if (!pdata) {//解析设备树配置pdata = gpio_keys_get_devtree_pdata(dev);if (IS_ERR(pdata))return PTR_ERR(pdata);}//给driver data分配内存,struct_size用来计算gpio_keys_drvdata结构体和nbuttons个gpio_button_data 所占内存大小ddata = devm_kzalloc(dev, struct_size(ddata, data, pdata->nbuttons),GFP_KERNEL);if (!ddata) {dev_err(dev, "failed to allocate state\n");return -ENOMEM;}dev_err(dev, "william debug gpio keys driver\n");ddata->keymap = devm_kcalloc(dev,pdata->nbuttons, sizeof(ddata->keymap[0]),GFP_KERNEL);if (!ddata->keymap)return -ENOMEM;//分配input设备input = devm_input_allocate_device(dev);if (!input) {dev_err(dev, "failed to allocate input device\n");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];//获取每个按键节点childif (!dev_get_platdata(dev)) {child = device_get_next_child_node(dev, child);if (!child) {dev_err(dev,"missing child device node for entry %d\n",i);return -EINVAL;}}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 = input_register_device(input);if (error) {dev_err(dev, "Unable to register input device, error: %d\n",error);return error;}device_init_wakeup(dev, wakeup);return 0;
}

button设置函数gpio_keys_setup_key

static int gpio_keys_setup_key(struct platform_device *pdev,struct input_dev *input,struct gpio_keys_drvdata *ddata,const struct gpio_keys_button *button,int idx,struct fwnode_handle *child)
{const char *desc = button->desc ? button->desc : "gpio_keys";struct device *dev = &pdev->dev;//每一个bdata跟button对应,见以上结构体之间的关系struct gpio_button_data *bdata = &ddata->data[idx];irq_handler_t isr;unsigned long irqflags;int irq;int error;bdata->input = input;bdata->button = button;spin_lock_init(&bdata->lock);if (child) {//如果child节点不空,使用此函数获取gpio descriptionbdata->gpiod = devm_fwnode_gpiod_get(dev, child,NULL, GPIOD_IN, desc);if (IS_ERR(bdata->gpiod)) {error = PTR_ERR(bdata->gpiod);if (error == -ENOENT) {/** GPIO is optional, we may be dealing with* purely interrupt-driven setup.*/bdata->gpiod = NULL;} else {if (error != -EPROBE_DEFER)dev_err(dev, "failed to get gpio: %d\n",error);return error;}}} else if (gpio_is_valid(button->gpio)) {/** Legacy GPIO number, so request the GPIO here and* convert it to descriptor.*/unsigned flags = GPIOF_IN;if (button->active_low)flags |= GPIOF_ACTIVE_LOW;error = devm_gpio_request_one(dev, button->gpio, flags, desc);if (error < 0) {dev_err(dev, "Failed to request GPIO %d, error %d\n",button->gpio, error);return error;}//将gpio转成gpio description,为了使用新的gpio控制接口,gpiod接口  bdata->gpiod = gpio_to_desc(button->gpio);if (!bdata->gpiod)return -EINVAL;}if (bdata->gpiod) {//GPIO_ACTIVE_LOW表示在低电平时触发某种操作,而GPIO_ACTIVE_HIGH表示在高电平时触发相同的操作,将逻辑电平与物理电平区分开bool active_low = gpiod_is_active_low(bdata->gpiod);if (button->debounce_interval) {error = gpiod_set_debounce(bdata->gpiod,button->debounce_interval * 1000);/* use timer if gpiolib doesn't provide debounce */if (error < 0)//如果对应的gpio chip没有实现debounce的实现,使用software debouncebdata->software_debounce =button->debounce_interval;}if (button->irq) {bdata->irq = button->irq;} else {irq = gpiod_to_irq(bdata->gpiod);if (irq < 0) {error = irq;dev_err(dev,"Unable to get irq number for GPIO %d, error %d\n",button->gpio, error);return error;}bdata->irq = irq;}//初始化一个delayed work,作为终端的下半部INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func);//中断服务函数,中断上半部isr = gpio_keys_gpio_isr;//触发中断的电平条件,上升沿或者下降沿触发irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;switch (button->wakeup_event_action) {case EV_ACT_ASSERTED:bdata->wakeup_trigger_type = active_low ?IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;break;case EV_ACT_DEASSERTED:bdata->wakeup_trigger_type = active_low ?IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;break;case EV_ACT_ANY:default:/** For other cases, we are OK letting suspend/resume* not reconfigure the trigger type.*/break;}} else {if (!button->irq) {dev_err(dev, "Found button without gpio or irq\n");return -EINVAL;}bdata->irq = button->irq;if (button->type && button->type != EV_KEY) {dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");return -EINVAL;}bdata->release_delay = button->debounce_interval;timer_setup(&bdata->release_timer, gpio_keys_irq_timer, 0);isr = gpio_keys_irq_isr;irqflags = 0;/** For IRQ buttons, there is no interrupt for release.* So we don't need to reconfigure the trigger type for wakeup.*/}bdata->code = &ddata->keymap[idx];*bdata->code = button->code;//设置该输入设备的能力,支持上报的事件类型input_set_capability(input, button->type ?: EV_KEY, *bdata->code);/** Install custom action to cancel release timer and* workqueue item.*///用做软件防抖?当中断下半部触发之后,如果在debounce time时间之内,gpio口电平有变化,会执行gpio_keys_quiesce_key把上报键值的work cancel掉error = devm_add_action(dev, gpio_keys_quiesce_key, bdata);if (error) {dev_err(dev, "failed to register quiesce action, error: %d\n",error);return error;}/** 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;//申请中断,中断服务函数isrerror = devm_request_any_context_irq(dev, bdata->irq, isr, irqflags,desc, bdata);if (error < 0) {dev_err(dev, "Unable to claim irq %d; error %d\n",bdata->irq, error);return error;}return 0;
}

中断服务函数isr

static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
{struct gpio_button_data *bdata = dev_id;BUG_ON(irq != bdata->irq);if (bdata->button->wakeup) {const struct gpio_keys_button *button = bdata->button;//保持系统awake状态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);}}//在防抖时间software_debounce之后,调度执行delayed work,在work中上报input eventmod_delayed_work(system_wq,&bdata->work,msecs_to_jiffies(bdata->software_debounce));return IRQ_HANDLED;
}

退出delayed work的函数

static void gpio_keys_quiesce_key(void *data)
{struct gpio_button_data *bdata = data;if (bdata->gpiod)cancel_delayed_work_sync(&bdata->work);elsedel_timer_sync(&bdata->release_timer);
}

四,按键测试

volumn up:

[     611.497258] /dev/input/event0: EV_KEY       KEY_VOLUMEUP         DOWN

[     611.497258] /dev/input/event0: EV_SYN       SYN_REPORT           00000000             rate 0

[     611.643337] /dev/input/event0: EV_KEY       KEY_VOLUMEUP         UP

[     611.643337] /dev/input/event0: EV_SYN       SYN_REPORT           00000000             rate 6

google key:

[     731.598789] /dev/input/event0: EV_KEY       KEY_SEARCH           DOWN

[     731.598789] /dev/input/event0: EV_SYN       SYN_REPORT           00000000             rate 0

[     731.779700] /dev/input/event0: EV_KEY       KEY_SEARCH           UP

[     731.779700] /dev/input/event0: EV_SYN       SYN_REPORT           00000000             rate 5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/153727.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于ABB速度,加速度,轴监控指令

关于ABB速度&#xff0c;加速度&#xff0c;轴监控 关于轴监控指令要选择启用和关闭&#xff0c;这个指令是为了防止机器人在抓件放件过程中6轴来回旋转&#xff0c;已最佳的姿态运动 收录于合集 #ABB机器人 9个 上一篇关于ABB机器人的IO创建和设置

嵌入式养成计划-38----C++--匿名对象--友元--常成员函数和常对象--运算符重载

八十七、匿名对象 概念&#xff1a;没有名字对象格式 &#xff1a;类名&#xff08;&#xff09;;作用 用匿名对象给有名对象初始化的用匿名对象给对象数组初始化的匿名对象作为函数实参使用 示例 : #include <iostream> using namespace std; class Dog { private:s…

微电网单台并网逆变器PQ控制matlab仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 微电网运行在并网模式下且公共电网供应正常时&#xff0c;因为公共电网给定了电 压和频率的参考值&#xff0c;所有的逆变器可以使用PQ控制方式。 当系统频率为额定频率f0时&#xff0c;系统稳定在A点&#x…

awvs 中低危漏洞

低危 X-Frame-Options Header未配置 查看请求头中是否存在X-Frame-Options Header字段 会话Cookie中缺少secure属性(未设置安全标志的Cookie) 当cookie设置为Secure标志时&#xff0c;它指示浏览器只能通过安全SSL/TLS通道访问cookie。 未设置HttpOnly标志的Cookie 当cookie设置…

终于找到了!多种类型的电子期刊模板在这里!

经过我不懈的努力和搜寻&#xff0c;终于找到了一个提供多种类型电子期刊模板的网站。这个网站拥有丰富多样的模板&#xff0c;可以满足各种不同的需求&#xff0c;无论是学术研究、商业报告还是个人兴趣爱好&#xff0c;都能在这里找到心仪的模板。 一、网站介绍 这个网站叫做…

软件行业与就业(导师主讲)

在企业软件应用的整体架构体系中&#xff0c;有一部分被称为中间件&#xff0c;那么什么叫中间件&#xff1f; 中间件&#xff08;Middleware&#xff09;是指位于操作系统和应用程序之间的一层软件层&#xff0c;它提供了一组工具和服务&#xff0c;用于简化和增强企业软件应用…

关于hive的时间戳

unix_timestamp&#xff08;&#xff09;和 from_unixtime&#xff08;&#xff09;的2个都是格林威治时间 北京时间 格林威治时间8 from_unixtme 是可以进行自动时区转换的 (4.0新特性) 4.0之前可以通过from_utc_timestamp进行查询 如果时间戳为小数&#xff0c;是秒&#…

Java基本数据类型

Java基本数据类型 1 数值型 整型数据类型 数据类型内存空间&#xff08;8位1字节&#xff09;取值范围byte(字节型&#xff09;8位&#xff08;1字节&#xff09;-128~127 &#xff08;2的8次方&#xff09;short(短整型&#xff09;16位&#xff08;2字节&#xff09;-32768~3…

利用MobaXterm连接服务器的全程配置

一、服务器上的操作 1.1 保证openssh的安装 openssh安装命令如下 apt-get update apt install openssh-server1.2 保证SSH服务没有在相应端口上侦听连接 1确保本地 SSH 服务正在运行 可以尝试使用以下命令检查 SSH 服务的状态&#xff08;在大多数 Linux 系统上&#xff0…

若依微服务部署,裸服务部署、docker部署、k8s部署

目录 前言windows 部署若依-微服务版本浏览器验证docker部署若依-微服务版本浏览器验证k8s部署若依-微服务版本浏览器验证总结 前言 环境&#xff1a;centos7、Win10 若依是一个合适新手部署练习的开源的微服务项目&#xff0c;本篇讲解Windows部署若依微服务、docker部署若依…

【NLTK系列02】NLTK库应用:使用 NLTK 进行情绪分析

使用 NLTK 进行情绪分析 上篇&#xff1a;【NLTK系列】&#xff1a;nltk库介绍&#xff08;01&#xff09;_无水先生的博客-CSDN博客 一、说明 上篇《NLTK库介绍》介绍了NLTK的基本用法&#xff0c;本篇介绍NLTK的一个具体应用&#xff1a;“使用 NLTK 进行情绪分析”&#xf…

基于SpringBoot的桂林旅游景点导游平台

目录 前言 一、技术栈 二、系统功能介绍 用户信息管理 景点类型管理 景点信息管理 线路推荐管理 用户注册 线路推荐 论坛交流 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实…

阶段六-Day02-Maven

一、学习Maven 使用Maven创建Web项目&#xff0c;并部署到服务器。 二、Maven介绍及加载原理 1. 介绍 Maven是使用Java语言编写的基于项目对象模型&#xff08;POM&#xff09;的项目管理工具。开发者可以通过一小段描述信息来管理项目的构建、报告和文档。 使用Maven可以…

记录在搭建Jenkins时,所遇到的坑,以及解决方案

项目场景&#xff1a; 记录在搭建Jenkins时,所遇到的坑,以及解决方案.问题描述1 在使用Jenkins构建时,报错如下&#xff1a; cp: cannot stat /project/xx/xxxx/dist/: No such file or directory Build step Execute shell marked build as failure Finished: FAILURE解决方…

【网络豆送书第五期】Kali Linux高级渗透测试

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆云计算学堂 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 本期好书推荐&#xff1a;《Kali Linux高级渗透测试…

MySQ 学习笔记

1.MySQL(老版)基础 开启MySQL服务: net start mysql mysql为安装时的名称 关闭MySQL服务: net stop mysql 注: 需管理员模式下运行Dos命令 . 打开服务窗口命令 services.msc 登录MySQL服务: mysql [-h localhost -P 3306] -u root -p****** Navicat常用快捷键 键动作CTRLG设…

Sentinel入门

文章目录 初始Sentinel雪崩问题服务保护技术对比认识Sentinel微服务整合Sentinel 限流规则快速入门流控模式关联模式链路模式 流控效果warm up排队等待 热点参数限流全局参数限流热点参数限流 隔离和降级FeignClient整合Sentinel线程隔离熔断降级慢调用异常比例、异常数 授权规…

dockerfile lnmp 搭建wordpress、docker-compose搭建wordpress

-----------------安装 Docker--------------------------- 目前 Docker 只能支持 64 位系统。systemctl stop firewalld.service setenforce 0#安装依赖包 yum install -y yum-utils device-mapper-persistent-data lvm2 --------------------------------------------------…

软考高级架构师下篇-18

目录 1. 引言2. 传统数据处理系统的问题1.传统数据库的数据过载问题2.大数据的特点3.大数据利用过程4.大数据处理系统架构分析3.典型的大数据架构1. Lambda架构2.Kappa架构3. Lambda架构与Kappa架构的对比4.大数据架构的实践1.大规模视频网络2.广告平台3.公司智能决策大数据系统…

网络安全(骇客)—技术学习

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟入…