stm32mp157 盘古开发板 Linux内核版本4.19
目录
1、背景
在我的博客《Linux驱动分析——input输入子系统》中已经大概了解了按键输入的来龙去脉,博客链接:https://blog.youkuaiyun.com/fang_yang_wa/article/details/113254396
2、测试情况:
应用层测试用代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <string.h>
#define KEY "/dev/input/event0"
int main(void)
{
int fd = -1, ret = -1;
struct input_event ev;
// 第1步:打开设备文件
fd = open(KEY, O_RDONLY);
if (fd < 0)
{
perror("open");
return -1;
}
while (1)
{
// 第2步:读取一个event事件包
memset(&ev, 0, sizeof(struct input_event));
ret = read(fd, &ev, sizeof(struct input_event));
if (ret != sizeof(struct input_event))
{
perror("read");
close(fd);
return -1;
}
// 第3步:解析event包,才知道发生了什么样的输入事件
printf("-------------------------\n");
printf("time.tv_sec: %llu\n", ev.time.tv_sec);
printf("type: %hd\n", ev.type);
printf("code: %hd\n", ev.code);
printf("value: %d\n", ev.value);
printf("\n");
}
// 第4步:关闭设备
close(fd);
return 0;
}
如上图设备树配置,按键按下返回0,按键抬起返回1(按键抬起状态为高电平)
如上图设备树配置,按键按下返回1,按键抬起返回0(按键抬起状态为高电平)由此可见这个设备树中gpios引脚配置的高低电平会影响input返回的value值
关于按键的设备树配置可通过博客了解:https://www.it610.com/article/1295820080185155584.htm 该链接中关键内容截图如下:
博客内容:
1.概述
Gpio-keys 是基于input子系统实现的一个通用按键驱动,该驱动也符合linux驱动实现模型,即driver和device分离模型.一般按键驱动,都是基于gpio-keys进行开发的.
2. gpio-keys 代码分析(基于 linux 4.14.40)
(1)整体来说分为以下四步
static int gpio_keys_probe(struct platform_device *pdev) { ........... ........... /*第一,从设备树获取button,即gpio相关控制方式和属性*/ pdata = gpio_keys_get_devtree_pdata(dev); ........... ........... /*第二,分配一个input 设备*/ input = devm_input_allocate_device(dev); ........... ........... /*第三,注册gpio相关信息和分配资源*/ for (i = 0; i < pdata->nbuttons; i++) { error = gpio_keys_setup_key(pdev, input, ddata, button, i, child); } ........... ........... /*第四,注册input 设备到kernel*/ error = input_register_device(input); ........... ........... return 0; }
(2)从注册一个input设备来看,二,四步是一个通用的步骤,第三步和第一步是关键,先看第三步.
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) { ............. ............. /*第一,请求gpio并获取irq 资源*/ error = devm_gpio_request_one(dev, button->gpio, flags, desc); irq = gpiod_to_irq(bdata->gpiod); ............. ............. /*第二,注册一个delayed work任务,这个任务的目的,当中断发生时,需要向 通过input子系统发消息,这个过程有可能会sleep或者schedule,所以必须通一个 delay work去完成这件事. */ INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func); isr = gpio_keys_gpio_isr; irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;} ............. ............. /*第三,注册中断*/ error = devm_request_any_context_irq(dev, bdata->irq, isr, irqflags, desc, bdata); return 0; }
(3)下面来看中断和delay work做了什么事
static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; ............... ............... /*中断中,唯一做的事,就是把delay work加入到了system_wq队列中去.还做了一个延迟 这个延迟目的是为了去抖动.*/ mod_delayed_work(system_wq, &bdata->work, msecs_to_jiffies(bdata->software_debounce)); return IRQ_HANDLED; } static void gpio_keys_gpio_work_func(struct work_struct *work) { /*调用上报消息,接着向下看*/ ........... gpio_keys_gpio_report_event(bdata); ........... } static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { ............ /*第一步,获取gpio 状态.*/ state = gpiod_get_value_cansleep(bdata->gpiod); ............ /*发送消息到input*/ input_event(input, type, *bdata->code, state); input_sync(input); ............ }