网站开发前端工程师,如何进行网站推广活动过程,辽宁学校网站建设,新闻做的差的网站按键、鼠标、键盘、触摸屏等都属于输入(input)设备#xff0c;Linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。输入设备本质上还是字符设备#xff0c;只是在此基础上套上了input框架#xff0c;用户只需要负责上报输入事件#xff0c;比如按键值、坐标等…按键、鼠标、键盘、触摸屏等都属于输入(input)设备Linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。输入设备本质上还是字符设备只是在此基础上套上了input框架用户只需要负责上报输入事件比如按键值、坐标等信息input核心层负责处理这些事件。本章就来学习一下Linux内核中的input子系统。
input子系统
input子系统简介
input子系统就是管理输入的子系统和pinctrl、gpio子系统一样都是Linux内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备不同的输入设备所代表的含义不同按键和键盘就是代表按键信息鼠标和触摸屏代表坐标信息因此在应用层的处理就不同对于驱动编写者而言不需要去关心应用层的事情只需要按照要求上报这些输入事件即可。为此input子系统分为input驱动层、input核心层、input事件处理层最终给用户空间提供可访问的设备节点input子系统框 架如下图所示 上图中左边就是最底层的具体设备比如按键、USB键盘 /鼠标等中间部分属于Linux内核空间分为驱动层、核心层和事件层最右边的就是用户空间所有的输入设备以文件的形式供用户应用程序使用。可以看出input子系统用到了前面讲解的驱动分层模型编写驱动程序的时候只需要关注中间的驱动层、核心层和事件层这三个层的分工如下
驱动层输入设备的具体驱动程序比如按键驱动程序向内核层报告输入内容。核心层承上启下为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行 处理。事件层主要和用户空间进行交互。
input驱动编写流程
input核心层会向Linux内核注册一个字符设备找到drivers/input/input.c这个文件input.c就是input输入子系统的核心层此文件里面有如下所示代码 第2498行注册一个input类这样系统启动以后就会在/sys/class目录下有一个input子 目录如下图所示 第2508-2509行注册一个字符设备主设备号为INPUT_MAJORINPUT_MAJOR定义 在include/uapi/linux/major.h文件中定义如下
#define INPUT_MAJOR 13因此input子系统的所有设备主设备号都为13在使用input子系统处理输入设备 的时候就不需要去注册字符设备了只需要向系统注册一个input_device即可。
注册input_dev
在使用input子系统的时候只需要注册一个input设备即可input_dev结构体表示input设备此结构体定义在include/linux/input.h文件中定义如下 (有省略) 第139行evbit表示输入事件类型可选的事件类型定义在include/uapi/linux/input.h文件 中事件类型如下 比如本章要使用到按键那么就需要注册EV_KEY事件如果要使用连按功能的话还需要注册EV_REP事件。
继续回到示例代码38.1.2.2中第139行-147行的evbit、keybit、relbit等等都是存放不同事件对应的值。比如本章要使用按键事件因此要用到keybitkeybit就是按键事件使用的位图Linux内核定义了很多按键值这些按键值定义在include/uapi/linux/input-event-codes.h文件中按键值如下 可以将开发板上的按键值设置为示例代码38.1.2.4中的任意一个比如本章实验会将STM32MP1开发板上的KEY0按键值设置为KEY_0。
在编写input设备驱动的时候需要先申请一个input_dev结构体变量使用input_allocate_device函数来申请一个input_dev此函数原型如下所示
struct input_dev *input_allocate_device(void)函数参数和返回值含义如下
参数 无。返回值申请到的input_dev。
如果要注销input设备的话需要使用input_free_device函数来释放掉前面申请到的input_dev。input_free_device函数原型如下
void input_free_device(struct input_dev *dev) 函数参数和返回值含义如下
dev需要释放的input_dev。返回值无。
申请好一个input_dev以后就需要初始化这个input_dev需要初始化的内容主要为事件类型(evbit)和事件值(keybit)这两种。input_dev初始化完成以后就需要向Linux内核注册input_dev了需要用到input_register_device函数此函数原型如下
int input_register_device(struct input_dev *dev) 函数参数和返回值含义如下
dev要注册的input_dev 。返回值0input_dev注册成功负值input_dev注册失败。
同样的注销input驱动的时候也需要使用input_unregister_device函数来注销掉前面注册的input_devinput_unregister_device函数原型如下
void input_unregister_device(struct input_dev *dev) 函数参数和返回值含义如下
dev要注销的input_dev 。返回值无。
综上所述input_dev注册过程如下
使用input_allocate_device函数申请一个input_dev。初始化input_dev的事件类型以及事件值。使用input_register_device函数向Linux系统注册前面初始化好的input_dev。卸载input驱动的时候需要先使用input_unregister_device函数注销掉注册的input_dev然后使用input_free_device函数释放掉前面申请的input_dev。
input_dev注册过程示例代码如下所示
示例代码38.1.2.5 input_dev注册流程
1 struct input_dev *inputdev; /* input结构体变量 */
2
3 /* 驱动入口函数 */
4 static int __init xxx_init(void)
5 {
6 ......
7 inputdev input_allocate_device(); /* 申请input_dev */
8 inputdev-name test_inputdev; /* 设置input_dev名字 */
9
10 /*********第一种设置事件和事件值的方法***********/
11 __set_bit(EV_KEY, inputdev-evbit); /* 设置产生按键事件 */
12 __set_bit(EV_REP, inputdev-evbit); /* 重复事件 */
13 __set_bit(KEY_0, inputdev-keybit); /*设置产生哪些按键值 */
14 /************************************************/
15
16 /*********第二种设置事件和事件值的方法***********/
17 keyinputdev.inputdev-evbit[0] BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
18 keyinputdev.inputdev-keybit[BIT_WORD(KEY_0)] | BIT_MASK(KEY_0);
19 /************************************************/
20
21 /*********第三种设置事件和事件值的方法***********/
22 keyinputdev.inputdev-evbit[0] BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
23 input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
24 /************************************************/
25
26 /* 注册input_dev */
27 input_register_device(inputdev);
28 ......
29 return 0;
30 }
31
32 /* 驱动出口函数 */
33 static void __exit xxx_exit(void)
34 {
35 input_unregister_device(inputdev); /* 注销input_dev */
36 input_free_device(inputdev); /* 删除input_dev */
37 }第1行定义一个input_dev结构体指针变量。
第4-30行驱动入口函数在此函数中完成input_dev的申请、设置、注册等工作。第7行调用input_allocate_device函数申请一个input_dev。第10-23行都是设置input设备事件和按键值这里用了三种方法来设置事件和按键值。第27行调用input_register_device函数向Linux内核注册inputdev。
第33-37行驱动出口函数第35行调用input_unregister_device函数注销前面注册的input_dev第36行调用input_free_device函数删除前面申请的input_dev。
上报输入事件
当向Linux内核注册好input_dev以后需要获取到具体的输入值或者说是输入事件然后将输入事件上报给Linux内核。比如按键需要在按键中断处理函数或者消抖定时器中断函数中将按键值上报给Linux内核这样Linux内核才能获取到正确的输入值。不同的事件其上报事件的API函数不同依次来看一下一些常用的事件上报API函数。
首先是input_event函数此函数用于上报指定的事件以及对应的值函数原型如下
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)函数参数和返回值含义如下
dev需要上报的input_dev。type: 上报的事件类型比如EV_KEY。code事件码也就是注册的按键值比如KEY_0、KEY_1等等。value事件值比如1表示按键按下0表示按键松开。返回值无。
input_event函数可以上报所有的事件类型和事件值Linux内核也提供了其他的针对具体事件的上报函数这些函数其实都用到了input_event函数。比如上报按键所使用的input_report_key函数此函数内容如下 从示例代码38.1.2.6可以看出input_report_key函数的本质就是input_event函数如果要上报按键事件的话还是建议使用input_report_key函数。
同样的还有一些其他的事件上报函数这些函数如下所示
void input_report_rel(struct input_dev *dev, unsigned int code, int value)
void input_report_abs(struct input_dev *dev, unsigned int code, int value)
void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
void input_report_switch(struct input_dev *dev, unsigned int code, int value)
void input_mt_sync(struct input_dev *dev)当上报事件以后还需要使用input_sync函数来告诉Linux内核input子系统上报结束input_sync函数本质是上报一个同步事件此函数原型如下所示
void input_sync(struct input_dev *dev) 函数参数和返回值含义如下
dev需要上报同步事件的input_dev。返回值无。
综上所述按键的上报事件的参考代码如下所示
示例代码38.1.2.7 事件上报参考代码
1 /* 用于按键消抖的定时器服务函数 */
2 void timer_function(unsigned long arg)
3 {
4 unsigned char value;
5
6 value gpio_get_value(keydesc-gpio); /* 读取IO值 */
7 if(value 0){ /* 按下按键 */
8 /* 上报按键值 */
9 input_report_key(inputdev, KEY_0, 1); /* 最后一个参数1按下 */
10 input_sync(inputdev); /* 同步事件 */
11 } else { /* 按键松开 */
12 input_report_key(inputdev, KEY_0, 0); /* 最后一个参数0松开 */
13 input_sync(inputdev); /* 同步事件 */
14 }
15 }第6行获取按键值判断按键是否按下。
第9-10行如果按键值为0那么表示按键被按下了如果按键按下的话就要使用input_report_key函数向Linux系统上报按键值比如向Linux系统通知KEY_0这个按键按下了。
第12-13行如果按键值为1的话就表示按键没有按下是松开的。向Linux系统通知KEY_0这个按键没有按下或松开了。
input_event结构体
Linux内核使用input_event这个结构体来表示 所有的输入事件input_envent结构体定义在include/uapi/linux/input.h文件中结构体内容如下 依次来看一下input_event结构体中的各个成员变量
time时间也就是此事件发生的时间为timeval结构体类型timeval结构体定义如下 从示例代码38.1.3.2可以看出tv_sec和tv_usec这两个成员变量都为long类型也就是32位这个一定要记住后面分析event事件上报数据的时候要用到。type事件类型比如EV_KEY表示此次事件为按键事件此成员变量为16位。code事件码比如在EV_KEY事件中code就表示具体的按键码如KEY_0、KEY_1等等这些按键。此成员变量为16位。value值比如EV_KEY事件中value就是按键值表示按键有没有被按下如果为1说明按键按下如果为0的话说明按键没有被按下或者按键松开了。
input_envent这个结构体非常重要因为所有的输入设备最终都是按照input_event结构体呈现给用户的用户应用程序可以通过input_event来获取到具体的输入事件或相关的值比如按键值等。关于input子系统就讲解到这里接下来就以开发板上的KEY0按键为例讲解一下如何编写input驱动。
硬件原理图分析
就是之前的按键原理图。
实验程序编写
修改设备树文件
创建按键引脚的pinctrl子节点
首先在stm32mp15-pinctrl.dtsi文件中创建按键对应的pinctrl子节点内容如下
示例代码38.3.1.1 key_pins_a节点
1 key_pins_a: key_pins-0 {
2 pins1 {
3 pinmux STM32_PINMUX(G, 3, GPIO), /* KEY0 */
4 STM32_PINMUX(H, 7, GPIO); /* KEY1 */
5 bias-pull-up;
6 slew-rate 0;
7 };
8
9 pins2 {
10 pinmux STM32_PINMUX(A, 0, GPIO); /* WK_UP */
11 bias-pull-down;
12 slew-rate 0;
13 };
14 };这里将STM32MP1开发板上的三个按键KEY0、KEY1和WK_UP都设置了。由于KEY0和KEY1这两个按键是低电平有效(按下以后为低电平)WK_UP是高电平有效(按下以后为高电平)。所以这里分开初始化其中第2-7行初始化KEY0和KEY1使用的PG3和PH7这两个引脚第9-13行初始化WK_UP使用的PA0引脚。
创建按键引脚的设备节点
接下就是创建按键设备节点可以直接在之前的key节点上修改即可修改完成以后如下所示
示例代码38.3.1.2 key节点
1 key {
2 compatible alientek,key;
3 status okay;
4 pinctrl-names default;
5 pinctrl-0 key_pins_a;
6 key-gpio gpiog 3 GPIO_ACTIVE_LOW;
7 interrupt-parent gpiog;
8 interrupts 3 IRQ_TYPE_EDGE_BOTH;
9 };按键input驱动程序编写
按键结构体key_dev中需要input_dev结构体指针*idev需要timer_list结构体的timer来辅助按键消抖需要int类型的gpio_key表示按键的GPIO编号以及int类型的irq_key表示按键对应的中断号。
编写按键的中断服务函数static irqreturn_t的函数key_interrupt里面需要先disable_irq_nosync禁止中断然后通过mod_timer激活定时器来消抖。
编写key_gpio_init来初始化按键里面就是常规操作了先of_get_named_gpio从设备树获取按键的GPIO然后gpio_request申请GPIO设置输入gpio_direction_input通过irq_of_parse_and_map来获取对应的中断号之后irq_get_trigger_type获取中断触发类型并request_irq申请中断。
编写定时器的中断服务函数完成按键消抖key_timer_function中gpio_get_value获取当前按键值后input_report_key上报案件时间并通过input_sync来同步最后enable_irq开启中断。
编写platform驱动的probe函数atk_key_probe完成GPIO的初始化调用key_gpio_init初始化然后timer_setup初始化定时器input子系统需要input_allocate_device申请设备然后设置key.idev-name名字通过__set_bit来设置事件以及触发的按键值(也可以通过BIT_MASK设置效果一样)最后input_register_device注册input设备。
编写platform驱动的remove函数atk_key_remove需要free_irq释放中断号gpio_free释放GPIOdel_timer_sync删除timer最后input_unregister_device释放input设备。
platform还需要编写一个of_device_id结构体的列表key_of_match[]设置.compatible属性保持与设备树中节点的compatible一致。
然后设置platform_driver结构体的atk_key_driver这个platform驱动设置.driver中的.name(设备树中名字)以及.of_match_table然后设置.probe以及.remove函数。
最后module_platform_driver(atk_key_driver)即可。
编写测试APP
传入的argc就2个open(argv[1], O_RDWR)之后在for死循环中read读取设备通过传入的值判断具体的按键情况最后写一个close就可以了。
运行测试
编译驱动程序
Makefile还是老样子改一下obj-m为keyinput.o然后“make”就可以了。
编译测试APP
可以通过如下命令编译 arm-none-linux-gnueabihf-gcc keyinputApp.c -o keyinputApp
运行测试
将上一小节编译出来keyinput.ko和keyinputApp这两个文件拷贝到rootfs/lib/modules/5.4.31目录中重启开发板进入到目录lib/modules/5.4.31中。在加载 keyinput.ko驱动模块之前先看一下/dev/input目录下都有哪些文件结果如下图所示 可以从上图看出当前在/dev目录下不存在input文件夹因为此时系统中并没有加载过input输入类设备所以这个文件夹不存在。接下来输入如下命令加载keyinput.ko这个驱动模块 depmod //第一次加载驱动的时候需要运行此命令 modprobe keyinput.ko //加载设备模块
当驱动模块加载成功以后再来看一下/dev/input目录下有哪些文件结果如下图所示 从上图可以看出在/dev/input目录下生成了一个event0文件这其实就是注册的驱动所对应的设备文件。keyinputApp就是通过读取/dev/input/event0这个文件来获取输入事 件信息的输入如下测试命令 ./keyinputApp /dev/input/event0
之后按下KEY0按键即可被检测到。
另外也可以不用keyinputApp来测试驱动可以直接使用hexdump命令来查看/dev/input/event0文件内容输入如下命令 hexdump /dev/input/event0
可以得到如下结果
上图就是input_event类型的原始事件数据值采用十六进制表示这些原始数据的含义如下 type为事件类型查看示例代码38.1.2.3可知EV_KEY事件值为1EV_SYN事件值为0。因此第1行表示EV_KEY事件第2行表示EV_SYN事件。code为事件编码也就是按键号查看示例代码38.1.2.4可知KEY_0这个按键编号为11对应的十六进制为0xb因此第1行表示KEY_0这个按键事件最后的value就是按键值为1表示按下为0的话表示松开。
综上所述示例代码38.4.2.1中的原始事件值含义如下
第1行按键(KEY_0)按下事件。第2行EV_SYN同步事件因为每次上报按键事件以后都要同步的上报一个EV_SYN事件。第3行按键(KEY_0)松开事件。第4行EV_SYN同步事件和第2行一样。
Linux自带按键驱动程序使用
自带按键驱动程序源码解析
Linux内核也自带了KEY驱动如果要使用内核自带的KEY驱动的话需要配置Linux内核不过Linux内核一般默认已经使能了KEY驱动但是还是要检查一下。按照如下路径找到相应的配置选项 - Device Drivers - Input device support - Generic input layer (needed for keyboard, mouse, ...) (INPUT [y]) - Keyboards (INPUT_KEYBOARD [y]) -GPIO Buttons
选中“GPIO Buttons”将其编译进入Linux内核中如下图所示 选中以后就会在.config文件中出现“CONFIG_KEYBOARD_GPIOy”这一行Linux内核就会根据这一行来将KEY驱动文件编译进Linux内核。Linux内核自带的KEY驱动文件为drivers/input/keyboard/gpio_keys.cgpio_keys.c采用了platform驱动框架在KEY驱动上使用了input子系统实现。在gpio_keys.c文件中找到如下所示内容 从示例代码38.5.1.1可以看出这就是一个标准的platform驱动框架如果要使用设备树来描述KEY设备信息的话设备节点的compatible属性值要设置为“gpio-keys”。当设备和驱动匹配以后gpio_keys_probe函数就会执行gpio_keys_probe函数内容如下(为了篇幅有缩减)
示例代码 38.5.1.2 gpio_keys_probe 函数代码段
764 static int gpio_keys_probe(struct platform_device *pdev)
765 {
766 struct device *dev pdev-dev;
767 const struct gpio_keys_platform_data *pdata dev_get_platdata(dev);
768 struct fwnode_handle *child NULL;
769 struct gpio_keys_drvdata *ddata;
770 struct input_dev *input;
771 int i, error;
772 int wakeup 0;
773
774 if (!pdata) {
775 pdata gpio_keys_get_devtree_pdata(dev);
776 if (IS_ERR(pdata))
777 return PTR_ERR(pdata);
778 }
......
793 input devm_input_allocate_device(dev);
794 if (!input) {
795 dev_err(dev, failed to allocate input device\n);
796 return -ENOMEM;
797 }
.....
806 input-name pdata-name ? : pdev-name;
807 input-phys gpio-keys/input0;
808 input-dev.parent dev;
809 input-open gpio_keys_open;
810 input-close gpio_keys_close;
811
812 input-id.bustype BUS_HOST;
813 input-id.vendor 0x0001;
814 input-id.product 0x0001;
815 input-id.version 0x0100;
.....
821 /* Enable auto repeat feature of Linux input subsystem */
822 if (pdata-rep)
823 __set_bit(EV_REP, input-evbit);
824
825 for (i 0; i pdata-nbuttons; i) {
826 const struct gpio_keys_button *button pdata-buttons[i];
827
828 if (!dev_get_platdata(dev)) {
829 child device_get_next_child_node(dev, child);
830 if (!child) {
831 dev_err(dev,
832 missing child device node for entry %d\n,
833 i);
834 return -EINVAL;
835 }
836 }
837
838 error gpio_keys_setup_key(pdev, input, ddata,
839 button, i, child);
840 if (error) {
841 fwnode_handle_put(child);
842 return error;
843 }
844
845 if (button-wakeup)
846 wakeup 1;
847 }
848
849 fwnode_handle_put(child);
850
851 error input_register_device(input);
852 if (error) {
853 dev_err(dev, Unable to register input device, error: %d\n,
854 error);
855 return error;
856 }
857
858 device_init_wakeup(dev, wakeup);
859
860 return 0;
861 }第775行调用gpio_keys_get_devtree_pdata函数从设备树中获取到KEY相关的设备节点信息。
第793行使用devm_input_allocate_device函数申请input_dev。
第806-815初始化input_dev。
第823行设置input_dev事件这里设置了EV_REP事件。
第838行调用gpio_keys_setup_key函数继续设置KEY此函数会设置input_dev的EV_KEY事件以及事件码(也就是KEY模拟哪个按键)。
第851行调用input_register_device函数向Linux系统注册input_dev。
接下来再来看一下gpio_keys_setup_key函数此函数内容如下 第614行调用input_set_capability函数设置EV_KEY事件以及KEY的按键类型也就是KEY作为哪个按键会在设备树里面设置指定的KEY作为哪个按键。
一切都准备就绪以后剩下的就是等待按键按下然后向Linux内核上报事件事件上报是在gpio_keys_irq_isr函数中完成的此函数内容如下 gpio_keys_irq_isr是按键中断处理函数第447行向Linux系统上报EV_KEY事件表示按键按下。第448行使用input_sync函数向系统上报EV_REP同步事件。
综上所述Linux内核自带的gpio_keys.c驱动文件思路和前面编写的keyinput.c驱动文件基本一致。都是申请和初始化input_dev设置事件向Linux内核注册input_dev。最终在按键中断服务函数或者消抖定时器中断服务函数中上报事件和按键值。
自带按键驱动程序的使用
要使用Linux内核自带的按键驱动程序很简单只需要根据Documentation/devicetree/bindings/input/gpio-keys.txt这个文件在设备树中添加指定的设备节点 即可节点要求如下
节点名字为“gpio-keys”。gpio-keys节点的compatible属性值一定要设置为“gpio-keys”。所有的KEY都是gpio-keys的子节点每个子节点可以用如下属性描述自己 gpiosKEY所连接的GPIO信息。interruptsKEY所使用GPIO中断信息不是必须的可以不写。labelKEY名字。linux,codeKEY要模拟的按键也就是示例代码38.1.2.4中的这些按键。 如果按键要支持连按的话要加入autorepeat。
这里将开发板上的三个按键都用起来KEY0、KEY1和WKUP分别模拟为键盘上的L、S和Enter(回车)健。
打开stm32mp157d-atk.dts先添加一个头文件“dt-bindings/input/input.h”此文件就是“linux,code”属性的按键宏定义如下图所示 头文件添加完成以后再根据上面的要求创建对应的设备节点设备节点内容如下所示
示例代码 38.5.2.1 gpio-keys 节点内容
1 gpio-keys {
2 compatible gpio-keys;
3 pinctrl-names default;
4 pinctrl-0 key_pins_a;
5 autorepeat;
6
7 key0 {
8 label GPIO Key L;
9 linux,code KEY_L;
10 gpios gpiog 3 GPIO_ACTIVE_LOW;
11 };
12
13 key1 {
14 label GPIO Key S;
15 linux,code KEY_S;
16 gpios gpioh 7 GPIO_ACTIVE_LOW;
17 };
18
19 wkup {
20 label GPIO Key Enter;
21 linux,code KEY_ENTER;
22 gpios gpioa 0 GPIO_ACTIVE_HIGH;
23 gpio-key,wakeup;
24 };
25 };第5行autorepeat表示按键支持连按。
第7-11行STM32MP1开发板KEY0按键信息名字设置为“GPIO Key L”这里将开发板上的KEY0按键设置为“EKY_L”这个按键也就是‘L’键效果和键盘上的‘L’键一样。
第13-17行KEY1按键设置模拟键盘上的‘S’键。
第19-24行WKUP按键模拟键盘上的‘回车’键注意WKUP按键连接的PA0引脚查看原理图可以知道WKUP按下去以后是高电平因此这里设置高电平有效。
最后一定要检查一下设备树看看这些引脚有没有被用到其他外设上如果有的话要删除掉相关代码
重新编译设备树然后用新编译出来的stm32mp157d-atk.dtb启动Linux系统系统启动以后查看/dev/input目录看看都有哪些文件结果如下图所示 从上图可以看出存在event0这个文件这个文件就是KEY对应的设备文件使用hexdump命令来查看/dev/input/event0文件输入如下命令 hexdump /dev/input/event0
然后按下STM32MP1开发板上的按键终端输出如下图所示内容 如果按下KEY按键以后会在终端上输出上图所示的信息那么就表示Linux内核的按键驱动工作正常。
如果按键没有反应可以检查一下如下3方面
是否使能Linux内核KEY驱动。设备树中gpio-keys节点是否创建成功。在设备树中是否有其他外设也使用了KEY按键对应的GPIO但是并没有删除掉这些外设信息。检查Linux启动log信息看看是否有类似下面这条信息如果有表明GPIO申请失败有其他外设使用此GPIO gpio-keys gpio-keys: failed to get gpio: -16
总结
input子系统可以驱动输入的外设使用起来就是platform驱动加上input自己的驱动内容。
自行添加input驱动
编写驱动前需要先在stm32mp15-pinctrl.dtsi文件中创建对应的pinctrl子节点并在stm32mp157d-atk.dts设备树中添加对应节点。
驱动程序中初始化就是gpio的初始化写法没什么花头最多就是跟本篇笔记中一样加一个中断的初始化。
然后是定时器服务函数这里在gpio_get_value之后获取状态需要input_report_key和input_sync上报按键事件并enable_irq开启中断。
probe函数就是调用GPIO的初始化然后timer_setup初始化定时器关键就是input_allocate_device然后__set_bit设置事件最后input_register_devize注册输入设备。
remove函数中区别就是加上input_unregister_device释放input设备。
最后module_platform_driver加上接好的platform_driver结构体的驱动。
Linux内核自带
如果是Linux内核相关则先打开图形化界面然后去使能比如本次的按键输入使能之后在stm32mp157d-atk.dts这个设备树文件中首先需要添加头文件dt-bindings/input/input.h然后添加对应的GPIO节点之后就可以使用了。