author: hjjdebug
date: 2026年 05月 26日 星期二 09:20:00 CST
descrip: 注册一个输入设备. 系统有那些改变?
文章目录
- [1. 查看 /proc 目录的变化](#1. 查看 /proc 目录的变化)
- [2. 查看 /sys/devices/.../目录](#2. 查看 /sys/devices/.../目录)
-
- [2.1 关于设备文件目录下的event14 目录](#2.1 关于设备文件目录下的event14 目录)
- [3. /dev 下目录变化](#3. /dev 下目录变化)
- [4. 附录(测试代码)](#4. 附录(测试代码))
测试代码见附录. 代码很短,不足百行.
代码中那些分配内存,赋值等是容易理解的.
关键是下面这个函数,向系统注册, 则系统发生了什么变化?
这是跟系统打交道的函数,
ret = input_register_device(g_in_dev);
向系统注册一个输入设备, 当然系统就多管理了一个输入设备.
系统会通过伪文件系统/sysfs, /proc, 及其它方式把设备信息暴露给用户.
我们下面就来研究一下这些接口,以便更好的了解这个设备,使用这个设备.
实验步骤如下:
把文件编译成.ko文件(input_dev.ko)
然后安装模块,查看信息输出.
$ insmod input_dev.ko
$ dmesg
2512.487674 input_dev demo init
2512.487725 input: test_input_device as /devices/virtual/input/input15
2512.594059 input device register success
1. 查看 /proc 目录的变化
在 /proc/bus/input 目录下,有一个devices 文件, 记录了所有输入设备. 你这里新添加了一个输入设备,
$ cat devices 文件, 就会多出来一项设备记录. 如下:
I: Bus=0019 Vendor=1234 Product=5678 Version=0001
N: Name="test_input_device"
P: Phys=test_input/input0
S: Sysfs=/devices/virtual/input/input15
U: Uniq=
H: Handlers=kbd event14
B: PROP=0
B: EV=3
B: KEY=800
其中ID,name,phys, 就是我们填充的信息. 其它信息在/sysfs 中说明
kbdhandler,代表内核键盘子系统也能收到上报的 KEY 事件
重点看一下sysfs 系统文件目录
2. 查看 /sys/devices/.../目录
sysfs, 说明在/sys/devices/virtual/input/input15/目录下建立了对应的输入设备
设备的属性能够从这里得到.
hjj@hjj-laptop:/sys/devices/virtual/input/input15$ ll
总用量 0
drwxr-xr-x 6 root root 0 5月 26 08:16 ./
drwxr-xr-x 3 root root 0 5月 26 08:16 .../
drwxr-xr-x 2 root root 0 5月 26 08:16 capabilities/
drwxr-xr-x 3 root root 0 5月 26 08:16 event14/
drwxr-xr-x 2 root root 0 5月 26 08:16 id/
-r--r--r-- 1 root root 4096 5月 26 08:36 modalias
-r--r--r-- 1 root root 4096 5月 26 08:16 name
-r--r--r-- 1 root root 4096 5月 26 08:16 phys
drwxr-xr-x 2 root root 0 5月 26 08:36 power/
-r--r--r-- 1 root root 4096 5月 26 08:16 properties
lrwxrwxrwx 1 root root 0 5月 26 08:16 subsystem -> .../.../.../.../class/input/
-rw-r--r-- 1 root root 4096 5月 26 08:16 uevent
-r--r--r-- 1 root root 4096 5月 26 08:36 uniq
hjj@hjj-laptop:/sys/devices/virtual/input/input15$ cat name
test_input_device
hjj@hjj-laptop:/sys/devices/virtual/input/input15$ cat phys
test_input/input0
hjj@hjj-laptop:/sys/devices/virtual/input/input15$ cat properties
0
hjj@hjj-laptop:/sys/devices/virtual/input/input15$ cat uniq
hjj@hjj-laptop:/sys/devices/virtual/input/input15$ cat uevent
PRODUCT=19/1234/5678/1
NAME="test_input_device"
PHYS="test_input/input0"
PROP=0
EV=3
KEY=800
MODALIAS=input:b0019v1234p5678e0001-e0,1,kramlsfw
EV=3 说明
EV 是event bit, 支持的事件类型位图.
__set_bit(EV_KEY, g_in_dev->evbit); 是设置了位图1(bit1,EV_KEY=0x01) 为1
EV_REL = 0x02 (相对坐标,鼠标) 未设置
EV_ABS = 0x03 (绝对坐标,触摸屏),未设置
EV_SYN = 0x00 (同步事件, 默认开启).
所以 EV= EV_SYN(0) + EV_KEY(1) = 3
KEY=800 说明
KEY 是 key bit, 按键码位图集合
代码中
__set_bit(TEST_KEY_CODE, g_in_dev->keybit); //TEST_KEY_CODE(11)
是设置keybit 的第11位
KEY=0x800 就对应着第11位,代表按键0, 就是说它仅支持按键0, 当然,如果你想支持2个按键,
就再加一个,想支持101键盘,就加101个按键.
2.1 关于设备文件目录下的event14 目录
其中14代表的是系统登记的第14个输入事件,在/dev/input 目录下记录了所有输入事件
hjj@hjj-laptop:/sys/devices/virtual/input/input15/event14$ ll
总用量 0
drwxr-xr-x 3 root root 0 5月 26 08:16 ./
drwxr-xr-x 6 root root 0 5月 26 08:16 .../
-r--r--r-- 1 root root 4096 5月 26 09:00 dev
lrwxrwxrwx 1 root root 0 5月 26 08:16 device -> .../.../input15/
drwxr-xr-x 2 root root 0 5月 26 09:00 power/
lrwxrwxrwx 1 root root 0 5月 26 08:16 subsystem -> .../.../.../.../.../class/input/
-rw-r--r-- 1 root root 4096 5月 26 08:16 uevent
hjj@hjj-laptop:/sys/devices/virtual/input/input15/event14$ cat dev
13:78
事件文件uevent
hjj@hjj-laptop:/sys/devices/virtual/input/input15/event14$ cat uevent
MAJOR=13
MINOR=78
DEVNAME=input/event14
3. /dev 下目录变化
在/sysfs 中看到了event14, 在/sys/.../event14 的uevent中也看到input/event14
在/dev/input 下,确实看到了字符设备 event14, 其主设备号13, 子设备号78
hjj@hjj-laptop:/dev/input$ ll
总用量 0
drwxr-xr-x 4 root root 380 5月 26 08:16 ./
drwxr-xr-x 17 root root 3380 5月 26 07:35 .../
drwxr-xr-x 2 root root 120 5月 26 07:35 by-id/
drwxr-xr-x 2 root root 160 5月 26 07:35 by-path/
crw-rw---- 1 root input 13, 64 5月 26 07:35 event0
crw-rw---- 1 root input 13, 65 5月 26 07:35 event1
crw-rw---- 1 root input 13, 74 5月 26 07:35 event10
crw-rw---- 1 root input 13, 75 5月 26 07:35 event11
crw-rw---- 1 root input 13, 76 5月 26 07:35 event12
crw-rw---- 1 root input 13, 77 5月 26 07:35 event13
crw-rw---- 1 root input 13, 78 5月 26 08:16 event14 *************
crw-rw---- 1 root input 13, 66 5月 26 07:35 event2
crw-rw---- 1 root input 13, 67 5月 26 07:35 event3
crw-rw---- 1 root input 13, 68 5月 26 07:35 event4
crw-rw---- 1 root input 13, 69 5月 26 07:35 event5
crw-rw---- 1 root input 13, 70 5月 26 07:35 event6
crw-rw---- 1 root input 13, 71 5月 26 07:35 event7
crw-rw---- 1 root input 13, 72 5月 26 07:35 event8
crw-rw---- 1 root input 13, 73 5月 26 07:35 event9
其中event14 就是我们刚刚及建立的.
这是用户程序访问设备的接口文件
如果你rmmod input_device, 则上面内核暴露的对应的文件目录及文件都会消失
/proc/input/devices 中的项
/sysfs/... 对应的目录
/dev/input/对应的节点
4. 附录(测试代码)
cpp
$cat input_dev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/delay.h>
// 定义按键值(KEY_0 代表数字0按键)
#define TEST_KEY_CODE KEY_0
// 定义输入设备结构体指针
static struct input_dev *g_in_dev;
/*
* 模拟按键上报函数(实际硬件中,会在GPIO中断中调用)
* 这里仅做演示,手动上报按下+松开
*/
static void report_key_event(int pressed)
{
// 上报按键事件:事件类型EV_KEY,按键码KEY_0,状态(1按下/0松开)
input_report_key(g_in_dev, TEST_KEY_CODE, pressed);
// 通知系统事件发送完成
input_sync(g_in_dev);
}
/*
* 驱动入口函数
*/
static int __init input_dev_demo_init(void)
{
int ret;
printk("input_dev demo init\n");
// 1. 分配输入设备结构体
g_in_dev = input_allocate_device();
if (!g_in_dev) {
printk(KERN_ERR "input_allocate_device failed\n");
return -ENOMEM;
}
// 2. 设置输入设备基本信息
g_in_dev->name = "test_input_device"; // 设备名称
g_in_dev->phys = "test_input/input0"; // 设备物理路径
g_in_dev->id.bustype = BUS_HOST; // 总线类型,BUS_HOST(0x19)
g_in_dev->id.vendor = 0x1234; // 厂商ID
g_in_dev->id.product = 0x5678; // 产品ID
g_in_dev->id.version = 0x0001; // 版本号
// 3. 声明设备支持的事件类型和事件码
// 支持按键类事件(EV_KEY)
__set_bit(EV_KEY, g_in_dev->evbit); //EV_KEY(1)
// 支持KEY_0这个按键
__set_bit(TEST_KEY_CODE, g_in_dev->keybit); //TEST_KEY_CODE(11)
// 4. 注册输入设备到内核
ret = input_register_device(g_in_dev); //注册后发生了什么?
if (ret) {
printk(KERN_ERR "input_register_device failed\n");
// 注册失败,释放已分配的内存
input_free_device(g_in_dev);
return ret;
}
// 模拟:上报一次按键按下 → 延时 → 按键松开
report_key_event(1);
msleep(100);
report_key_event(0);
printk("input device register success\n");
return 0;
}
/*
* 驱动出口函数
*/
static void __exit input_dev_demo_exit(void)
{
printk("input_dev demo exit\n");
// 注销输入设备
input_unregister_device(g_in_dev);
}
module_init(input_dev_demo_init);
module_exit(input_dev_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("demo");
MODULE_DESCRIPTION("Simple input_dev usage example");