1 struct input_dev 和 struct input_handler
1.1 简介
struct input_dev表示一个设备驱动层的输入设备。
struct input_handler是处理struct input_dev上报的事件的事件处理器。
1.2 全局变量input_dev_list,input_handler_list
输入设备链表:input_dev_list
事件处理器链表:input_handler_list
在input_register_device()函数里会将注册的struct input_dev结构体对象加入全局变量input_dev_list中。
在input_register_handler()函数里会将注册的struct input_handler结构体对象加入全局变量input_handler_list中。
1.3 struct input_dev 和 struct input_handler的匹配
1.3.1 匹配
input_register_device()函数会遍历事件处理器链表input_handler_list,根据id号查找匹配的struct input_handler。
input_register_handler()函数会遍历输入设备链表input_dev_list,根据id号查找匹配的struct input_handler。
一个输入设备可以关联多个事件处理器。
如果匹配成功,调用handler->connect();
1.3.2 不同struct input_handler的connect()函数
1.3.2.1 evdev_handler的evdev_connect();
evdev_connect()函数做以下工作:
- 调用input_register_handle()函数注册事件处理句柄struct input_handle。
- 注册字符设备文件/dev/input/eventX
evdev_handler会和所有的struct input_dev 对象匹配成功,所以有多少个struct input_dev 对象,/dev/input/目录下就会有多少个eventX设备文件。
1.3.2.2 mousedev_handler的mousedev_connect();
mousedev_connect()函数做以下工作:
- 调用input_register_handle()函数注册事件处理句柄struct input_handle。
- 注册字符设备文件/dev/input/mouseX
1.3.2.3 input_leds_handler的input_leds_connect();
input_leds_connect()函数做以下工作:
- 调用input_register_handle()函数注册事件处理句柄struct input_handle。
- 调用led_classdev_register()函数register a new object of LED class,会在/sys/class/leds/目录下生成"inputXXX"的目录。
1.3.2.4 其他struct input_handler
sysrq_handler
kbd_handler
1.4 查看系统下的struct input_dev 和 struct input_handler信息
1.4.1 /proc/bus/input/devices
信息如下:
bash
I: Bus=0019 Vendor=0000 Product=0003 Version=0000
N: Name="Sleep Button"
P: Phys=PNP0C0E/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=4000 0 0
I: Bus=0019 Vendor=0000 Product=0005 Version=0000
N: Name="Lid Switch"
P: Phys=PNP0C0D/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/input/input1
U: Uniq=
H: Handlers=event1
B: PROP=0
B: EV=21
B: SW=1
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=PNP0C0C/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/input/input2
U: Uniq=
H: Handlers=kbd event2
B: PROP=0
B: EV=3
B: KEY=10000000000000 0
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input3
U: Uniq=
H: Handlers=kbd event3
B: PROP=0
B: EV=3
B: KEY=10000000000000 0
I: Bus=0011 Vendor=0001 Product=0001 Version=ab83
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input4
U: Uniq=
H: Handlers=sysrq kbd event4 leds
B: PROP=0
B: EV=120013
B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=7
I: Bus=0011 Vendor=0002 Product=000a Version=0063
N: Name="TPPS/2 Elan TrackPoint"
P: Phys=isa0060/serio1/input0
S: Sysfs=/devices/platform/i8042/serio1/input/input6
U: Uniq=
H: Handlers=mouse2 event7
B: PROP=21
B: EV=7
B: KEY=70000 0 0 0 0
B: REL=3
I: Bus=0003 Vendor=413c Product=301a Version=0111
N: Name="PixArt Dell MS116 USB Optical Mouse"
P: Phys=usb-0000:00:14.0-7/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/0003:413C:301A.0002/input/input10
U: Uniq=
H: Handlers=mouse3 event8
B: PROP=0
B: EV=17
B: KEY=70000 0 0 0 0
B: REL=903
B: MSC=10
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="Intel HID events"
P: Phys=
S: Sysfs=/devices/platform/INTC1051:00/input/input11
U: Uniq=
H: Handlers=rfkill kbd event9
B: PROP=0
B: EV=13
B: KEY=81000300000000 5000004000 1e294000000020 0
B: MSC=10
......
1.4.2 /proc/bus/input/handlers
bash
N: Number=0 Name=rfkill
N: Number=1 Name=kbd
N: Number=2 Name=sysrq (filter)
N: Number=3 Name=mousedev Minor=32
N: Number=4 Name=evdev Minor=64
N: Number=5 Name=leds
N: Number=6 Name=joydev Minor=0
2 struct input_event(输入事件数据结构体)
2.1 struct input_event的定义(kernel-5.4.18)
一个输入事件包括时间戳、输入事件类型,事件编码(code)和事件值(value),具体信息如下:
cpp
struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)
struct timeval time;
#define input_event_sec time.tv_sec
#define input_event_usec time.tv_usec
#else
__kernel_ulong_t __sec;
#if defined(__sparc__) && defined(__arch64__)
unsigned int __usec;
#else
__kernel_ulong_t __usec;
#endif
#define input_event_sec __sec
#define input_event_usec __usec
#endif
__u16 type;
__u16 code;
__s32 value;
};
2.2 Event types
cpp
/*
* Event types
*/
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
EV_SYN、EV_KEY、EV_REL 和 EV_ABS网上资料很多,本文就不过多介绍了,后面会重点介绍EV_SW 和 EV_LED。
2.3 事件类型------EW_SW(0x05)
2.3.1 简介
EV_SW events describe stateful binary switches. For example, the SW_LID code is
used to denote when a laptop lid is closed.
Documentation/input/event-codes.rst
2.3.2 支持的事件编码(code)
cpp
/*
* Switch events
*/
#define SW_LID 0x00 /* set = lid shut */
#define SW_TABLET_MODE 0x01 /* set = tablet mode */
#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */
#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any"
set = radio enabled */
#define SW_RADIO SW_RFKILL_ALL /* deprecated */
#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */
#define SW_DOCK 0x05 /* set = plugged into dock */
#define SW_LINEOUT_INSERT 0x06 /* set = inserted */
#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */
#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */
#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */
#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */
#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */
#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */
#define SW_LINEIN_INSERT 0x0d /* set = inserted */
#define SW_MUTE_DEVICE 0x0e /* set = device disabled */
#define SW_PEN_INSERTED 0x0f /* set = pen inserted */
#define SW_MAX 0x0f
#define SW_CNT (SW_MAX+1)
2.3.3 事件编码------SW_LID
笔记本电脑的屏幕上盖在合上 或者 掀开时会触发待机 或者 唤醒等操作,在linux内核里,驱动通过SW_LID事件编码向内核报告屏幕上盖是被合上 还是 被掀开。
事件值为1,表示笔记本电脑屏幕上盖被合上;
事件值为0,表示笔记本电脑屏幕上盖被掀开;
2.3.4 事件编码------SW_HEADPHONE_INSERT、SW_LINEOUT_INSERT、SW_LINEIN_INSERT
在很多机器上都会有以下的音频接口,驱动里会通过事件编码SW_HEADPHONE_INSERT、SW_LINEOUT_INSERT、SW_LINEIN_INSERT 和 SW_MICROPHONE_INSERT来通知系统有无插头接入。
2.3.5 通过evtest命令获取笔记本电脑上耳机插孔的耳机插头接入事件
2.3.5.1 判断哪个/dev/input/eventX支持EV_SW事件类型中的SW_HEADPHONE_INSERT 和 SW_MICROPHONE_INSERT
在/proc/bus/input/devices文件内容中有以下信息。表示/dev/input/event14可以上报microphone是否接入,/dev/input/event15可以上报headphone是否接入。
bash
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="sof-hda-dsp Mic"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card0/input19
U: Uniq=
H: Handlers=event14
B: PROP=0
B: EV=21
B: SW=10 //第4个bit为1,表示支持事件编码SW_MICROPHONE_INSERT
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="sof-hda-dsp Headphone"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card0/input20
U: Uniq=
H: Handlers=event15
B: PROP=0
B: EV=21
B: SW=4 //第2个bit为1,表示支持事件编码SW_HEADPHONE_INSERT
2.3.5.2 通evtest抓取输入事件的数据
cpp
# evtest /dev/input/event14
Input driver version is 1.0.1
Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0
Input device name: "sof-hda-dsp Mic"
Supported events:
Event type 0 (EV_SYN)
Event type 5 (EV_SW)
Event code 4 (SW_MICROPHONE_INSERT) state 0
Properties:
Testing ... (interrupt to exit)
Event: time 1713156364.680074, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1 //话筒插头接入
Event: time 1713156364.680074, -------------- SYN_REPORT ------------
Event: time 1713156369.338703, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0 //拔掉话筒插头
Event: time 1713156369.338703, -------------- SYN_REPORT ------------
cpp
# evtest /dev/input/event15
Input driver version is 1.0.1
Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0
Input device name: "sof-hda-dsp Headphone"
Supported events:
Event type 0 (EV_SYN)
Event type 5 (EV_SW)
Event code 2 (SW_HEADPHONE_INSERT) state 0
Properties:
Testing ... (interrupt to exit)
Event: time 1713156221.813617, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1 //耳机插头接入
Event: time 1713156221.813617, -------------- SYN_REPORT ------------
Event: time 1713156229.416541, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0 //拔掉耳机插头
Event: time 1713156229.416541, -------------- SYN_REPORT ------------
2.4 事件类型------EV_LED
2.4.1 支持的事件编码(code)
cpp
/*
* LEDs
*/
#define LED_NUML 0x00
#define LED_CAPSL 0x01
#define LED_SCROLLL 0x02
#define LED_COMPOSE 0x03
#define LED_KANA 0x04
#define LED_SLEEP 0x05
#define LED_SUSPEND 0x06
#define LED_MUTE 0x07
#define LED_MISC 0x08
#define LED_MAIL 0x09
#define LED_CHARGING 0x0a
#define LED_MAX 0x0f
#define LED_CNT (LED_MAX+1)
2.4.2 LED_NUML、LED_CAPSL 和 LED_SCROLLL
2.4.2.1 简介
普通键盘上有下面的3个LED灯
可以通过LED_NUML、LED_CAPSL 和 LED_SCROLLL 这几个事件编码来控制。
2.4.2.2 /sys/class/leds/
上面的"1.3.2.3"小节中描述了,led_classdev_register()函数中会register a new object of LED class,会在/sys/class/leds/目录下生成"inputXXX"的目录,如下:
cpp
/sys/class/leds/input4::capslock
/sys/class/leds/input4::numlock
/sys/class/leds/input4::scrolllock
每个目录下都有一个brightness文件,可以通过这个文件来控制LED灯的亮 和 灭。
bash
echo 0 > /sys/class/leds/input4\:\:capslock/brightness //熄灭键盘上的capslock灯
echo 1 > /sys/class/leds/input4\:\:capslock/brightness //点亮键盘上的capslock灯
2.5 事件类型------EV_SND
蜂鸣器可以使用这个事件类型
3 输入事件的报告流程
3.1 简介
绝大多数的输入事件是通过驱动程序里调用input_event()函数来上报的,另外一种方法是应用程序通过/dev/input/eventX文件接口(evdev_write())来上报输入事件。
3.2 应用程序通过evdev_write()模拟输⼊事件
3.2.1 模拟输入SW_LID事件,让系统进入休眠状态
cpp
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/input.h>
int main()
{
int fd, i, ret;
struct input_event event;
if((fd = open("/dev/input/event1", O_RDWR)) < 0){
return 1;
}
event.type = EV_SW;
event.code = SW_LID;
event.value = 1;
ret = write(fd, &event, sizeof(struct input_event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
ret = write(fd, &event, sizeof(struct input_event));
close(fd);
return 0;
}
3.2.2 模拟输入LED_CAPSL事件,让键盘上的capslock灯闪烁
cpp
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/input.h>
int main()
{
int fd, i, ret;
struct input_event event;
if((fd = open("/dev/input/event19", O_RDWR)) < 0){
return 1;
}
for( i=0; i <= 7; i++){
event.type = EV_LED;
event.code = LED_CAPSL;
event.value = 1;
ret = write(fd, &event, sizeof(struct input_event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
ret = write(fd, &event, sizeof(struct input_event));
sleep(1);
event.type = EV_LED;
event.code = LED_CAPSL;
event.value = 0;
ret = write(fd, &event, sizeof(struct input_event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
ret = write(fd, &event, sizeof(struct input_event));
sleep(1);
}
close(fd);
return 0;
}
4 扩展知识
4.1 ACPI中的相关设备
PNP0C0D: Lid Device
PNP0C0E: Sleep Button Device
PNP0C0C: Power Button Device
4.2 相关命令
evtest
xinput