news 2026/6/2 4:57:29

Linux INPUT子系统实验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux INPUT子系统实验

Input子系统概述

按键、鼠标、键盘、触摸屏等都属于输入(input)设备,Linux内核为此专门做了一个叫做input
子系统的框架来处理输入事件。输入设备本质上还是字符设备,只是在此基础上套上了input
架,用户只需要负责上报输入事件,比如按键值、坐标等信息,input核心层负责处理这些事件。
input子系统分为input驱动 层、input核心层、input事件处理层,最终给用户空间提供可访问的设备节点,input子系统框架如图所示:
左边就是最底层的具体设备,比如按键、USB键盘/鼠标等,中间部分属于
Linux内核空间,分为驱动层、核心层和事件层,最右边的就是用户空间,所有的输入设备以文
件的形式供用户应用程序使用。可以看出input子系统用到了我们前面讲解的驱动分层模型,我
们编写驱动程序的时候只需要关注中间的驱动层、核心层和事件层,这三个层的分工如下:
驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
事件层:主要和用户空间进行交互。

Input驱动编写流程

input核心层会向Linux内核注册一个字符设备,找到drivers/input/input.c这个文件,
input.c就是input输入子系统的核心层,此文件里面有如下所示代码:
1767 struct class input_class = { 1768 .name = "input", 1769 .devnode = input_devnode, 1770 }; ...... 2414 static int __init input_init(void) 2415 { 2416 int err; 2417 2418 err = class_register(&input_class); 2419 if (err) { 2420 pr_err("unable to register input_dev class\n"); 2421 return err; 2422 } 2423 2424 err = input_proc_init(); 2425 if (err) 2426 goto fail1; 2427 2428 err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0), 2429 INPUT_MAX_CHAR_DEVICES, "input"); 2430 if (err) { 2431 pr_err("unable to register char major %d", INPUT_MAJOR); 2432 goto fail2; 2433 } 2434 2435 return 0; 2436 2437 fail2: input_proc_exit(); 2438 fail1: class_unregister(&input_class); 2439 return err; 2440 }
2418行,注册一个input类,这样系统启动以后就会在/sys/class目录下有一个input
目录,如图所示:
2428~2429行,注册一个字符设备,主设备号为INPUT_MAJORINPUT_MAJOR定义
include/uapi/linux/major.h文件中,定义如下:
#define INPUT_MAJOR 13
input子系统的所有设备主设备号都为13,我们在使用input子系统处理输入设备
的时候就不需要去注册字符设备了,我们只需要向系统注册一个input_device。

注册input_dev

。。。

上报输入事件

。。。

input_event结构体

实验程序编写

修改设备树

使用key按键节点的设备树

按键input驱动程序编写

keyinput.c

