摘要:USB(Universal Serial Bus)即“通用串行总线”是一种应用在计算机领域的新型接口技 术。它的出现大大简化了PC机和外围设备的连接过程,使PC机接口的扩展变得更加容易。USB作为近年来计算机和嵌入式领域中的热点,推动了计算机外设的 飞速发展。本文介绍了适用于PC的嵌入式操作系统的USB HID设备驱动的设计,并给出了具体的实现方法。
关键词: USB HID设备 PC 嵌入式 驱动程序
从USB 1.1到USB2.0再到目前的USB OTG(On-The-Go),USB在不断自我完善,并走向成熟。USB具有高速度、低成本、低功耗、即插即用和使用维护方便等优点,不仅成为了PC主 板上的标准接口,而且成为了所有PC外部设备如键盘、鼠标、显示器、打印机、数码相机等与PC相连的标准协议之一,迅速占领了计算机中、低速外部设备的市 场。
USB(Universal Serial Bus)即“通用串行总线”是一种应用在计算机领域的新型接口技术。USB的拓扑结构中居于核心地位的是Host(也称为主机)。任何一次USB的数据传 输都必须由主机来发起和控制,所有的USB外设都只能和主机建立连接,任何两个外设之间或是两个主机之间无法直接通信。而目前,大量的扮演主机角色的是个 人电脑PC。
随着USB应用领域的逐渐扩大,对于USB的期望也越来越高。我们希望USB能应用在各种计算机领域中,希望能通过PDA等移动设备直接和USB外设通信,使得USB能应用在没有PC的领域中。
而我们目前所使用的USB移动设备,大多数都是USB的外设,比如USB的移动硬盘、USB接口的数码相机等。所有这些设备都只能在PC上使用,只能通过PC来进行相互的文件和数据交换。
本驱动程序是为完善我们自行设计的嵌入式操作系统,使得它具备能识别USB HID设备的功能而开发的。所使用的编程语言为C语言,并下载到目标机上,通过测试验证可以识别USB HID设备,如USB键盘,USB鼠标等。本文探讨的即是PC上实现USB HID设备驱动的方法。
⒈ HID 设备 驱动简介
为简化USB设备的开发过程,USB提出了设备类的概念。HID设备类,即人机接口设备。典型的HID设备如键盘、鼠标。
所有设备类都必须支持标准USB描述符和标准USB设备请求。如果有必要,设备类还可以自行定义其专用的描述符和设 备请求,这分别被称为设备类定义描述符和设备类定义请求。另外,一个完整的设备类还将指明其接口和端点的使用方法,如如接口所包含端点的个数、端点的最大 数据包长度等。
HID设备既可以是低速设备也可以是全速设备,其典型的数据传输类型为中断IN传输,即它适用于主机接收USB设备 发来的小量到中等量的数据。HID具有以下的功能特点:1)适用于传输少量或中量的数据;2)传输的数据具有突发性;3)传输的最大速率有限制;4)无固 定的传输率。
HID设备类除支持标准USB描述符外(设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符),还自行 定义了3种类描述符,分别为HID描述符(主要用于识别HID设备所包含的其他类描述符)、报告描述符(提供HID设备和主机间交换数据的格式)和物理描 述符。一个HID设备只能支持一个HID描述符;可以支持一个或多个报告描述符;物理描述符是可选的,大多数HID设备不需要使用它。
⒉ USB HID设备驱动原理
设备的USB 人机交互设备必须遵循以下的USB开始程序:
1) 插入设备
USB设备第一次连接到总线时,虽然接上了电源,但是总线仍然没有任何功能,一定要到重置总线为止才可以开始运作。注意,一旦USB在D端使用了 1.5kΩ的提升电阻,就会立即通知总线的集线器,有一个低速设备(1.5Mb/s)刚被连接上。而程序以设备地址0开始运行。
设备插上时,电源打开重置的过程: 重置->执行初始设置并出发总线重置中断->位于中止模式下知道总线被重置为止->等待设备列举->执行程序循环
2) 总线重置
接着主机将会辨认新的USB设备并重置它。在总线重置过程中,除了设定堆栈指针外,也出发所有被使用到的中断。(总线重置的中断服务程序ISR功能)
3) 设备列举。
主机会负责检测与设定所有连接至根集线器的设备,辨别与设定一个USB设备的程序,称为设备列举。主机首先会送出SETUP封包以读取默认地址0的设备描 述符。当收到描述符后,主机将会指定新的USB地址给设备。从设备所返回的信息中,主机就会知道设备所支持的数据端点的数量。完成设备列举。
4) 数据捕捉与转换
这里以键盘为例,在固件中将以周期性的方式,把扫描的形式写入到扫描矩阵的列I/O端口伤(接口2),并且在行I/O接口伤读取结果值以决定哪个键被按下 了。通过键盘扫描后所得到的数据码,可以使用中断传输以端点1来传送给主机。设备就将键盘的8B数据放置在IN令牌包随后跟随的资料封包的数据域位内,再 返回给主机。
当含有LED的按键(如NumLock ,Caps lock 与 Scroll lock)被按下或放开时,主机就会送出含有设定报告(Set_Report)要求的SETUP封包,通过控制传输传至设备的端口0上。
⒊ USB HID设备驱动程序设计的流程说明
USB总线与设备间的交互都是通过USBD即USB总线驱动程序完成。USBD起着中间桥梁作用,解释USB设备类驱动程序发来的命令并将其划分为一系列的USB事务,然后发送给USB主控制器驱动程序。
具体流程是插入一个USB设备后,主机检测到有设备接入,USBD就从链表中查找匹配HID设备类。为每一个接入的 HID设备驱动建立一个对应的USB_HID_SIO_CHAN结构来对该HID设备驱动进行管理。这里的USB_HID_SIO_CHAN结构是 USBD为每一个HID设备所分配的一个关键的内部数据结构。此后由USB主控制器驱动程序来负责硬件底层的驱动。
而HID设备移除时,会调用函数usbHIDDeviceAttachCallback() ,这时先判断是否有与该HID设备绑定的结构,有则清除该结构。
成功注册一个没有被初始化的USB HID设备的程序流程如下:
⒋ USB HID设备驱动程序的实现
因为键盘和鼠标同为HID设备,具体驱动程序实现极为相似,这里仅以键盘的驱动程序实现为例,给出最主要的函数说明:
1. STATUS usbKeyboardDevInit()
功能:键盘初始化函数,依次初始化与USBD的连接,和其他所需的内部资源。
返回值:操作成功返回OK,失败返回ERROR
注意:在调用usbKeyboardDevInit ( )前,必须保证USBD层已经初始化-至少调用了usbdInitialize().还要保证至少一个USB HCD(USB Host Controller Driver)连接到了USBD层。
2.STATUS usbdClientRegister
(
T_BYTE* pClientName, /* Client name */
pUSBD_CLIENT_HANDLE pClientHandle /* Client hdl returned by USBD */
)
功能:向USBD注册一个新客户(键盘,鼠标等)
返回值:操作成功返回OK,失败返回ERROR
3。STATUS usbdDynamicAttachRegister
(
USB_KBD_ATTACH_CALLBACK callback, /* new callback to be registered */
T_VOID* arg /* user-defined arg to callback */
)
功能:每次插上或者移除键盘时,都会由回调函数调用,
该函数实现USB设备动态插拔。
返回值:操作成功返回OK,失败返回ERROR
4. T_MODULE T_VOID usbKeyboardAttachCallback
(
USBD_NODE_ID nodeId,
T_UHWORD attachAction,
T_UHWORD configuration,
T_UHWORD interface,
T_UHWORD deviceClass,
T_UHWORD deviceSubClass,
T_UHWORD deviceProtocol
)
功能:每次插上或者移除键盘时由USBD调用
注意:有可能同一个设备会多次插拔,对这种情况USBD会忽略除第一次外的callback
返回值:无
5. T_ MODULE pUSB_KBD_SIO_CHAN createSioChan
(
USBD_NODE_ID nodeId,
T_UHWORD configuration,
T_UHWORD interface
)
功能:给USBD分配的nodeId创建一个新的USB_KBD_SIO_CHAN结构
返回值:成功返回指向该结构的指针,失败返回NULL
6. T_MODULE T_BOOL configureSioChan
(
pUSB_KBD_SIO_CHAN pSioChan
)
功能:配置键盘信息
返回值:成功返回TRUE,失败返回FALSE
7. T_MODULE T_VOID usbKeyboardIrpCallback
(
T_VOID* p /* completed IRP */
)
功能: IRP完成或取消时调用
返回值:无
8. T_MODULE T_VOID interpKbdReport
(
pUSB_KBD_SIO_CHAN pSioChan
)
功能:解释USB键盘的BOOT REPORT,得到键盘扫描码。
返回值:无
⒌ USB HID设备的数据获取方式
必须注意的是这里鼠标和键盘的处理方式完全不一样。
键盘是以轮询方式获得数据。使用如下的函数:
T_MODULE T_WORD usbKeyboardPollInput
(
SIO_CHAN *pChan,
T_BYTE *thisChar /*按键值*/
)
返回值:收到字符返回OK;
设备错误返回EIO;
如果输入缓冲为空返回EAGAIN;
设备只能在中断模式下工作返回ENOSYS.
所获取的按键值都将存放在给键盘分配的对应的USB_KBD_SIO_CHAN结构里的inQueue [KBD_Q_DEPTH]。
而鼠标驱动程序的设计则是在IRP成功返回的时候,直接由函数usbMouseIrpCallback()通过调用函数interpMseReport()取得USB_MSE_SIO_CHAN结构结构里的pReport项。该pReport项即包含的是鼠标按键的状态。
随着USB2.0的发布,USB越来越流行,它已经成为绝大多数PC外设上的标准接口。我们看到,USB的应用开发也在不断发展,不断完善。因此,研究USB技术将具有极大的应用背景和市场前景。