Android USB HID整理

概述

USB协议支持外设热插拔,这些外设分为许多不同类型,每一种设备类都具有相同的动作和类似的功能。比如显示器,鼠标,话筒等等。
HID(Human Interface Device)类提供了人机接口的界面,许多典型的HID类设备具有LED,音频反馈等,以此展现设备信息给用户。
HID设备定义的最基本目的:

  • 尽可能节省设备的数据空间
  • 允许操作系统忽略未知的信息
  • 使数据定义可扩展
  • 支持嵌套和集合
  • 拥有自身的相关信息,使之适用于一般的软件

详细资料可直接查阅 www.usb.org/hid
对开发来说最主要的定义在HID Usage Tables 1.22

数据传输

从Host到Device的数据称为Output数据,从Device到Host的数据,称为Input数据。
Android USB HID整理_第1张图片
通常HID设备会有一条控制管道和一条输入中断管道组成。Input中断管道用于设备端发起的HID事件通知并传输到Host端。而控制管道是双向的,既可以由Host发起用于获取设备的数据,也可以用于发送Host端发起的HID事件通知并传输到设备端。通常一些HID设备还会单独支持一条输出中断管道,用于Host端发送较大的数据内容给设备端。

Android USB HID整理_第2张图片

Android 传输数据API
  1. Input Endpoint接收Input Data:
val bytesRead = mConnection!!.bulkTransfer(mInUsbEndpoint, buffer, size, timeout)
  1. Control Endpoint 获取描述符或者Report以及发送Host端HID报告
  /*获取描述符*/
  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)
  1. 如果存在Ouput Endpoint接口发送大数据
ret = mConnection!!.bulkTransfer(mOutUsbEndpoint, data, data.size, timeout)
HID报告描述符

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也可能是设备端做了限制,枚举完之后不再允许获取描述符,有的时候是设备处理较久,需要加大等待时间调试。

Android 解析HID报告描述符
  1. 解析Item:
    报告描述符由一条条的Item组成.解析报告描述符的第一步就是要解析出这些Item.每一个Item都包含一个字节的前缀,这个前缀中包含了三个信息–item tag,、item type、item size。Item有两种基本类型:short items and long item。Item的数据部分的长度取决于Item的基本类型。

Android USB HID整理_第3张图片
Android USB HID整理_第4张图片
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);
    }
  1. Android驱动调试工具中已经支持了HID报告描述符的解析. 因此在APP中进行HID报告描述符一般是该APP安装在host端且需求要支持多种类似功能设备做的. 如果单纯只是适配一种固定的USB 设备,只需要直接根据驱动调试工具解析出来的report格式来进行组装和解析report数据即可.
 #可使用以下命令,插入usb设备后即可打印出解析后的HID报告描述符:
 getevent -d
Android Accessory模式

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系统简单分析之一

你可能感兴趣的:(android)