WinUSBドライバの自動インストール
WindowsにはWinUSBというドライバフレームワーク(?)があります。これを使えば、 自作USBデバイスと通信するドライバを自作する必要なく、 アプリケーションレベルでバルク転送などの通信を行うことができます。
というのは組み込み業界では良く知られていると思います。でも、 比較的最近可能になった裏技(?)についてはあまり知られていないのではないかと思います。
これまでは、WinUSBのドライバをインストールするためにinfファイルを作ったりする必要がありました。 でも実はWindows8以降からは、infファイルさえ必要なく、デバイスを接続するだけでドライバをインストールすることが可能になっています。
つまり、ドライバ署名なども一切気にする必要はないのです。ドライバ署名のチェックを無効にして再起動するという前時代的な作業は不要です。
これを実現するには、USBデバイス側にも少し変更が必要です。具体的には、 Microsoft OSディスクリプタというものを作成し、Windowsからのリクエストに応じて返すようにします。
Microsoft OS descriptorには2種類存在します。
- 標準USBストリングディスクリプタ。OS string descriptorと呼ばれます。 このディスクリプタにより、デバイスから(次に述べる)OS feature descriptorを取得可能であるとOSが判断します。
- OS feature descriptor: デバイスは1つまたは複数のOS feature descriptorを持つことができます。
OS feature decriptorを取得するための手順は次のようになります。
- まず、WindowsがOS string descriptorを取得するためのコントロール要求を送る
- Windowsは有効なOS string descriptorであることを検証する
- WindowsはOS string descriptorのbMS_VendorCodeフィールドの情報を用いて、OS feature descriptorを取得する
OS feature descriptorには次の種類があります。
- Extended Compat ID この情報に基づき、Windowsはどのドライバをロードするかを決定します。
- Extended Properties
- Genre これはHIDデバイスによって使用される(予定)。
とりあえず、上記二つのExtended Compat ID, Extended Propertiesのディスクリプタを準備すれば、 ドライバの自動インストールと、GUIDによるアプリケーションからの通信が可能になります。
OS string descriptor
OS string descriptorは標準ストリングディスクリプタの、string index 0xEEに格納されます。 OS string descriptorはデバイスにつき1つのみ持つことができます。
OS string descriptorを取得するために、GET_DESCRIPTORコントロール要求がデバイスに送られます。
- bmRequestType=0x80
- bRequest=GET_DESCRIPTOR
- wValue=0x03EE
- wIndex=0x0000
- wLength=0x12
OS string desciptorの要求に応じる場合、デバイスはDataフィールドにdescriptorを返します。 Version 1.00のOS string descriptorは18バイトの固定長であり、次から成ります。
- bLength (1Byte): 0x12
- bDescriptorType (1Byte): 0x03
- qwSignature (14Bytes): ‘MSFT100’
- bMS_VendorCode (1Byte): Vendor-specific
- bPad (1Byte): 0x00
qwSignatureはUnicode文字です。
bMS_VendorCodeは、関連するfeature descriptorを取得するために以降用いられます。
(Windows 8とWindows Server 2012では、USB3.0複合デバイスでbMS_VendorCodeに0x00を指定するものは、列挙に失敗します)
続いて、OS feature descriptorを取得するためにGET_MS_DESCRIPTORリクエストが送られます。
- bmRequestType=0xC0(デバイスの場合), 0xC1(インタフェースの場合)
- bRequest=GET_MS_DESCRIPTOR(この値は上記bMS_VendorCode)
- wValue=0x0000(デバイスの場合。インタフェースの場合、下位バイトがインタフェース番号)
- wIndex=要求されるOS feature descriptorの種類。次の値のいずれか
- Genre: 0x0001
- Extended Compat ID: 0x0004
- Extended Properties: 0x0005
- wLength=Dataフィールドのバイト数。最大4KB。初回の要求では、wLengthは0x10に設定され、デバイスはヘッダ部分のみを返します。
Extended compat ID OS feature descriptor
デバイスは1つのExtended Compat IDディスクリプタしか持てません。 このディスクリプタは、固定長のヘッダーセクションと、ヘッダーセクションに続く1つ以上の機能セクションから成ります。
各機能セクションは、特定のインタフェース、機能、インタフェースの機能グループと関連します。 デバイスが返すbCountフィールドは機能セクションの数となります。
Windowsは、まずExtended Compat IDディスクリプタのヘッダ部のみを要求し、ヘッダのdwLengthフィールドを用いて、 再度GET_MS_DESCRIPTORリクエストを送ります。このときのwLengthにはdwLengthの値が入ってきます。
ヘッダーセクションは
- dwLength (4bytes)
- bcdVersion (2bytes)
- wIndex (2bytes)
- bCount (1byte)
- RESERVED (7bytes)
から成ります。
extended compat ID descriptorの場合、wIndexは上記のとおり0x0004です。bcdVersionは、1.00だと0x0100にします。 RESERVEDは0x00で埋めます。
機能セクション
機能セクションは
- bFirstInterfaceNumber (1byte)
- RESERVED (1byte)
- compatibleID (8bytes)
- subCompatibleID (8bytes)
- RESERVED (6bytes)
から成ります。最初のRESERVEDは0x01固定です。最後のRESERVEDは0x00で埋めます。 機能セクションはヘッダーセクションの後に、インタフェース番号順に続きます。 したがって、最初の機能セクションのbFirstInterfaceNumberは0x00となります。
WinUSBドライバを関連付けたい場合、compatibleIDは0x57 0x49 0x4E 0x55 0x53 0x42 0x00 0x00を入れます。 これはASCII文字列’WINUSB’と、残りを0x00で埋めたものです。 WinUSBの場合、subCompatibleIDは全て0x00で良いようです。
Extended Properties OS feature descriptor
次に、Extended Properties OS featureディスクリプタでGUIDを指定することで、 アプリケーションが当該GUIDを指定することで、 接続されているデバイスと通信するためのエンドポイントを開くことができるようになります。
このディスクリプタもヘッダーセクションとカスタムプロパティセクションから成ります。
ヘッダーセクションは
- dwLength (4bytes)
- bcdVersion (2bytes)
- wIndex (2bytes)
- wCount (2bytes)
から成ります。これに続くカスタムプロパティセクションは
- dwSize (4bytes)
- dwPropertyDataType (4bytes)
- wPropertyNameLength (2bytes)
- bPropertyName (上記wPropertyNameLength bytes)
- dwPropertyDataLength (4bytes)
- bPropertyData (上記dwPropertyDataLength bytes)
となります。GUIDを指定するためには、上記bPropertyNameに’DeviceInterfaceGUID’を指定し、 bPropertyDataに所望のGUIDを指定します。両方ともUnicode文字列です。 dwPropertyDataTypeは0x00000001を指定します。
続きはパッチで
Extended Properties OS feature descriptorの残りは、 Zynqで動作するUSBデバイスの改造方法のパッチを置いておきますので、そちらで確認してください。
これは、Vivadoを標準的な場所にインストールした時に、 C:\Xilinx\SDK\2017.1\data\embeddedsw\XilinxProcessorIPLib\drivers\usbps_v2_4\examples にインストールされている、USB Mass Storageとして動作するファームウェアに対するパッチとなります。 この改造により、Mass Storageとしてではなく、 バルクIN, OUTエンドポイントを一つずつ持つWinUSBデバイスとして見えるようになります。 あとはバルク転送のファームウェアの部分を改造すれば、PC側のソフトウェアと合わせて任意の通信ができるようになります。
ちなみに、Windowsはデバイスのディスクリプタをキャッシュしてしまうので、“多分動作するように修正したはずなのに、 なぜか動作しない"といった場合、とりあえずVendor ID/Product IDを変更することで、 別のデバイスとしてWindowsに認識させると動いたりします。