CatHand  Development

■ USB機器の制御 ■
 ここではUSB機器を制御する基本的な方法を紹介します.
 とはいえ私はこーゆーことを専門にやってる者でもなく(今後はわかりませんが),Appleのドキュメント読んで実際にコード書いてみて動きましたよってだけなので,何か間違ってたら容赦なく突っ込んでください.

 はじめにとりあえずここ読んどきましょう.USB Device Interfaceの基本的なことが書いてあります.
 あとこのへんにあるUSB SDKをインストールしましょう.Universal Serial Bus (USB) DDKではなく,Mac OS X USB Software Development Kit (SDK)です.前者はOS9用みたいです.
 で,これを入れるとたぶん/Developer/Examples/IOKit/usb/にUSBSimple Exampleってのができてると思います.今回はこれ使って説明します.

 USBデバイスにはマウスからハードディスクまであります.当然それぞれの機器の制御の仕方も違います.機器によってはデバイスドライバが必要なものもありますが,例えばマウスなんかはたいていのものは挿しただけで動きますね.
 そーゆーものはMacOSXが標準でサポートしてくれているわけですが,例えばトランスバイブレータなんかは当然ドライバはありませんし,挿しても動きません.ですが,ドライバレベルでは認識していて,コマンドを送ることができるよーになっています.
 USB SDKをインストールすると/Developer/Applications/にUSB Proberってのがインストールされます.これは現在繋がっているUSBデバイスの表示します.例えばトランスバイブレータですと,
 
 こんなかんじになります.
 今回はこれを動かしてみましょう.

 とりあえずUSBSimpleExample.pbprojを開いてみましょう.
 ソースコードはmain.cのみです.main()関数から見て行きましょう.変数宣言でVenderIDとProductIDを設定しています.この2つのIDでUSB機器を特定するのです.
    SInt32			idVendor = 1351;
    SInt32			idProduct = 8193;
 今回はトランスバイブレータを動かすので,トランスバイブレータのIDに変えておきましょう.IDはさきほどのUSB Proberを見ればわかります.VenderIDが0x0B49,ProductIDが0x064Fですね.
 ここを変えたらとりあえずビルドして実行してみましょう.
 トランスバイブレータが挿してあれば,結果はたぶんこんなかんじだと思います.
Found device 0xe03
dealWithDevice: found 1 configurations
found interface: 0x1103
dealWithInterface: found 1 pipes
dealWithPipes: skipping pipe 1 because it is not a bulk pipe

USBSimpleExample はステータス 0 で終了しました。
 このような結果になれば,コマンドを送る準備はだいたいできました.

 ではmain.cの続きを見て行きましょう.
 大きな流れとしては,main()→dealWithDevice()→dealWithInterface()→dealWithPipes()のようになります.
 main()では,VenderIDとProductIDからUSBデバイスへの参照を取得してdealWithDevice()へ渡しています.
 dealWithDevice()では主にIOUSBDeviceInterfaceクラスを取得して様々な情報を取得,設定して,USBインタフェースへ参照を取得してdealWithInterface()へ渡しています.
 dealWithInterface()では,IOUSBInterfaceInterfaceクラスを使ってPipeを取得してdealWithPipes()へ渡しています.
 dealWithPipes()ではBulkPipeを用いてバルク転送を行っています(コメントアウトされています).トランスバイブレータにはBulkPipeがないため,先程実行した時には転送はスキップされました.
 このサンプルコードはCで書かれていますが,IOUSBDeviceInterfaceやIOUSBInterfaceInterfaceはオブジェクト指向のクラスのような振る舞いをしているようです.
 IOUSBDeviceInterfaceやIOUSBInterfaceInterfaceのAPIはここを参照してください.

 今回はとりあえず動かすのが目的ですので,main()からdealWithInterface()の内容は「おまじない」と思っておいて…,dealWithPipes()を見てみます.
 さっき実行した時最後の出力は
dealWithPipes: skipping pipe 1 because it is not a bulk pipe
 でしたね.ということは,
	if (transferType != kUSBBulk)
	{
	    printf("dealWithPipes: skipping pipe %d because it is not a bulk pipen", i);
	    continue;
	}
 の部分で終了したということです.バルク転送の部分もコメントアウトされてますし,dealWithPipes()ではあんまりたいしたことしていないので,dealWithInterface()へ戻ってみます.
 dealWithPipes()を呼んでいるのは,
    if (numPipes)
	dealWithPipes(intf, numPipes);
 この部分です.逆にいうとここまででintfにはIOUSBInterfaceInterfaceを使う準備がしっかりできているということです.ということでここをコメントアウトして,コードを追加していきます.

 トランスバイブレータを動かすにはUSBインタフェースの0番のPipeへControlRequestを送ります.USBのインタフェースには必ず0番のPipeがあるみたいです.
 ControlRequestを送るには,IOUSBInterfaceInterfaceのControlRequest()を使います.引数は*self,pipeRef,*reqということになっていますが,selfはintf,pipeRefは0番目なので0でいいとして,残りは*reqになります.
IOUSBDevRequest *req 
 ということなのでIOUSBDevRequestを見てみます.この部分でコマンドを設定するわけです.
 コマンドは機器によって違うわけですが,トランスバイブレータの例でいいますと,
    req.bRequest=1;			//1に設定
    req.wValue = 0xFF;		//256段階で強さを設定
    req.wIndex = 0;			//設定しない
    req.wLength = 0;		//設定しない
    req.wLenDone = 0;		//設定しない
 となります.pDataは
    unsigned char iobuff[32];
 などとしてポインタを渡しておきましょう.今回は特に関係はありません.
 残るbmRequestTypeはUSBmakebmRequestTypeで作ります.第一引数は
enum {
	kUSBOut			= 0,
	kUSBIn			= 1,
	kUSBNone		= 2,
	kUSBAnyDirn		= 3
};
 となっています.コマンドを送りますのでkUSBOutを指定します.第二引数は
enum {
	kUSBStandard		= 0,
	kUSBClass		= 1,
	kUSBVendor		= 2
};
 となっています.Vender SpecificですのでkUSBVendorを指定します.第三引数は
enum {
	kUSBDevice		= 0,
	kUSBInterface		= 1,
	kUSBEndpoint		= 2,
	kUSBOther		= 3
};
 となっています.Interfaceへコマンドを送りますのでkUSBInterfaceを指定します.

 これで全て設定できました.先程コメントアウトした部分を下記と置き換えてビルドして実行してみましょう.
    {
    	IOUSBDevRequest req;
    	unsigned char iobuff[32];
    
    	req.bmRequestType=USBmakebmRequestType(kUSBOut,kUSBVendor,kUSBInterface);
    	req.bRequest=1;
    	req.wValue = 0xFF;		//最大振動
    	req.wIndex = 0;
    	req.wLength = 0;
    	req.wLenDone = 0;
    	req.pData = iobuff;

    	err = (*intf)->ControlRequest(intf,0,&req);
    	if(err) printf("ControlRequestErr=%Xn",err);
    }
 トランスバイブレータが振動すれば成功です.
 振動したまま止まらないと思いますので,wValue=0x0としてもう一度実行して止めましょう(^^;

 すごーく簡単に説明しましたが,基本的な方法はこんなかんじです.
 コマンドを送るまでの個々の関数についてはAppleのドキュメントを見てみると参考になると思います.
 また,何かエラーを吐いた場合は,ここや,USB.hを参照してください.