1 #include <linux/types.h> 2 #include <linux/kernel.h> 3 #include <linux/delay.h> 4 #include <linux/ide.h> 5 #include <linux/init.h> 6 #include <linux/module.h> 7 #include <linux/errno.h> 8 #include <linux/gpio.h> 9 #include <linux/cdev.h> 10 #include <linux/device.h> 11 #include <linux/of.h> 12 #include <linux/of_address.h> 13 #include <linux/of_gpio.h> 14 #include <linux/input.h> 15 #include <linux/semaphore.h> 16 #include <linux/timer.h> 17 #include <linux/of_irq.h> 18 #include <linux/irq.h> 19 #include <asm/mach/map.h> 20 #include <asm/uaccess.h> 21 #include <asm/io.h> 32 #define KEYINPUT_CNT 1 /* 设备号个数 */ 33 #define KEYINPUT_NAME "keyinput" /* 名字 */ 34 #define KEY0VALUE 0X01 /* KEY0 按键值 */ 35 #define INVAKEY 0XFF /* 无效的按键值 */ 36 #define KEY_NUM 1 /* 按键数量 */ 37 38 /* 中断 IO 描述结构体 */ 39 struct irq_keydesc { 40 int gpio; /* gpio */ 41 int irqnum; /* 中断号 */ 42 unsigned char value; /* 按键对应的键值 */ 43 char name[10]; /* 名字 */ 44 irqreturn_t (*handler)(int, void *); /* 中断服务函数 */ 45 }; 46 47 /* keyinput 设备结构体 */ 48 struct keyinput_dev{ 49 dev_t devid; /* 设备号 */ 50 struct cdev cdev; /* cdev */ 51 struct class *class; /* 类 */ 52 struct device *device; /* 设备 */ 53 struct device_node *nd; /* 设备节点 */ 54 struct timer_list timer; /* 定义一个定时器 */ 55 struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键描述数组 */ 56 unsigned char curkeynum; /* 当前的按键号 */ 57 struct input_dev *inputdev; /* input 结构体 */ 58 }; 59 60 struct keyinput_dev keyinputdev; /* key input 设备 */ 61 62 /* @description : 中断服务函数,开启定时器,延时 10ms, 63 * 定时器用于按键消抖。 64 * @param - irq : 中断号 65 * @param - dev_id : 设备结构。 66 * @return : 中断执行结果 67 */ 68 static irqreturn_t key0_handler(int irq, void *dev_id) 69 { 70 struct keyinput_dev *dev = (struct keyinput_dev *)dev_id; 71 72 dev->curkeynum = 0; 73 dev->timer.data = (volatile long)dev_id; 74 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); 75 return IRQ_RETVAL(IRQ_HANDLED); 76 } 77 78 /* @description : 定时器服务函数,用于按键消抖,定时器到了以后 79 * 再次读取按键值,如果按键还是处于按下状态就表示按键有效。 80 * @param - arg : 设备结构变量 81 * @return : 无 82 */ 83 void timer_function(unsigned long arg) 84 { 85 unsigned char value; 86 unsigned char num; 87 struct irq_keydesc *keydesc; 88 struct keyinput_dev *dev = (struct keyinput_dev *)arg; 89 90 num = dev->curkeynum; 91 keydesc = &dev->irqkeydesc[num]; 92 value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */ 93 if(value == 0){ /* 按下按键 */ 94 /* 上报按键值 */ 95 //input_event(dev->inputdev, EV_KEY, keydesc->value, 1); 96 input_report_key(dev->inputdev, keydesc->value, 1);/*1,按下*/ 97 input_sync(dev->inputdev); 98 } else { /* 按键松开 */ 99 //input_event(dev->inputdev, EV_KEY, keydesc->value, 0); 100 input_report_key(dev->inputdev, keydesc->value, 0); 101 input_sync(dev->inputdev); 102 } 103 } 104 105 /* 106 * @description : 按键 IO 初始化 107 * @param : 无 108 * @return : 无 109 */ 110 static int keyio_init(void) 111 { 112 unsigned char i = 0; 113 char name[10]; 114 int ret = 0; 115 116 keyinputdev.nd = of_find_node_by_path("/key"); 117 if (keyinputdev.nd== NULL){ 118 printk("key node not find!\r\n"); 119 return -EINVAL; 120 } 121 122 /* 提取 GPIO */ 123 for (i = 0; i < KEY_NUM; i++) { 124 keyinputdev.irqkeydesc[i].gpio = of_get_named_gpio(keyinputdev.nd,"key-gpio", i); 125 if (keyinputdev.irqkeydesc[i].gpio < 0) { 126 printk("can't get key%d\r\n", i); 127 } 128 } 129 130 /* 初始化 key 所使用的 IO,并且设置成中断模式 */ 131 for (i = 0; i < KEY_NUM; i++) { 132 memset(keyinputdev.irqkeydesc[i].name, 0, sizeof(name)); 133 sprintf(keyinputdev.irqkeydesc[i].name, "KEY%d", i); 134 gpio_request(keyinputdev.irqkeydesc[i].gpio, keyinputdev.irqkeydesc[i].name); 135 gpio_direction_input(keyinputdev.irqkeydesc[i].gpio); 136 keyinputdev.irqkeydesc[i].irqnum =irq_of_parse_and_map(keyinputdev.nd, i); 137 } 138 /* 申请中断 */ 139 keyinputdev.irqkeydesc[0].handler = key0_handler; 140 keyinputdev.irqkeydesc[0].value = KEY_0; 141 142 for (i = 0; i < KEY_NUM; i++) { 143 ret = request_irq(keyinputdev.irqkeydesc[i].irqnum, keyinputdev.irqkeydesc[i].handler, 144 IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, keyinputdev.irqkeydesc[i].name, &keyinputdev); 145 if(ret < 0){ 146 printk("irq %d request failed!\r\n",keyinputdev.irqkeydesc[i].irqnum); 147 return -EFAULT; 148 } 149 } 150 151 /* 创建定时器 */ 152 init_timer(&keyinputdev.timer); 153 keyinputdev.timer.function = timer_function; 154 155 /* 申请 input_dev */ 156 keyinputdev.inputdev = input_allocate_device(); 157 keyinputdev.inputdev->name = KEYINPUT_NAME; 158 #if 0 159 /* 初始化 input_dev,设置产生哪些事件 */ 160 __set_bit(EV_KEY, keyinputdev.inputdev->evbit); /*按键事件 */ 161 __set_bit(EV_REP, keyinputdev.inputdev->evbit); /* 重复事件 */ 162 163 /* 初始化 input_dev,设置产生哪些按键 */ 164 __set_bit(KEY_0, keyinputdev.inputdev->keybit); 165 #endif 166 167 #if 0 168 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_ MASK(EV_REP); 169 keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |=BIT_MASK(KEY_0); 170 #endif 171 172 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 173 input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); 174 175 /* 注册输入设备 */ 176 ret = input_register_device(keyinputdev.inputdev); 177 if (ret) { 178 printk("register input device failed!\r\n"); 179 return ret; 180 } 181 return 0; 182 } 183 184 /* 185 * @description : 驱动入口函数 186 * @param : 无 187 * @return : 无 188 */ 189 static int __init keyinput_init(void) 190 { 191 keyio_init(); 192 return 0; 193 } 194 195 /* 196 * @description : 驱动出口函数 197 * @param : 无 198 * @return : 无 199 */ 200 static void __exit keyinput_exit(void) 201 { 202 unsigned int i = 0; 203 /* 删除定时器 */ 204 del_timer_sync(&keyinputdev.timer); 205 206 /* 释放中断*/ 207 for (i = 0; i < KEY_NUM; i++) { 208 free_irq(keyinputdev.irqkeydesc[i].irqnum, &keyinputdev); 209 } 210 /* 释放 IO */ 211 for (i = 0; i < KEY_NUM; i++) { 212 gpio_free(keyinputdev.irqkeydesc[i].gpio); 213 } 214 /* 释放 input_dev */ 215 input_unregister_device(keyinputdev.inputdev); 216 input_free_device(keyinputdev.inputdev); 217 } 218 219 module_init(keyinput_init); 220 module_exit(keyinput_exit); 221 MODULE_LICENSE("GPL"); 222 MODULE_AUTHOR("zipeng");

