USB协议支持外设热插拔,这些外设分为许多不同类型,每一种设备类都具有相同的动作和类似的功能。比如显示器,鼠标,话筒等等。
HID(Human Interface Device)类提供了人机接口的界面,许多典型的HID类设备具有LED,音频反馈等,以此展现设备信息给用户。
HID设备定义的最基本目的:
详细资料可直接查阅 www.usb.org/hid
对开发来说最主要的定义在HID Usage Tables 1.22
从Host到Device的数据称为Output数据,从Device到Host的数据,称为Input数据。
通常HID设备会有一条控制管道和一条输入中断管道组成。Input中断管道用于设备端发起的HID事件通知并传输到Host端。而控制管道是双向的,既可以由Host发起用于获取设备的数据,也可以用于发送Host端发起的HID事件通知并传输到设备端。通常一些HID设备还会单独支持一条输出中断管道,用于Host端发送较大的数据内容给设备端。
val bytesRead = mConnection!!.bulkTransfer(mInUsbEndpoint, buffer, size, timeout)
/*获取描述符*/
enum class CommonRequest(val value: Int){
//获取描述符
GET_DESCRIPTOR (0x06),
}
enum class HidValue(val value: Int){
/*高字节用于描述符*/
HID_DESCRIPTOR((0x21 shl 8) and 0xFFFF),
HID_REPORT_DESCRIPTOR( (0x22 shl 8) and 0xFFFF),
PHYSICS_DESCRIPTOR((0x23 shl 8) and 0xFFFF),
//!!:reserved 其他高字节保留,可自定义用
/*低字节为非0值时被用来选定实体描述符,即需要或上LOW_BYTE_REPORT_ID*/
INPUT(0x01 shl 8 and 0xffff),
OUTPUT(0x02 shl 8 and 0xffff),
FEATURE(0x03 shl 8 and 0xffff),
PROTOCOL_BOOT(0x00),
PROTOCOL_REPORT(0x01)
//!!:reserved 其他低字节保留,可自定义用
}
//!!常用: 获取HID报告描述符:
Int value = HidValue.HID_REPORT_DESCRIPTOR.value
val ret = mConnection !!. controlTransfer (
RequestType.GET_DESCRIPTOR_FROM_DEVICE.value, CommonRequest.GET_DESCRIPTOR.value, value ,
0x00 , outputBuffer, outputBuffer.size, timeout)
//!!常用: 用于host主动获取设备Input报告:
Int value = HidValue.INPUT.value or reportId!!.toInt()
val ret = mConnection!!.controlTransfer(
RequestType.HID_INTERFACE_FROM_DEVICE.value, HidRequest.GET_REPORT.value, hidValue,
mUsbInterface!!.id , outputBuffer, outputBuffer.size, timeout)
//!!常用: 发送Host端触发的Output报告
Int value = HidValue.OUTPUT.value or reportId!!.toInt()
val ret = mConnection!!.controlTransfer(
RequestType.HID_INTERFACE_FROM_HOST.value, HidRequest.SET_REPORT.value,value,
mUsbInterface!!.id , data, data.size, timeout)
ret = mConnection!!.bulkTransfer(mOutUsbEndpoint, data, data.size, timeout)
HID报告分为Input,OutPut ,Feature三种主要报告。我们一般只用到Input和Output两种报告,Input是设备端触发的报告,可以由设备端主动通知host,也可以由host端主动获取该报告。Output是host端触发的报告,由host直接发给设备端。
HID报告描述符就是用于定义HID报告格式,通过解析HID报告描述符我们便能知道设备端支持的Input/Output/Feature报告的格式。之后设备端和host端都按此约定来组装和解析HID报告。整个通信就能顺利完成。
因为大多数设备支持的功能是相同的,因此USB HID协议便约定了通用类的页码(UsagePage)和通用类编号(Usage). 解析HID报告描述符就是要解析出我们感兴趣的类所在的Report的Id, 以及该类在该Report的哪几位(offset), 这个类的数据类型是怎样的.
注意:有时候无法从控制端口获取到设备的HID报告描述符,可能是设备端Bug也可能是设备端做了限制,枚举完之后不再允许获取描述符,有的时候是设备处理较久,需要加大等待时间调试。
2. 解析出Item后需要跟进Item 的类型判断是Local, Global ,Main. 将Item转换成感兴趣的Usage 与ReportFeild的对应关系表.
相关代码参考UsbHidProxy中的HidReportDescriptorParser,此处仅列出最关键的ItemTag定义以及与Local, Global ,Main的关系. Local, Global ,Main转换成ReportFeild原理可参考USB HID Report Descriptor 报告描述符详解
enum class ItemTag(val value: Byte, val type: ItemType){
//二进制B1111
LONG(0xF,ItemType.UNKNOWN),
/*Hid other tag should be short*/
//B1000
INPUT(0x8,ItemType.MAIN),
//B1001
OUTPUT(0x9,ItemType.MAIN),
//B1011
FEATURE(0xB,ItemType.MAIN),
//B1010
COLLECTION(0xa,ItemType.MAIN),
//B1100
END_COLLECTION(0xc,ItemType.MAIN),
//B0000
USAGE_PAGE(0x0,ItemType.GLOBAL),
//B0001
LOGICAL_MIN(0x1,ItemType.GLOBAL),
//B0010
LOGICAL_MAX(0x2,ItemType.GLOBAL),
//B0101
UNIT_EXPONENT(0x5,ItemType.GLOBAL),
//B0110
UNIT(0x6,ItemType.GLOBAL),
//B0111
REPORT_SIZE(0x7,ItemType.GLOBAL),
//B1000
REPORT_ID(0x8,ItemType.GLOBAL),
//B1001
REPORT_COUNT(0x9,ItemType.GLOBAL),
//B1010
PUSH(0xa,ItemType.GLOBAL),
//1011
POP(0xb,ItemType.GLOBAL),
//B0000
USAGE(0x0,ItemType.LOCAL),
//B0001
USAGE_MIN(0x1,ItemType.LOCAL),
//B0010
USAGE_MAX(0x2,ItemType.LOCAL),
//B0011
DESIGNATOR_INDEX(0x3,ItemType.LOCAL),
//B0100
DESIGNATOR_MIN(0x4,ItemType.LOCAL),
//B0101
DESIGNATOR_MAX(0x5,ItemType.LOCAL),
//B0111
STRING_INDEX(0x7,ItemType.LOCAL),
//B1000
STRING_MIN(0x8,ItemType.LOCAL),
//B1001
STRING_MAX(0x9,ItemType.LOCAL),
//1010
DELIMITER(0xa,ItemType.LOCAL),
//B1011~B1111
RESERVED(0xf,ItemType.LOCAL);
}
enum class ItemType(val value: Byte){
MAIN(0x0),
GLOBAL(0x1),
//B10
LOCAL(0x2),
UNKNOWN(0xf);
fun isThis(itemPreValue : Byte) : Boolean{
val temp = itemPreValue.toInt() and 0xf shr 2
if(this.value.compareTo(temp) == 0){
return true
}
return false
}
companion object{
fun find(itemPreValue : Byte) : ItemType? {
for (temp in ItemType.values()){
if(temp.isThis(itemPreValue)){
return temp
}
}
return null
}
}
}
enum class ItemSize(val value: Byte, val realSizeValue : Int){
ZERO(0x0,0),
ONE(0x1,1),
TWO(0x2,2),
FOUR(0x3,4);
}
#可使用以下命令,插入usb设备后即可打印出解析后的HID报告描述符:
getevent -d
Android 提供了Accessory模式一套完整的APIs,但这部分接口需要驱动按照官方指导文档修改定制部分代码才能支持.同时也对host端APP提出了定制要求. 因此一般适用于host单独适配某一类固定HID device且该HID device功能比较单一的情况(需要驱动协调处理多模块功能的情况,该设计也不适用)下应用. 具体可参考USB HID Report Descriptor 报告描述符详解
总体参考:
www.usb.org/hid
HID设备类定义中文版
调用接口参数说明相关:
关于Android设备USBHID通信控制传输相关接口说明
Android(安卓)系统USB-OTG-HID外设通讯开发
Android usb 控制传输,关于Android设备USBHID通信控制传输相关接口说明
USB设备请求命令详解
理解解析与组装Report参考:
USB描述符解析–>枚举.
USB HID Report Descriptor 报告描述符详解
描述符具体类别值定义参考:
从零开始学USB(二十一、USB接口HID类设备(三)_报表描述符Global类)
【USB笔记】 标准设备请求Standard Device Requests
Android Accessory模式参考
Android下USB Accessory的实现分析
ANDROID: 关于USB ACCESSORY & HID
其他实践参考
Android USB Host 使用详解(U盘)(二)
Android USB Host 使用详解(U盘)(三)
AndroidUSB实现原理参考:
Android的USB系统简单分析之一