使用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.hqwc.cn/news/130188.html

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

相关文章

Android修改默认system/bin/下可执行程序拥有者和权限,使用实例,只有root和系统app权限才能执行某个命令。

一、执行ls -l /system/bin/ 查看一下用户和权限。 二、这些权限在哪里修改呢&#xff1f; 默认编译system/bin/可执行程序赋予权限的地方system\core\libcutils\fs_config.cpp 文件里面的android_files 三、使用实例&#xff0c;只有root和系统app权限才能执行某个命令&#x…

【C++设计模式之备忘录模式:行为型】分析及示例

简介 备忘录模式&#xff08;Memento Pattern&#xff09;是一种行为型设计模式&#xff0c;它用于保存和恢复对象的状态。备忘录模式通过将对象的状态封装成一个备忘录&#xff08;Memento&#xff09;&#xff0c;并将备忘录保存在一个管理者&#xff08;Caretaker&#xff…

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

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

【初识Jmeter】【接口自动化】

jmeter的使用笔记1 Jmeter介绍与下载安装介绍安装配置配置与扩展组件 jmeter的使用基本功能元素登陆请求与提取cookie其他请求接口关联Cookie-响应成功聚合报告查看 Jmeter介绍与下载安装 介绍 jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;体积小&…

【MySQL】表的基础增删改查

前面我们已经知道怎么来创建表了&#xff0c;接下来就来对创建的表进行一些基本操作。 这里先将上次创建的表删除掉&#xff1a; mysql> use test; Database changedmysql> show tables; ---------------- | Tables_in_test | ---------------- | student | -----…

[MongoDB]-权限验证管理

[MongoDB]-权限验证管理 senge | 2023年9月 背景说明&#xff1a;现有两套MongoDB副本集群给开发人员使用时未开启认证。 产生影响&#xff1a;用户若输入账号以及密码则会进行校验&#xff0c;但用户可以在不输入用户名和密码的情况下也可直接登录。 倘若黑客借此进行攻击勒索…

自动求导,计算图示意图及pytorch实现

pytorch实现 x1 torch.tensor(3.0, requires_gradTrue) y1 torch.tensor(2.0, requires_gradTrue) a x1 ** 2 b 3 * a c b * y1 c.backward() print(x1.grad) print(y1.grad) print(x1.grad 6 * x1 * y1) print(y1.grad 3 * (x1 ** 2))输出为&#xff1a; tensor(36.) …

C语言——文件操作_学习笔记

一、引言——为什么使用文件 如果没有文件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运行程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进行持久化的…

【Spring Boot】日志文件

日志文件 一. 日志文件有什么用二. 日志怎么用三. ⾃定义⽇志打印1. 在程序中得到⽇志对象2. 使⽤⽇志对象打印⽇志3. ⽇志格式说明 四. 日志级别1. ⽇志级别有什么⽤2. ⽇志级别的分类与使⽤ 五. 日志持久化六. 更简单的⽇志输出—lombok1. 添加 lombok 依赖2. 输出⽇志3. lom…

VueRouter与expres/koa中间件的关联

ueRouter: runQueue 路由守卫都是有三个参数to,from,next。其中next就是下方的fn执行时候传入的第二个参数(回调函数)&#xff0c;只有该回调执行后才会挨个遍历queue内的守卫。 中间件的作用 隔离基础设施与业务逻辑之间的细节。详细的内容位于《深入浅出Node.js》P210 另外一…

深度学习-卷积神经网络-AlexNET

文章目录 前言1.不同卷积神经网络模型的精度2.不同神经网络概述3.卷积神经网络-单通道4.卷积神经网络-多通道5.池化层6.全连接层7.网络架构8.Relu激活函数 1.LeNet-52.AlexNet1.架构2.局部响应归一化&#xff08;VGG中取消了&#xff09;3.重叠/不重叠池化4.过拟合-数据增强5.过…

Scala第二十章节

Scala第二十章节 scala总目录 文档资料下载 章节目标 理解Akka并发编程框架简介掌握Akka入门案例掌握Akka定时任务代码实现掌握两个进程间通信的案例掌握简易版spark通信框架案例 1. Akka并发编程框架简介 1.1 Akka概述 Akka是一个用于构建高并发、分布式和可扩展的基于事…