none
USB Bluetoothデバイスの制御について RRS feed

  • 質問

  • お世話になっております。

    現在PCにBluetoothドングルを接続し、USB I/F経由でコマンドを送ることを行っております。

    CreateFileでシンボリックリンクに対しOpenは成功しておりますが、

    その後WriteFileでコマンドを送るとERROR_INVALID_FUNCTIONが返ってきてしまいます。

    int _tmain(int argc, _TCHAR* argv[])
    {
    	HANDLE h;
    	BYTE hci_command[255] = { 0 };
    	DWORD WriteSize = 0;
    	int err = 0;
    
    	hci_command[0]=0x81;
    	hci_command[1]=0x00;
    	hci_command[2]=0x00;
    	hci_command[3]=0x00;
    
    	/**********************/
    	/*** 6&109c578c&0&2 ***/
    	/**********************/
    	/* BTH USBのシンボリックリンク */
    	h = CreateFile("\\\\.\\USB#VID_0A12&PID_0001#6&109c578c&0&2#{0850302a-b344-4fda-9be9-90576b8d46f0}",
    					GENERIC_READ | GENERIC_WRITE,
    					FILE_SHARE_READ | FILE_SHARE_WRITE,
    					NULL,
    					OPEN_EXISTING,
    					0,
    					NULL);
    
    	if (h == INVALID_HANDLE_VALUE) {
    		err = GetLastError();
    		printf("CreateFile err\n");
    	}
    	else
    	{
    		if( WriteFile(h, hci_command, sizeof(hci_command), &WriteSize, NULL) == 0)
    		{
    			err = GetLastError();
    			printf("WriteFile err\n");
    		}
    
    		CloseHandle(h);
    
    	}
    
    	return 0;
    }

    USBView.exeを使用しUSB機器の情報を確認すると、以下のように取得でき、

    Bluetooth仕様のHCI-USBで制御ができそうですがWriteFile()などでコマンドを送ることは

    出来ないのでしょうか。

    Device Descriptor:
    bcdUSB:             0x0200
    bDeviceClass:         0xE0
    bDeviceSubClass:      0x01
    bDeviceProtocol:      0x01
    bMaxPacketSize0:      0x40 (64)
    idVendor:           0x0A12 (Cambridge Silicon Radio Ltd.)
    idProduct:          0x0001
    bcdDevice:          0x3164
    iManufacturer:        0x00
    iProduct:             0x00
    iSerialNumber:        0x00
    bNumConfigurations:   0x01

    ConnectionStatus: DeviceConnected
    Current Config Value: 0x01
    Device Bus Speed:     Full
    Device Address:       0x03
    Open Pipes:              5

    Endpoint Descriptor:
    bEndpointAddress:     0x81  IN
    Transfer Type:   Interrupt
    wMaxPacketSize:     0x0010 (16)
    bInterval:            0x01

    Endpoint Descriptor:
    bEndpointAddress:     0x02  OUT
    Transfer Type:        Bulk
    wMaxPacketSize:     0x0040 (64)
    bInterval:            0x01

    Endpoint Descriptor:
    bEndpointAddress:     0x82  IN
    Transfer Type:        Bulk
    wMaxPacketSize:     0x0040 (64)
    bInterval:            0x01

    Endpoint Descriptor:
    bEndpointAddress:     0x03  OUT
    Transfer Type: Isochronous
    wMaxPacketSize:     0x0000 (0)
    bInterval:            0x01

    Endpoint Descriptor:
    bEndpointAddress:     0x83  IN
    Transfer Type: Isochronous
    wMaxPacketSize:     0x0000 (0)
    bInterval:            0x01


    • 移動 Mike Wang (MSCS) 2012年10月2日 12:59 (移動元:Windows デバイスドライバー開発)
    2012年6月26日 7:03

