一、概况
以Linux5.10内核中USB键盘驱动为例进行解析:https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.gz
文件路径:linux-5.10/drivers/hid/usbhid/usbkbd.c
二、探索
入口
- 首先我们直接看
usb_kbd_probe
这个函数的335行usb_fill_int_urb
,这里是在填充USB中断传输请求。也就是说在用后面那一大堆参数填充kbd->irq
这个变量。然后设置的这个传输的回调函数是usb_kbd_irq
,也就是说当有键值报上来的时候,将会用usb_kbd_irq
函数处理。
usb_kbd_irq
- 我们可以看到该函数的参数是一个urb(USB request block),表示本次完成传输的请求。在看117和118行,调用了函数
input_report_key
向输入子系统上报键值。该函数第一个参数代表设备,第二个参数代表键值,第三个参数代表该键被按下还是释放。
for (i = 0; i < 8; i++)
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
-
第二个参数
usb_kbd_keycode[i + 224]
,这里的usb_kbd_keycode
是一个数组,该数组用来将键盘报上来的键值,转化为输入子系统能够识别的键值。转化的原理就是usb_kbd_keycode
的数组下标代表键盘报上来的键值,对应位置的值,代表转化后报给输入子系统的键值。所以这里的意思就是将键盘报上来的i+224
这个键值转化为输入子系统对应的键值。 -
那么这里的
i+224
又代表哪个键呢?由于i是循环变量,从0开始到7。所以i+224,其实是224~231。所以我们只需要搞清楚,224~231
这几个值代表哪几个键。 -
通过查阅HID Usage Tables的第10节
10 Keyboard/Keypad Page (0x07)
,我们可以知道当键盘报上来04,代表字母a。如下:
-
所以我们继续查阅该表,16进制E0转换成10进制就是224,其代表左侧CTRL键。所以上面的
224~231
也就是E0~E7
,分别代表LeftControl
,LeftShift
,。。。。。。
-
我们再次回到代码里面,
usb_kbd_keycode[i + 224]
意思就是将LeftControl
,LeftShift
,。。。。。。八个键盘报上来的键值转化为输入子系统定义的键值。 -
上面我们知道了键值转化的原理,那么我们怎么知道这个键有没有被按下呢?我们接着看
(kbd->new[0] >> i) & 1
,将new[0]元素右移i位,然后在与1。啥意思?通过查阅USB HID协议8.3节,我们可以知道USB会通过一个8位的bitmap来上报CTRL,SHIFT,ALT等键。具体如下:
-
也就是说
new[0]
这个unsigned char
类型的变量里面的每一位代表一个键,如果这个位为0,则表示该位对应的键没有被按下;为1,则该位对应的键被按下了。每个位代表的键如上表。
三、总结
总结一下,117和118行的这个for循环,通过依次查看new[0]元素的各个位,确定该位对应的键是否被按下。并将该键值通过usb_kbd_keycode表转化为输入子系统定义的键值,后上报给输入子系统。