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);
}

Linux Driver : gpio-keys的更多相关文章

  1. linux driver ------ GPIO的驱动编写和调用

    判断哪些文件被编译进内核: 1.通过 make menuconfig 查看 2.比如查看gpio类型的文件,输入 ls drivers/gpio/*.o,有生成.o文件表示被编译进内核 在编写驱动程序 ...

  2. linux 标准 GPIO 操作

    Linux 提供了GPIO 操作的 API,具体初始化及注册函数在 driver/gpio/lib_gpio.c 中实现.   #include    int gpio_request(unsigne ...

  3. Linux driver 板级文件跟踪一般方法

    /*********************************************************************************** * Linux driver ...

  4. linux下GPIO的用户层操作(sysfs)

    linux的GPIO通过sysfs为用户提供服务,下面是linux kernel里的说明文档,学习一下. GPIO Sysfs Interface for Userspace ============ ...

  5. Android(Linux)控制GPIO方法二

    前文<Android(Linux)控制GPIO的方法及实时性分析>主要使用Linux shell命令控制GPIO,该方法可在调试过程中快速确定GPIO硬件是否有问题,即对应的GPIO是否受 ...

  6. 调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之二

    /** ****************************************************************************** * @author    暴走的小 ...

  7. 调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之一

    /** ****************************************************************************** * @author    暴走的小 ...

  8. 调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之三

    /** ****************************************************************************** * @author    暴走的小 ...

  9. OK335xS pwm buzzer Linux driver hacking

    /**************************************************************************** * OK335xS pwm buzzer L ...

  10. linux driver开发

    1 开发linux driver时的调试思路 基本上是打印调试,原因很简单,方便.或者使用工具挂住cpu.

随机推荐

  1. 如何在M1 MACBOOK上准备好ORB-SLAM2的环境

    1. 环境 M1 Macbook Air Parallels Desktop 17 关于虚拟机,在之前尝试了UTM,但是性能欠佳,卡顿情况比较多,而且未能解决联网问题,最终只能含泪放弃 Paralle ...

  2. SAP Adobe Form 教程一 简单示例

    马上需要用到adobe form,这里搬运一篇教程学习下. 英文原文:SAP Adobe Interactive Form Tutorial. Part I. First Adobe Form 本文链 ...

  3. 大模型高效微调详解-从Adpter、PrefixTuning到LoRA

    一.背景 目前NLP主流范式是在大量通用数据上进行预训练语言模型训练,然后再针对特定下游任务进行微调,达到领域适应(迁移学习)的目的. 指令微调是预训练语言模型微调的主流范式 其目的是尽量让下游任务的 ...

  4. ios系统的css兼容问题处理和iOS上网页滑动不流畅问题

    1.H5网页touch滑动的时候在苹果手机上出现不流畅的问题 -webkit-overflow-scrolling 用来控制元素在移动设备上是否使用滚动回弹效果. 解决办法:给所有网页添加如下样式 b ...

  5. leaflet 加载geojson叠加显示

    geojson需要先制作shp,然后导入下面网站生成geojson. https://mapshaper.org/ geojson,最好放后台,前台通过异步请求去加载json,然后显示. getGeo ...

  6. Chrome 浏览器插件 Manifest.json V3 中权限(Permissions)字段解析

    一.权限(Permissions) 再使用拓展程序的 API 时,大多数的时候,需要在 manifest.json 文件中声明 permissions 字段. 一.权限类型 在 V3 版本中可以声明以 ...

  7. 基于FPGA的贪吃蛇游戏 之代码解析

    基于FPGA的贪吃蛇游戏 之代码解析 1. 代码结构 代码结构包含7格.v文件. 下面依次解析. 2. 代码解析 (1) seg_display.v 数码管的译码模块是最熟悉,最简单的模块了.这里是共 ...

  8. PageOffice 6 保存数据区域数据同时保存文档

    在实际应用中,例如在线签订合同的时候,合同的签订日期,合同号等等这些信息既要保存到数据库,合同签订后又要将整个合同文件保存起来.这时候就需要用到PageOffice的保存数据区域数据的同时保存整个文件 ...

  9. 用pageOffice控件实现 office 文档在线编辑Word插入另外word文档的功能

    OA办公中,业务需要编辑word文档,需要插入另外word文档的功能. 怎么实现编辑word文档插入另外word文档呢? 2 实现方法 通过pageOffice实现简单的在线打开编辑word时, 通过 ...

  10. MATLAB txt文件抽稀并分为多个txt文件

    chouxi.m a = load("file.txt"); % len = length(a); interval = 9;%间隔+1 b = a(1:interval:end, ...