回答

  • > HCI部分の実装は各スタックを提供しているメーカ依存のため、私がコマンドを無理やり
    > 送っても認識されないということでしょうか。

    HCIの実装依存の問題ではなく、BTスタック上位層との整合が問題です。
    例えばAPIなら1アクションで行えるBTオン(有効化)の操作でもHCIコマンドひとつで
    出来るわけではなく、モード設定やデバイス名設定などの複数のコマンド送信とその
    レスポンスや通知などのシーケンスから成り立っています。こういった処理をBTスタックと
    独立に行うことは極めて困難です。

    またHCIコマンド送信できれば問題解決と考えていませんか?確かにBTモジュールへの
    適切なアクセスが出来てHCIコマンドのデータ構造が正しければコントローラにコマンドは
    届くでしょう。しかしそのレスポンスは誰が処理するのでしょうか?
    例えばBTデバイス検索をしようとHCI_Inquiryコマンドを送信したとして、BTスタックは
    送ってもいないコマンドのレスポンスをエラー扱いしないでしょうか
    さらにコントローラの状態によっては拒絶されるコマンドもあるのですが、現在の状態を
    どのように知るのでしょうか?HCIのコマンドやレスポンス、イベントなどのやり取りを
    全部監視しますか、それとも情報取得のHCIコマンドを発行しますか?

    まあ限定された範囲ならBTスタックと独立にHCIコマンドを発行して大丈夫な可能性は
    ありますが、APIで提供されるような汎用的なデバイス制御は不可能と考えるべきでしょう。

    BTのHCI仕様は以下の"Core Version 4.0"中に記載(規定)されていますので参考まで
    https://www.bluetooth.org/Technical/Specifications/adopted.htm

    2012年7月4日 4:56