编写测试APP

keyinputApp.c

1 #include "stdio.h" 2 #include "unistd.h" 3 #include "sys/types.h" 4 #include "sys/stat.h" 5 #include "sys/ioctl.h" 6 #include "fcntl.h" 7 #include "stdlib.h" 8 #include "string.h" 9 #include <poll.h> 10 #include <sys/select.h> 11 #include <sys/time.h> 12 #include <signal.h> 13 #include <fcntl.h> 14 #include <linux/input.h> 27 /* 定义一个 input_event 变量,存放输入事件信息 */ 28 static struct input_event inputevent; 29 30 /* 31 * @description : main 主程序 32 * @param - argc : argv 数组元素个数 33 * @param - argv : 具体参数 34 * @return : 0 成功;其他 失败 35 */ 36 int main(int argc, char *argv[]) 37 { 38 int fd; 39 int err = 0; 40 char *filename; 41 42 filename = argv[1]; 43 44 if(argc != 2) { 45 printf("Error Usage!\r\n"); 46 return -1; 47 } 48 49 fd = open(filename, O_RDWR); 50 if (fd < 0) { 51 printf("Can't open file %s\r\n", filename); 52 return -1; 53 } 54 55 while (1) { 56 err = read(fd, &inputevent, sizeof(inputevent)); 57 if (err > 0) { /* 读取数据成功 */ 58 switch (inputevent.type) { 59 case EV_KEY: 60 if (inputevent.code < BTN_MISC) { /* 键盘键值 */ 61 printf("key %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release"); 62 } else { 63 printf("button %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release"); 64 } 65 break; 66 67 /* 其他类型的事件,自行处理 */ 68 case EV_REL: 69 break; 70 case EV_ABS: 71 break; 72 case EV_MSC: 73 break; 74 case EV_SW: 75 break; 76 } 77 } else { 78 printf("读取数据失败\r\n"); 79 } 80 } 81 return 0; 82 }

