以下以usb接口的有线和蓝牙键鼠为例来先容详细的实现流程。
一. USB有线键盘1. 框图如下:2.模块先容:
1)适配器端(键盘芯片):
---机器键盘矩阵: 键盘矩阵由多个行和列组成,按键与矩阵的交叉点连接在一起。当按下某个按键时,电路会识别出对应的行和列信息;
---键盘掌握器: 键盘掌握器通过扫描键盘矩阵来检测按键的状态变革, 并将对应的按键编码经由分外协议的封装再发送给USB接口掌握器;
---HID协议转换: 将采集的按健编码封装成HID协议格式再通过USB接口传输给打算器;HID协议定义了标准的数据格式、命令构造以及传输办法,使得不同的输入设备间能相互识别和兼容。
---USB接口掌握器: USB接口掌握器则将按键编码封装成USB协议格式,通过USB接口传输给打算机。
2)主控端:
---USB Host掌握器;用于和usb从设备的底层数据通信。USB Host供应了主机掌握器的驱动程序,并管理USB Hub以及在Hub上连接的USB设备;
---USB core总线协议:USB Core是内核设计的一个抽象层,目的是将Class Driver和USB Host掌握器 Driver分别隔,使两者都依赖一个稳定的中间层;USB Core向上供应通信接口,向下统一管理USB设备,同时完成USB设备和USB Driver的匹配事情;
---HID协议转换:基于USB协议的实现。用于解析HID设备封装的用户操作和输入信息;HID协议定义了标准的数据格式、命令构造以及传输办法,使得不同厂商生产的输入设备可以被操作系统识别和兼容。
---input子系统: input子系统处理输入事务,任何输入设备的驱动程序都可以通过input 输入子系统供应的接口注册到内核,利用子系统供应的功能来与用户空间交互。 input 子系统用到了驱动分层模型,编写驱动程序的时只须要关注中间的驱动层、核心层和事宜层。
a.驱动层:输入设备的详细驱动程序,比如按键驱动程序,向内核层报告输入内容
b. 核心层:承上启下,为驱动层供应输入设备注册和操作接口。关照事宜层对输入事宜进行处理,链接其他两个层之间的纽带与桥梁,向下供应驱动层的接口, 向上供应事宜处理层的接口。
c.事宜层:紧张和用户空间进行交互,将硬件驱动层传来的事宜报告给用户程序
各层之间通信的基本单位是事宜, 任何一个输入设备的动作都可以抽象成一种事宜, 如键盘的按下,触摸屏的按下, 鼠标的移动等。 事宜有三种属性: 类型(type), 编码(code),值(value), input 子系统支持的所有事宜都定义在 input.h 中, 包括所有支持的类型, 所属类型支持的编码等。
事宜传送的方向:硬件驱动层-->子系统核心-->事宜处理层-->用户空间。 在节点/dev/input 下面则是输入设备的节点。
---usb设备驱动: 供应设备识别的初始化事情和设备详细操作的接口方法,类似于这个设备的利用解释书。以及读取数据后,会做一些键值映射和转换的功能,注册input子系统,知足标准的input实行流程;
个中除usb设备驱动须要自己开拓外,其它模块linux内核都已经实现.
3.流程解释:下面来详细解释每一步的实行过程:
1)USB设备的列举和识别过程:
a.主机真个USB集线器监视着它的每个端口的旗子暗记线的电压,当USB设备插入主机时,旗子暗记线的电平会发生变革,此时主机知道有新设备插入;
b.当主机检测到设备的插入后会重启这个设备,接着主机发出Get_Port_Status要求来验证设备是否已经重启,设备重启后主机通过检测根旗子暗记线的电平状态判断设备的速率;
c. 主机发送第一次Get_Descriptor(wValue字段的高字节为0x01,表示设备描述符)要求取得设备描述符,设备描述符供应了设备的多种信息, 包括:设备通讯终端0的最大包的大小,设备支持的配置号以及有关这个设备的其它信息,主机通过对这些信息的剖析以确定接下来的通信动作;
d.设备描述符里规定了设备一个或多个配置描述符,主机再次或多次发出Get_Descriptor(wValue字段的高字节为0x02,表示配置描述符)指令来读取这些配置描述符,第一次只读出配置描述符的前9个字节,这9个字节里包含了配置描述符和它的所有从属描述符(接口描述符、端点描述符)的总长度, 然后主机根据这个长度读出设备的所有配置描述符(包括其所有从属描述符)。
e. 在读取完配置描述符后,若之间读取的设置描述中指定了干系字符串描述符(用来描述厂商、产品和设备序列号信息的)的索引, 主机将发出多少次Get_Descriptor(wValue字段的高字节为0x03,表示字符串描述符)命令来得到这些字符串描述,此时主机将会弹出窗口, 展示创造新设备的信息,产商、产品描述、型号等。
f. 在主机已经从它的描述符中知道了能够知道的所有信息后,便开始调用match函数通过PID/VID为这个设备匹配对应的驱动,如果没有找到,则会停滞接下来的流程;
g.加载完USB设备驱动,实行probe函数初始化完成后,主机发送Set_Configuration命令要求为该设备选择一个得当的配置。
至此,USB设备列举过程结束,USB通道建立,设备可以正常利用了。
2) USB设备和驱动的匹配解释;
linux系统的USB驱动模型是由三部分组成:设备(构造体device)、驱动(构造体device_driver)和总线(构造体bus_type)。 总线掩护着设备和驱动两个不同的行列步队,bus_type是两者的中间纽带,usb device 和driver间的通信都是通过bus_type供应的注册接口,两者在注册到总线的过程时都会触发总线的匹配流程。
3)设备端键值数据采集和USB输入;
紧张由两部分组成:键盘驱动电路和键盘掌握器;
a.键盘驱动电路卖力将键盘的电旗子暗记转换为数字旗子暗记,以便键盘掌握器能够精确解析扫描码, 键盘驱动电路常日包括滤波电路、键盘编码电路等;
b.键盘通过扫描矩阵的办法来检测按键的状态,键盘掌握器卖力将按键的状态转换为扫描码, 并经由USB HID协议封装后发送给USB主控端;
4)获取HID报告并解析(drivers/hid/usbhid/hid-core.c):
主机端收USB数据后判断为hid格式数据后,通过hid-core供应的方法,解析出详细的键值数据;
详细可看 hid_irq_in(struct urb urb)函数中的 hid_input_report(urb->context, HID_INPUT_REPORT,urb->transfer_buffer, urb->actual_length, 1)方法
5)设备驱动中断吸收处理和键值转换;
a. USB鼠标驱动程序(drivers/hid/usbhid/usbmouse.c):
通过中断获取urb数据,并根据设备类型按其格式通过input子模块进行数据上报。
b. USB键盘驱动程序(drivers/hid/usbhid/usbkbd.c):
内核中的键盘驱动程序卖力吸收键盘发送的扫描码(经由HID解析后),并将上述扫描码通过键盘映射表转换为操作系统能够理解的字符编码。 不同的操作系统可能有不同的键盘驱动程序,但其事情事理大致相同。
6)通过input子系统上报给用户空间;
---input_dev 注册过程:
①利用 input_allocate_device 函数申请一个 input_dev
②初始化 input_dev的事宜类型以及事宜值
③利用 input_register_device 函数向 Linux 系统注册初始化好的 input_dev
④先利用input_unregister_device函数注销掉注册的input_dev,然后利用 input_free_device 函数开释掉申请的 input_dev
---上报输入事宜
在 input 设备驱动中申请、 注册完成 input_dev 构造体后, 还不能正常利用 input 子系统, 由于 input设备是输入一些信息,但是 Linux 内核还不清楚输入的信息表示什么意思, 以是须要驱动获取到详细的输入值, 或者说输入事宜, 然后将输入事宜上报给 Linux 内核。比如按键设备, 须要在按键产生后将按键值上报给 Linux 内核, Linux 内核获取到详细的按键值后, 才会实行相应的功能。 不同的事宜上报的函数不同。
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 表示按键松开
常用的事宜上报函数:
void input_report_key(struct input_dev dev,unsigned int code, int value)
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 子系统上报结束,实质是上报一个同步事宜
void input_sync(struct input_dev dev)
7)用户空间键值获取、解析和处理;
(1)Linux 内核利用 input_event 这个构造体来表示所有的输入事宜,定义在include/uapi/linux/input.h 文件中
struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value;};
---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_event 构造体通报给用户,用户运用程序可以通过 input_event 来获取到详细的输入事宜或干系的值。在加载驱动模块之后,会在/dev/input 目录下event2文件;
(2)示例程序(测试键值):
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <linux/input.h>int main(int argc, char const argv[]){ //打开设备文件 int fd; int retval; fd_set readfds; struct timeval tv; if((fd = open("/dev/input/event0", O_RDONLY)) == -1) { perror("open error"); return -1; } //读取文件内容 struct input_event mykey; while(1){ FD_ZERO(&readfds); FD_SET(fd, &readfds); if((retval = select(fd+1, &readfds, NULL, NULL, &tv)) == 1) { if(read(fd, &mykey, sizeof(mykey)) == sizeof(mykey)){ // 事宜类型 鼠标或者按键 if(mykey.type == EV_KEY) { printf("--------------------\n"); printf("type = %u.\n", mykey.type); printf("code = %u.\n", mykey.code); printf("value = %u.\n", mykey.value); / 按键是按下还是开释,0开释、1按下、2长按 /switch (inputevent.type) {case EV_KEY:if (inputevent.code < BTN_MISC) / 键盘键值 /{printf("key %d %s\r\n", mykey.code, mykey.value ? "press" : "release");} else {printf("button %d %s\r\n", mykey.code, mykey.value ? "press" : "release");} break;/ 其他类型的事宜,自行处理 /case EV_REL:break;case EV_ABS: break;case EV_MSC: break;case EV_SW: break; } } } } return 0;}
二. 蓝牙usb无线键鼠:1. 简介:
通过蓝牙技能或2.4GHz无线技能,无线键鼠实现了与电脑的无线连接和通信。
详细事情事理如下:
1) 无线键盘和鼠标内部搭载了2.4GHz蓝牙发射模块,把检测到的用户输入旗子暗记或键值经由HID协议封装后,再通过蓝牙发送到主机吸收端;
2) 主机端有usb接口的蓝牙吸收器,紧张用于吸收无线鼠标或键盘发送的数据,并通过USB接口与主机通信;
2. 框图如下:3. 与有线usb键鼠差异:从两个框图比拟可以看出,USB无线鼠标和有线鼠标两部分的紧张差异在适配器部分的事情量,以及主机host端 HID协议和driver有差异,其它部分基本一样,差异如下:
1)输入键值的采集和hid协议封装是在蓝牙发射端处理的,无线usb吸收端仅做数据透传处理,不会进行hid封装;
2)主机会把usb 蓝牙吸收器识别成普通usb设备,而不是hid设备;
3)hid协议的解析是在net/bluetooth/hidp子系统下处理的,有线usb键鼠hid协议则是在/driver/usbhid模块下实行;
4). 有线USB键鼠driver1紧张事情是吸收到经由HID协议解析后的数据,再根据健值转换并封装成知足input event须要数据格式,并调用input 子系统上报给用户空间;
5) 无线USB键鼠中的driver2部分紧张事情除了有线鼠标driver1的功能外,还有便是须要完成ble配置干系的接口,如:模式设置,扫描、广播、配对、回连、通信等接口。
以上即为无线和有线键鼠事情流程的大略剖析,记下条记,后期理解深入后再补充完善。