すべての返信

  • ワイヤレス デバイスのデバッグはやったことがないので、あてにならないかも知れませんが。。。。

    ご質問に至った背景がよく理解できていなくて恐縮ですが、実現させたいのは以下のどちらでしょうか?

    ☆ Bluetooth ドングル経由で通信を行うワイヤレス デバイスの制御
    ☆ Bluetooth ドングル デバイスの制御

    もし実現させたい機能が上記の前者である場合、Bluetooh ドングルのデバイスをオープンしても意味がないと思います。
    ご提示されているコードでは、Bluetooh ドングル デバイスをオープンされているようですが、このデバイス ハンドルでのアクセス対象はあくまでも Bluetooth ドングル デバイスであって、ワイヤレス デバイスではないと思います。
    ワイヤレス デバイスを制御したのであれば、そのワイヤレス デバイス自身をオープンする必要があると思います。

    つまり。。。
    Bluetooth ドングルとのワイヤレス通信が正常に行われ、ワイヤレス デバイスが正しく認識されているのであれば、デバイス マネージャ上に "Bluetooh デバイス" (あるいは "Bluetooh 通信" etc.) のノードが表示されているはずだと思います。
    ワイヤレス デバイスを制御したいのであれば、そのノードの配下に表示されている実際のワイヤレス デバイス自身をオープンする必要があると思います。

    たとえば、USB メモリ内に保持されているファイルへのアクセスを例にすると。。。
    USB メモリのデバイスをオープンしても、そのデバイス ハンドルでアクセス出来るのは USB メモリ デバイス自身であって、メディア内のファイルにはアクセスできないわけです。
    USB メモリ内のファイルにアクセスしたいのであれば、そのファイルをオープンする必要があるわけですが、このファイルのオープン処理は以下のような経路で処理されるはずです。
    (ちょっと大雑把かもしれませんが。。。)

    ☆ USB メモリ内のファイルへのアクセス経路
    <ファイル システム> → <ボリューム デバイス> → <ディスク デバイス> → <USB ホスト コントローラ> → <USB メモリ>

    つまり USB メモリのデバイスをオープンしても、上記の <ファイル システム> から <ディスク デバイス> の部分は通りませんから、いきなりホスト コントローラにファイル アクセスのリクエストを投げても処理のしようがない。。。ということになるんだと思います。
    (↑これもかなり大雑把な表現ですが。。。)

    ちなみに、上記 USB メモリの例でファイルへの Read / Write アクセスを考えると。。。
    アプリから ReadFile() / WriteFile() でファイルへのアクセスを行った場合、<ファイル システム> から <ディスク デバイス> までは Read / Write リクエストとして渡されますが、それ以降は Device I/O Control リクエストに変換され、最終的には URB (USB Request Packet) として USB メモリ デバイスに渡されることになると思います。
    つまり、USB クライアント デバイスへの Read / Write リクエスト時に USB ホスト コントローラに渡されるのは、通常 Device I/O Control リクエストに変換された後のデータとなるので、USB ホスト コントローラへの直接的な Read / Write リクエストは許容していないと思います。
    実際、USB ホスト コントローラや USB ハブのドライバ (usbehci, usbuhci, usbhub、etc.) の Dispatch Routine を確認すると、Read / Write リクエストに対応する IRP_MJ_READ / IRP_MJ_WRITE は未サポートになっています。
    (USB ホスト コントローラにはルート ハブが実装されているので、USB 通信では必ず usbhub ドライバを経由するはずだったと思います。)
    したがって、ご提示されたコードでの WriteFile() API コールで ERROR_INVALID_FUNCTION が返されるのは、usbhub / usbehci ドライバが Read / Write リクエストをサポートしていないため。。。ということになるんだと思います。

    以上、(参考にならないかも知れませんが)お役に立てましたら幸に存じます。

    2012年6月28日 9:32
  • お馬鹿様

    ご回答ありがとうございます。
    大変参考になりました。

    私の知識が非常に足りていないため、質問も分かりづらく申し訳ないです。

    まず、開発しているアプリケーションの簡単なご説明をさせて頂きます。
    Windows7にBluetoothドングルを接続しており、androidスマートフォンとBluetooth経由(シリアルポートプロファイル)で
    データ通信を行うものを開発しております。

    もともとはWindows標準のBluetoothスタックを使用していたため、Winsockで問題なく実現できていたのですが
    他のBluetoothスタック(例えばBluesoleilやTOSHIBAなどです)がインストールされていた場合にWinsockでは無理なことが分かりました。
    その為、Bluetoothスタックを通さずに直接Bluetoothドングルに対し、コマンド(HCI Commandなど)を送れば
    制御できるのではないかと考えたことが経緯となっております。


    上記より実現させたいことは両方ともです。それぞれを記述しますと、

    ☆ Bluetooth ドングル経由で通信を行うワイヤレス デバイスの制御
     →こちらはandroid側と通信を行うために必要になると考えております。

    ☆ Bluetooth ドングル デバイスの制御
     →こちらは例えば、Bluetoothの設定(他機器からの探索可否設定)などで
      必要があるのではないかと考えております。
      
    アドバイス頂いた「ワイヤレス デバイス自身をオープンする」ということですが
    例えばデバイスマネージャに表示されているGeneric Bluetooth Radioなどを
    オープンするということでしょうか。

    上記に関し1つ疑問がございます。
    Generic Bluetooth RadioはWindows標準スタック時でも、
    TOSHIBAスタック時でも存在しますがそれぞれのドライバは異なっております。
    (TOSHIBAスタック時はTOSHIBAのドライバとなっている)

    その為、どんなBluetoothスタックがインストールされていても通信ができる汎用性を
    考えるとGeneric Bluetooth Radioをオープンし制御を実装しても別のスタックになった際には
    再度プログラムをし直すのかと考えてしまいますが誤っておりますでしょうか。

    質問がわかりづらく申し訳ないです。


    2012年6月29日 0:53
  • 返信が遅くなって申し訳ありません。
    (自分の仕事の方が手一杯だったもので。。)

    > 例えばデバイスマネージャに表示されているGeneric Bluetooth Radioなどを
    > オープンするということでしょうか。

    はい、私の意図したのはその通りです。
    Generic Bluetooth Radio などのデバイスをオープンするために CraeteFile() API で必要となる名前は、複数の Setup API を組み合わせれば取得することが可能であると思います。
    この名前の取得方法に関しては、WDK で提供されている usbsamp や osrusbfx2 等のサンプルの、ユーザ モード側のソースに実装例がありますので、そちらを参考にされる良いと思います。
    また Setup API の実装方法に関する詳細は、WDK に DevCon というツールがソース コード付きでありますので、そちらが参考になると思います。
    (もっとも、usbview をお使いになられているので、Setup API に関しては熟知されているとは思いますが。)

    ですが(Bluetooh に関しては全く無知の分際で大変恐縮ですが。。。)、以下は釈迦に説法になってしまうと思いますが、ご容赦ください。
    ご質問の元になった問題は、Bluetooth スタックを提供するドライバの違いにより発生しているんだと思いますが、この違いによりアプリが動かないということは、その問題をアプリレベルで吸収することは無理。。。ということだと思うのです。
    そもそもドライバの存在意義のひとつに「互換性」があると思います。
    異なるデバイスであっても、共通のアーキテクチャに基づいたドライバ設計になっていれば、アプリ側がそれを意識する必要はなくなります。
    たとえば大昔の USB UHCI/OHCI の様に、異なる仕様のデバイスであっても、usbuhci/usbohci ドライバが共通のアーキテクチャに基づいた設計になっているので、アプリ側はその違いを意識しなくて済みます。
    ですが今回の問題は、Microsoft 製とそれ以外の 3rd ベンダ製のドライバで、共通のアーキテクチャが存在しないため、アプリが動かない問題が発生しているように思います。
    つまり互換性の無い二者の間に、汎用性を実現させることは不可能だと思うのです。

    仮に Bluetooth ドングルに対して直接 HCI コマンドを送ることが出来たとしても、その先の I/O の処理過程で <Microsoft 製> or <3rd ベンダ製> のドライバが介在することになると思います。
    したがって、いずれにせよドングル デバイスに依存して、異なる Buletooh スタックを経由せざるを得ないのでは。。。と思うのです。
    そうなると、いくらアプリ側で汎用性を求めたところで、不可能な気がするのです。

    ですので個人的には、個々の Bluetooh スタックに対応してアプリ自体の実装を変更する以外に、対応方法は無いと思います。

    お役に立てそうになれず、申し訳ありません。。。

    2012年6月29日 12:06
  • 誤解があるようなのでちょっと解説いれときます

    > どんなBluetoothスタックがインストールされていても通信ができる汎用性

    これは現存するBlueToothスタックでは不可能な話です。
    もともとWinsockでできていたということですが、これはMSのBluetoothスタックが
    そういうI/Fを提供していたから実現できただけの話です。(ちょっと語弊あり)
    極端な話、「BTデバイス検索や接続は専用ユーティリティでだけ可能、SPPは
    ユーテリティで割り当てたCOMポート上の通信のみ可能」というスタックもありな訳で。

    まずは要望が実現可能なものか、使用BTスタックの提供APIを調査すべきかと。

    2012年6月30日 9:29
  • お馬鹿様、ホーミン様

    ご回答ありがとうございます。
    確認が遅くなってしまい申し訳ございません。

    ホーミン様がおっしゃっているようにWinsockで実現出来るのは、
    MSのBluetoothスタック前提というのは理解しております。

    その為、TOSHIBAスタックでしたらTOSHIBAのSDKを使用すれば
    制御できますし、Bluesoleilなどその他のスタックでも制御するためのSDKが
    公開されております。

    今回私が考えているのはBluetoothの通信のブロック図の中で
    HCIという層がありHCIは接続形態により、USBやUARTなどの通信で
    制御ができるようになっていると思います。

    HCIはどこのBluetoothスタックであろうがBluetooth仕様に則り
    制御しているはずですので直接USBに命令を送れば制御できるのではないかと
    考えました。

    上記より自分自身の考えを再度整理いたしますと、
    今の困っている点は以下となります。

    ・Bluetooth USB機器に何かしらのコマンドを送ることができないか。
     →下記にようにUSBのEndpointは割り当てられているため、
      出来るのではと考えております。
     
     Endpoint Descriptor:
     bEndpointAddress:     0x81  IN
     Transfer Type:   Interrupt
     wMaxPacketSize:     0x0010 (16)
     bInterval:            0x01


    2012年7月3日 0:43
  • HCIはBTモジュール(コントローラというべきかも?)の制御を行うためのインターフェースです。
    USBやSDといったI/Fの制御(例えば電源制御)以外は全てHCI経由でおこなわれます。
    これはBTスタックが管理するもので他から直接アクセスするものではありません。
    仮にBTスタックを経由せずBTモジュールにアクセス出来、HCIコマンドが送れたとしても
    BTスタックからのHCIコマンドとの整合が取れない(保証できない)ので意味がありません。

    BTスタックを自作するか、ダミーのHCIを作成してHCIのコマンド/レスポンスをフックをすれば
    技術的には任意のHCIコマンド送信が実現可能ですが非現実的でしょう。

    2012年7月3日 3:49
  • ホーミン様

    ご回答ありがとうございます。

    ご説明頂いた内容で理解できました。
    ありがとうございます。

    現実的ではないとのことですが、
    1点だけお教えください。

    > 仮にBTスタックを経由せずBTモジュールにアクセス出来、HCIコマンドが送れたとしても
    > BTスタックからのHCIコマンドとの整合が取れない(保証できない)ので意味がありません。

    この点ですが、Bluetoothの仕様でHCIコマンドの型は決まっているのに
    「BTスタックからのHCIコマンド」と「私が割り込ませようとしているHCIコマンド」で
    整合がとれないのはなぜでしょうか。

    Bluetoothホストとコントローラ間での通信がBluetooth仕様として統一されているが
    HCI部分の実装は各スタックを提供しているメーカ依存のため、私がコマンドを無理やり
    送っても認識されないということでしょうか。

    お忙しい中申し訳ございませんが、
    ご回答よろしくお願いいたします。
    2012年7月3日 6:23
  • > HCI部分の実装は各スタックを提供しているメーカ依存のため、私がコマンドを無理やり
    > 送っても認識されないということでしょうか。

    HCIの実装依存の問題ではなく、BTスタック上位層との整合が問題です。
    例えばAPIなら1アクションで行えるBTオン(有効化)の操作でもHCIコマンドひとつで
    出来るわけではなく、モード設定やデバイス名設定などの複数のコマンド送信とその
    レスポンスや通知などのシーケンスから成り立っています。こういった処理をBTスタックと
    独立に行うことは極めて困難です。

    またHCIコマンド送信できれば問題解決と考えていませんか?確かにBTモジュールへの
    適切なアクセスが出来てHCIコマンドのデータ構造が正しければコントローラにコマンドは
    届くでしょう。しかしそのレスポンスは誰が処理するのでしょうか?
    例えばBTデバイス検索をしようとHCI_Inquiryコマンドを送信したとして、BTスタックは
    送ってもいないコマンドのレスポンスをエラー扱いしないでしょうか
    さらにコントローラの状態によっては拒絶されるコマンドもあるのですが、現在の状態を
    どのように知るのでしょうか?HCIのコマンドやレスポンス、イベントなどのやり取りを
    全部監視しますか、それとも情報取得のHCIコマンドを発行しますか?

    まあ限定された範囲ならBTスタックと独立にHCIコマンドを発行して大丈夫な可能性は
    ありますが、APIで提供されるような汎用的なデバイス制御は不可能と考えるべきでしょう。

    BTのHCI仕様は以下の"Core Version 4.0"中に記載(規定)されていますので参考まで
    https://www.bluetooth.org/Technical/Specifications/adopted.htm

    2012年7月4日 4:56
  • ホーミン様ありがとうございます。

    1つ前の回答でご説明頂いていたため、
    HCIコマンド送信するだけで解決とは思っておりませんでした。

    ただ、HCI部分の仕様が同じなのになぜ整合性がとれないのかと
    疑問に思ったため追加でご質問させて頂きました。

    Bluetoothスタックを1から開発するのはさすがに厳しいため、
    アプリケーションの複数スタック対応はそれぞれのSDKを
    利用し行おうと考えております。

    > BTのHCI仕様は以下の"Core Version 4.0"中に記載(規定)されていますので参考まで
    > https://www.bluetooth.org/Technical/Specifications/adopted.htm

    情報ありがとうございます。
    今回はBluetooth v2.1 + EDRのドングルを使用していたため、
    仕様書はCoreV2.1 + EDRを参照しておりました。


    2012年7月4日 5:45
  • kende 様、問題が解決したとのことなので、一旦回答としてマークさせて頂きます。

    もしもさらに別の質問がある場合には、新たにスレッドを作られることをお勧めします。

    2012年7月17日 14:04
    モデレータ