运行测试

编译驱动程序

KERNELDIR := /home/zipeng/linux/myKernel/linux-imxrel_imx_4.1.15_2.1.0_ga ...... 4 obj-m := keyinput.o ...... 11 clean: 12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

make -j16

编译测试APP

arm-linux-gnueabihf-gcc keyinputApp.c -o keyinputApp

运行测试

将前面编译出来 keyinput.kokeyinputApp这两个文件拷贝到rootfs/lib/modules/4.1.15

目录中,重启开发板,进入到目录lib/modules/4.1.15中。在加载keyinput.ko驱动模块之前,先
看一下/dev/input目录下都有哪些文件,结果如图所示。
当前/dev/input目录只有event0mice这两个文件。输入 如下命令加载 keyinput.ko这个驱动模块。
depmod //第一次加载驱动的时候需要运行此命令
modprobe keyinput.ko //加载驱动模块
当驱动模块加载成功以后再来看一下/dev/input目录下有哪些文件,结果如图:

输入如下测试命令:

./keyinputApp /dev/input/event1

Linux自带按键驱动程序的使用

-> Device Drivers
-> Input device support
-> Generic input layer (needed for keyboard, mouse, ...) (INPUT [=y])
-> Keyboards (INPUT_KEYBOARD [=y])
->GPIO Buttons
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/2 4:57:10

Tree.js:轻松创建逼真3D树木的终极解决方案

Tree.js&#xff1a;轻松创建逼真3D树木的终极解决方案 【免费下载链接】tree-js Procedural tree generator written with JavaScript and Three.js 项目地址: https://gitcode.com/gh_mirrors/tr/tree-js 在WebGL和Three.js的世界中&#xff0c;创造逼真的自然环境一直…

作者头像 李华
网站建设 2026/6/2 4:56:09

基于单调迭代与保守离散化的随机变量期望上界计算

1. 项目概述&#xff1a;从理论到实践的期望上界计算在概率论和算法分析里&#xff0c;我们常常会遇到一些“长相”复杂的随机变量。它们可能由一个递归方程定义&#xff0c;比如S 1 max{U * S‘, (1-U) * S‘’}&#xff0c;其中U是均匀分布&#xff0c;S‘和S‘’是它的独立…

作者头像 李华
网站建设 2026/6/2 4:54:49

流式机器学习在工业实时监控中的应用与实战解析

1. 项目概述&#xff1a;当机器学习遇上实时数据流 在工业制造领域&#xff0c;尤其是像半导体晶圆生产这样的高精尖、高成本行业&#xff0c;一个微小的工艺偏差就可能导致价值数万甚至数十万美元的材料报废。传统的质量控制往往依赖于生产结束后的离线检测&#xff0c;这种“…

作者头像 李华