天天看點

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的輸入處理,簡化程式設計。

繼續閱讀