Saturday, October 11, 2014

USB PTP/MTP 簡介

MTP其實就是PTP的延伸,所以這兩個是一樣的東西。

MTP之前幾年幾乎沒有人在用。有啦,Pictbridge是架在PTP之上,所以PTP還有存在的價值,但是近幾年來Pictbridge也漸漸地消失在市場中了....理由我猜是相容性問題吧。


MTP的用途跟MSC類似,不同的地方wiki解釋的很清楚,我就不講了。


PTP規格是ISO標準(ISO15740),提到ISO,第一個就要想到[錢]。是的,下載它是要錢地。但是呢,MTP規格下載是不用錢的...。所以現在大家都用MTP規格在玩。


請注意一點,MTP只是個資料交換的通訊協定,所以要透過哪種載具(transport)去傳輸這些資料,是每個載具要定義的,如果要透過USB傳輸MTP資料,請參考[Still Image Capture Device Definition]這份規格書。跟MSC很像,這份文件只定義MTP資料要透過那些Endpoint傳輸,以及容器的格式。資料內容的話,請參照[Media Transfer Protocol]這份規格書。


先談[Still Image Capture Device Definition]。


這份文件很簡單,MTP主要透過Bulk-In,Bulk-In,跟 Interrupt-In來傳輸資料。一般情況下,Bulk-In跟Bulk-In就夠了,Interrupt-In主要是提供一些即時資訊給HOST知道,沒提供也不會怎樣。


每筆MTP資料之前都要加上一個header,這邊叫container,格式如下:


基本上HOST一定是發Command Block,Device回Response Block,Data Block則是看哪一方要發資料,這沒甚麼好說的。


Class Request則是有四個,Cancel、Get Extended Event Data、Device Reset、跟Get Device Status。

  • Cancel有點特別,它通常用來取消某次的傳輸。為什麼說他很特別?因為這是MTP特有的問題。在MTP規格內,如果要傳一個大檔案,因為要等很久,使用者有可能會在中途取消。問題來了,MTP規格沒有定義取消方式,所以只能在Transport Layer做這個處理。
  • Get Extended Event Data:我沒用過...。
  • Device Reset:沒有甚麼特殊。
  • Get Device Status:我看過唯一有用的是在Cancel之後,HOST會用這個Request來問狀態。
這四個Class Request的使用時機其實沒甚麼標準,跟HOST作業系統要怎麼運用有關,所以準備好USB Analyzer,然後準備好相容性測試吧。

接下來是[Media Transfer Protocol]。


MTP資料格式全部都是little endian,比較特殊的有字串跟Array:



  • 字串:它是所謂的Pascal String,但是用的是unicode 16 little endian編碼。第一個byte表示後面有幾個[字元],這個字串還必須包含'\0'這個字元。跟strlen()不同,要小心。
  • Array:第一個integer表示後面還有幾個element。
  • 日期:它是個字串,格式是"YYYYMMDDThhmmss.s"。既然它是個字串,所以它也遵循字串的編碼規定。".s"可有可沒有。


基本上MTP的流程是Command -> Data -> Response。在MTP規格裡,Command稱為[Operation]。而每個物件(或稱為檔案)都必須有個獨一無二的ID,規格稱為[Handle],整個通訊協定裡,雙方基本上是在處理這些Handle。請注意,物件並不是只有檔案,還包含目錄,這就是為什麼要叫物件而非檔案。

透過這些資訊,大概的物件交換協定也可以猜得出來。我這邊直接用規格來說明:
  • HOSTGetObjectHandles命令取得DEVICE端的物件列表。
  • HOST取得列表後,可以用GetObjectInfo命令取得每個物件的資訊。
  • HOST可以用GetObject命令取得該物件的資料內容。
  • 如果要取得某個物件的縮圖,可以用GetThumb命令。
  • 要刪除某個物件,可以使用DeleteObject命令。
  • GetObjectPropSupportedGetObjectPropValueGetObjectPropDesc則是MTP特有命令,它可以支援更多關於物件的屬性。因為GetObjectInfo的屬性是固定的,所以只能用額外的命令去新增屬性。
當然,當USB MTP裝置一插上HOST時,HOST可以用GetDeviceInfo取得這個裝置的資訊,並用GetDevicePropDescGetDevicePropValue取得這個裝置支援的屬性。
等等,上面沒提到如何寫入物件到裝置啊?這有點複雜,因為物件一向都是裝置提供對應的IDHOST根本沒法存取一個不存在的物件啊?所以這個動作要分兩個命令,第一個是SendObjectInfoDEVICE說要新增一個物件以及它的屬性,然後DEVICE會把這個物件的ID放在Response內,之後HOST再用SendObject把這個物件的資料傳給DEVICE。這樣做很引發一些問題,例如不能重傳或從某個地方開始傳。傳到一半要取消也是很麻煩。

實作MTP基本上不難,但是有幾點很麻煩:
  • 相容性問題。這個基本上無解,因為MTP是微軟主推的規格,其他作業系統可能不太想支援太多。就我所知,至少MAC OS對它的支援就不是太好。有一些行為跟Windows差很多。
  • 多檔案處理:因為所有物件都是用Handle ID來表示了,所以DEVICE必須處理Handle ID所對應的實體檔案轉換。所以一個映射表免不了,當檔案多到99999時候,這個映射表會有多大?存取速度呢?
  • WHCK。這個真的很煩。

No comments:

codeblock