天天看点

MTP协议开发入门

由于MTP协议在移动设备中的广泛应用,使其成为在设备互联产品中,必备的组件之一。设想在开车途中,接入车载设备,听听手机中的歌;或者在休息时,借用车载的大屏幕看看手机中的高清电影,这些都使得旅途变得轻松惬意。 

本文尽量避免介绍MTP协议(文档已经写的很清楚),主要针对某个具体设备(Google Nexus 4),介绍MTP开发入门知识。

1. MTP设备模型

理解MTP设备模型要有基础的USB协议知识。MTP设备通过USB Descriptor描述设备的通信端点(Endpoint)。下面列出四太子的USB Descriptor: 

# lsusb -v -d18d1:

Bus 001 Device 003: ID 18d1:4ee1 Google Inc.
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x18d1 Google Inc.
  idProduct          0x4ee1
  bcdDevice            2.28
  iManufacturer           1 LGE
  iProduct                2 Nexus 4
  iSerial                 3 0054700f490d669a
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           39
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    255 Vendor Specific Subclass
      bInterfaceProtocol      0
      iInterface              4 MTP
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x001c  1x 28 bytes
        bInterval               6
Device Qualifier (for other device speed):
  bLength                10
  bDescriptorType         6
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  bNumConfigurations      1
Device Status:     0x0000
  (Bus Powered)
           

可以看出N4设备只提供了一个Configuration,该Configuration提供了一个Interface,而这个Interface有3个Endpoint(bulk in, bulk out & interrupt in),都是用于MTP协议通信的。

2. MTP设备的枚举

MTP协议中,未规定如何判断一个USB设备是否是MTP设备,一般可以采用以下规律: 

1. 通过device descriptor的”bDeviceClass”字段判断是否是USB_CLASS_PTP(6); 

2. 通过udev,查询设备属性是否包含”ID_MTP_DEVICE”; 

3. 枚举所有的Interface,查询其iInterface对应的字符串是否是”MTP”(这个步骤对于多Configuration与多Interface的MTP设备是必须的,因为我们要特别指定USB通信所采用的Configuration与Interface)。

3. MTP通信模型

设备连接后,USB Host扮演的是Initiator角色,动作由Initiator主动发起;而设备扮演Responder,响应Initiator的请求。另外,Responder也可以向Initiator上报一些事件,用于触发Initiator做相应的动作(比如,设备的解锁动作,可以触发Initiator重新获取设备的存储列表)。 

Initiator与Responder的通信通过上面USB Descriptor描述的3个Endpoint完成:Initiator通过bulk out endpoint向设备写入请求,Responder通过bulk in endpoint向Initiator返回请求的数据,而MTP Event则是通过interrupt in endpoint由Responder向Initiator提交事件信息。

4. MTP通信数据封装

MTP的所有数据要以特定的容器进行封装,封装的格式如下:

Byte Offset Length Field Name Description
4 ContainerLength Total amount of data to be sent (including this header)
4 2 ContainerType Defines the type of this container
6 2 Code Operation, Response or Event Code as defined in the MTP specification.
8 4 TransactionID See section 4.3.3 Transaction IDs.
12 ContainerLength-12 Payload The data which is to be sent in this phase.

5. MTP通信的关键过程

  1. Open Session:

    _container->length = sizeof( mtpContainer ) + sizeof( guint32 ); 

    _container->type = USB_CONTAINER_COMMAND; 

    _container->code = OC_OPENSESSION; 

    _container->transactionId = transactionId ++; 

    memcpy( _buf, ( void * )container, sizeof( mtpContainer )); 

    memcpy( _buf + _offset, params, container->length - sizeof( mtpContainer ) );//这里params保存了预先生成的session id 

    libusb_bulk_transfer( mtpObj->handle, mtpObj->bulkOutEp, _buf, container->length, &_len, 0 );//通过libusb的bulk transfer将请求通过bulk out endpoint发出 

    之后要等待设备响应,即读bulk in endpoint,buffer的设置要至少为一个该endpoint的maxPacketSize大小,否则在传输大数据时,会libusb会报overflow,数据会丢失:

    libusb_bulk_transfer( mtpObj->handle, mtpObj->bulkInEp, _header, mtpObj->maxPacketSize, &_len, 0 ); 

    获取的数据要判断返回值,正常情况下code应该与请求的code一致,或者为0x2001,表示请求成功完成,其他情况要进行异常处理。

  2. GetDeviceInfo,通过这个命令可以得到设备的基本信息,包括支持的命令列表、文件类型、属性与事件类型。MTP中所有的字符串都是utf-16编码的,使用其他编码的系统要自行转换。
  3. 获取storageIds,如果是设备已锁定的情况下接入,获取的storage设备个数是0。这时需要等待interrupt in endpoint给出storage add事件,再重新获取设备的storage信息。
  4. 通过GetNumObjects、GetObjectHandles&GetObjectInfo命令,可以获取指定类型文件的个数、Handle和基本信息(如文件名、大小等等)。
  5. 获取指定文件的内容有2种方式:GetObject&GetPartialObject,MTP协议指出要使用GetPartialObject来替换GetObject,因为GetPartialObject支持文件的随机访问。

至此,MTP协议开发入门介绍结束了,libusb的使用,可以参考之前的文章libusb异步中断传输使用说明。结合libusb异步传输,可以实现单线程的多个endpoint的输入处理,简化程序设计。

继续阅读