2012年3月6日火曜日

WinUSB_ControlTransfer()が動かない理由

結局は自分のしょーもないポカミスなんだけれど、ある掲示板で同じようなことを質問していた人がいたのと、ちょっと気づきにくいミスなので、備忘録の意味も含めて書き留めておく。

WinUSBを使ってUSB通信を行うとき、ベンダspecificなリクエストを送信するコントロール転送を行うのに、
SendVendorSpecificRequest_in()SendVendorSpecificRequest_out()と名前の関数を用意してこれを呼び出すようにしていた。
問題の始まりはコントロールOUT転送を行うSendVendorSpecificRequest_out()関数の引数の型だった。

これは送信を行う関数になるので、DATAステージのデータをconst void* なポインタで渡すようプロトタイプ宣言していた。つまり、

bool SendVendorSpecificRequest_out(
  ...,
  const void* dat, // DATAステージで出力するconstなデータ
  int len,      // DATAステージで出力するデータ長さ
  ...);

という感じ(他の引数は省略)。
こういう関数を用意すると、この関数の呼び出し側では

static const char text_data[] = "hogehoge";


SendVendorSpecificRequest_out(
   ...,
   text_data,
   strlen(text_data),
   ...);

みたいな呼び出しができる訳だけれど、これが間違い。
それはこの関数の中身の実装がこうなっていたから。

BOOL SendVendorSpecificRequest_out(
   ...,
   const void* dat,
   int len, ...)
{

   WINUSB_SETUP_PACKET setup_pkt;
   setup_pkt.RequestType = 0x40 or 0xC0;
   setup_pkt.Request = リクエストコード;
   setup_pkt.Value = wValueの値;
   setup_pkt.Index = wIndexの値;
   setup_pkt.Length = len;
   ULONG sent;
   return WinUsb_ControlTransfer(
              WinUSBハンドル,
                      setup_pkt,
                      (PUCHAR)dat,
                      len,
              &sent,
              NULL);

}

WinUsb_ControlTransfer()の第三引数を無理矢理PUCHARにキャストしているところが間違い。
コントロールOUT転送だからWinUsb_ControlTransfer()関数は第三引数で示されるポインタを使ってreadしか行わないだろう(=read属性のみのメモリを渡してもキャストしておけば大丈夫)と思っていたら、内部の実装はそうではないようだ。コントロールOUT転送でもここで渡すポインタは必ずread-write可能なメモリのポインタを渡さなければならない。
という訳で回避策は、WinUsb_ControlTransfer()関数に渡す前に別のread-write可能なバッファにコピーしてから呼び出すか、そもそもSendVendorSpecificRequest_out()関数のプロトタイプからconst修飾子自体を取ってしまって、constなポインタは渡せないようにするか・・・。

0 件のコメント:

コメントを投稿