FS01BU は初期状態では Mass Storage (USB メモリ) である
FS01BU を単純に USB ポートに差し込むと Mass Storage であるとだけしか認識されない。モデムになってほしい。どうやら Linux では usb-modeswitch なるものをインストールするとベンダー特有のコマンドを発行してモードを切り替えることが出来るらしい。
usb-modeswitch は使ったことがないので上のリンク先を見てもらうことにして、ここでは Zynq で直に USB しりあるに切り替えてみたい。
USB-modeswitch から情報得る
USB-modeswitch のサイトから usb-modeswitch-data をとってくる。このファイルを展開すると、usb_modeswitch.d というディレクトリになにやら ID をそのまま名前にしたファイルがある。FS01BU は 1c9e:98ff 。
# Telewell TW-3G HSPA+, FS01BU 3G, SmartBro WM66E
TargetVendor=0x1c9e
TargetProductList="6801,9801,9803"
MessageContent="55534243123456780000000080000606f50402527000000000000000000000"
55 53 42 43 は CBW のパケットだ。この Mass Storage 用のパケットを送ってやればシリアルに切り替わるに違いない。
まずは普通に GET DESCRIPTOR (DEVICE)
12
01
00 02
00
00
00
40
9e 1c (Vendor ID = 0x1c9e)
ff 98 (Product ID = 0x98ff)
00 00
03
02
04
01
作業は Zynq で行った。過去の記録が参考になる。([FS01BU (Mass Storage)を解析してみた] (http://qiita.com/ryos36/items/f0daa0c61b68f03602f4) )
Bulk 転送でデータを投げてみる。
Bluk 転送で上のデータを endpoint 1 になげてみる(OUTトランザクション)。
dbp[0] = 0x55;
dbp[1] = 0x53;
dbp[2] = 0x42;
dbp[3] = 0x43;
dbp[4] = 0x12;
dbp[5] = 0x34;
dbp[6] = 0x56;
dbp[7] = 0x78;
dbp[8] = 0x0;
dbp[9] = 0x0;
dbp[10] = 0x0;
dbp[11] = 0x0;
dbp[12] = 0x80;
dbp[13] = 0x00;
dbp[14] = 0x06;
dbp[15] = 0x06;
dbp[16] = 0xf5;
dbp[17] = 0x04;
dbp[18] = 0x02;
dbp[19] = 0x52;
dbp[20] = 0x70;
for( i = 21 ; i < 32; ++i ) {
dbp[i] = 0;
}
転送するバイト数は 31 バイト。(上では 32 バイト目まで初期化しているが)00 も必要なので注意(21 バイトだと完了しない)。
通常は CBW を送るとその後データのやり取りやら、CSW の受信などをするのだが、このケースでは FS01BU は突然状態を変える(みたい)。具体的には USBSTS(0x144) の PCD ビット(2ビット目) を 1 にする。PCD = Port Change Detect。(Zynq の仕様書では PCI になっている気が、、、ついでに書くと OUT が DUT になってたりもする)。
ポートの活性化
更に、PORTSC1 の状態を xmd 見ると次のようになっている。
XMD% mrd 0xe0002184
E0002184: 8000180B
これをざっくり読むと Full Speed で Port Enabled Change が 1 になっている。Port Enable は 0 でまだ初期化されていない。そこで、0x100 を書き込んでポートを使えるようにする。その結果、PORTSC1 は次のようになる。
XMD% mrd 0xe0002184
E0002184: 80001805
あらためて GET DESCRIPTOR してみる
GET DESCRIPTOR のコントロール転送をして FS01BU からの返事をもらおう。それが次の結果。
(gdb) next
1160 iQH[0] = (uint32_t)iQH | 0x2 ;
(gdb)
1162 vid = dbp[4096 + 8];
(gdb)
1163 vid += dbp[4096 + 9] << 8;
(gdb)
1164 printf("Vendor ID 0x%04x\n", vid);
(gdb) p /x vid
$22 = 0x1c9e
(gdb) next
1165 if ( vid != 0x1c9e ) {
(gdb)
1169 pid = dbp[4096 + 10];
(gdb)
1170 pid += dbp[4096 + 11] << 8;
(gdb)
1171 printf("Product ID 0x%04x\n", pid);
(gdb) p /x pid
$23 = 0x6801
見事に pid は 0x6801 になってくれた。(え?プログラムがダサい?そのうち改良されるでしょう、、、、)
結論:FS01BU は特別な処理をすることでモデムになる
Linux で使うなら、こんなことしなくても usb-modeswitch でかんたんにできる(はず)。