E-mobileの電話型端末H12HWを試す機会が会ったのでOpenBSDでのデータ通信を試してみた。H12HWは型番が示すようにHUAWEI社の製品で3.6MbpsのHSDPAに対応している。
- product HUAWEI Mobile 0x1008 HUAWEI Mobile
- {{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_Mobile}, DEV_HUAWEI},
hack, hack, hack, write and hack.
E-mobileの電話型端末H12HWを試す機会が会ったのでOpenBSDでのデータ通信を試してみた。H12HWは型番が示すようにHUAWEI社の製品で3.6MbpsのHSDPAに対応している。
E-mobileの最近のモデムを触ってみる機会があったのですこし調べてみた。結果を共有。
少し前の話だがE-mobileのD21LCをしばらく借りることができたので、少し解析してOpenBSDで使えるようにしてみた。その時の話をメモとして残しておくことにする。
D21LCは中国のLongcheer社製のHSDPAモデムだ。E-mobileでは最初はHuawei社のモデムを使っていたが、しばらく前から2社の製品を投入している。D21LCも他のE-mobileのモデムと同様に「ゼロインストール」機能が搭載されている。ゼロインストールとはドライバが入っていないOS(つまり初回挿入時)にはモデムをUSBマスストレージデバイスとして認識させて必要なドライバをモデムから直接インストールさせる仕組みである。ドライバが動き始めると、そのドライバがモデムのモードを変更してモデムとして動作するようになる。Windowsなどからは大変便利な機能であるが、ほかのOSで使うためにはちょっとおせっかいだ。
Huawei社のモデムはメモ:D02HW解析で昔書いたように特別なUSBコマンドを発行すると、USBマスストレージモードからモデムモードに動作モードが切り替わった。D21LCの場合はどうだろうか?
D21LC:モデムモードへの切り替えUSBアナライザでUSBトランザクションをモニタしてみると、あるタイミングでデバイスがリセットされるタイミングがあった。これがモデムモードへの切り替えコマンドだろうと推測してもう少し中身を眺めてみる。
D21LCのモード変更コマンドはUSBマスストレージデバイスに対するコマンドとして実装されていた。(コマンドパケットの先頭の0x55 0x53 0x42 0x43:USBCが規定のマジックナンバー)といっても、USBマスストレージデバイスの仕様には定義されていない独自コマンドを発行している。
さらにモデムの動作を眺めてみたが、それ以降は普通の3Gモデムと同様の動作をしているようだ。
というわけで、このシーケンスを発行するようにデバイスドライバを拡張してやれば、ほかのOSでもモデムとして利用できるはずだ。
シリアルポート
縁あってOpenBSDの開発者の人たちと一緒にhackする機会があったので、なにかコントリビューションできることはないかなと考えたところ、OpenBSDの3Gモデムのサポートを拡張することにした。
特にD02HWは、本当の名前はE220といって世界各地で使われているメジャーなモデムなので、これがまともに使えるようになるとうれしい人がそれなりにいるだろうから、ちょうど良い。
OpenBSDもNetBSDと同様に、FreeBSD由来のUSBサブシステムを採用しているので、usbデバイスドライバは大きな差異は存在しない。NetBSD用につくったuhmodem(4)をそのまま移植するという手もあったのだけど、OpenBSDにはHSDPAモデム用のドライバとしてumsm(4)がすでに存在していたので、それを拡張する形で実装してみることにした。
umsm(4)(拡張前)
dev/usb/umsm.c(rev.1.21)を眺めてみると、非常にシンプルなドライバになっていた。具体的に言うと、
struct ucom_methods umsm_methods = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
となっていた。struct ucom_methods は、上位ドライバとなるucom(4)のopen/closeなどの各処理におけるデバイス依存コードへの関数ポインタを渡すための構造体なので、この状態ではシリアルに関する処理はucomでのデフォルト動作だけで実行されることを意味している。
どうしてこれで他のドライバが動作しているのだろうか?と「???」と思いながらざっくりとコードを追ってみたところ、シリアルデバイスのinterrupt endpointに関する処理がざっくりと抜け落ちていることに気がついた。単なるin/out endpointを処理するだけなら、確かにucom(4)における特別な処理は必要ないが、モデムで通信を行う際には自分から始めるだけではなく割り込み処理が必要である。
近くにいたOpenBSD開発者に聞いてみたら「おれが使っているE220で昔だれかが作ったパッチで試した見たところ、たしかにPPPまでは張れることもあるがうまく動かなかった。つながるけど切れたり通信できなかったりしてね。それから使ってない」と言われた。さもありなん、ということでinterrupt endpointの処理を拡張するところから始めることにする。D02HW(E220)以外でも役にたつだろう。
interrupt endpoint処理の追加
endpointの割り当て処理はumsm_attach()の中で行われる。data用のbulk in/out endpointしか処理していなかった部分を、以下のように変更する。また、struct umsm_softcにもinterrupt endpoint周りの状態を保持するための変数を拡張しておく。
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
sc->sc_intr_number = ed->bEndpointAddress;
sc->sc_isize = UGETW(ed->wMaxPacketSize);
DPRINTF(("%s: find interrupt endpoint for %s\n",
__func__, sc->sc_dev.dv_xname));
} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
uca.bulkin = ed->bEndpointAddress;
else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
uca.bulkout = ed->bEndpointAddress;
これで、interrupt endpointを持っているinterfaceがあったら、interrupt endpointを正しく扱うための準備ができる。
次に、ucomをopenしたときに検出したinterrupt endpointをucom割り込みとして利用するためのコードを追加しなければならない。まずucomをopenしたときにusb的にinterrupt endpointを登録するコード(usbd_open_pipe_intr()を呼ぶ)と、closeしたときに後始末(usbd_about_pipe() & usbd_close_pipe()を呼ぶ)をするデバイス依存の関数を追加する。それぞれubsa(4)から持ってきて、umsm_open(), umsm_close()ということにしておく。ucomがopen/closeされるタイミングで呼ばれるデバイス依存関数は、前述のstruct ucom_methodsで指定しておけばよい。
さらに割り込み発生時に呼び出されるデバイス依存関数とその補助関数として、同じくubsa(4)からumsm_intr(), umsm_get_status()を持ってくる。同様にstruct ucom_methodsに指定する。
これで、interrupt endpoint周りの処理は完了。普通のHSDPAモデムのDoCoMo A2502をつなげて試してPPPセッションが正しく張れるようになっていることを確認。とりあえずここまでで半分くらい終了。
ここまでのコードの差分は、OpenBSD CVSwebで確認できる。
次回のエントリーでは、D02HWのモード遷移のための関数の統合について書く予定。

別に3Gモデムを攻略するのが趣味ってわけではないが、近くにNTT DoCoMoのHSDPAモデム A2502 (製品情報)があったのでどんなものか眺めてみた。結構あっさりとNetBSDで動くようになったので方法を書いておく。
A2502はAnyData社の製品とのことだが、イーモバイルのD02HW(HAUWEIのE220)みたいにメジャーな製品のOEMというわけではなさそう。検索してもあまり素性がわからないので、とりあえずUSBバスを観測してみたところ、コマンドの系列がこの前書いたD02HWと互換性がありそうだ。というわけで、とりあえずモデムモードになったあとのD02HWと同じように扱ってみたところ通信ができることを確認。
モデムとしては、
何回かにわたってD02HWをNetBSDで使う話を書いていたが、1/21付けでNetBSD(-current)にマージされた。
基本的に「NetBSDでEmobile D02HWを使う(新デバイスドライバ)」でsend-prしたものがおおむねそのままマージされたかんじ。cvsで吸い出すだけで使えるようになったはず。
使う人が出てきたら潜在的な問題とかが出てくるかもしれないけど、一段落。
NetBSDでD02HWを使うためにいろいろとやった結果、前回のubsaデバイスドライバへのパッチというアプローチではどうも差分が大きくなりすぎる、という結論に達した。新しく別のデバイスドライバとしたほうがなにかと都合がよいので、uhmodem (USB huawei modem)という名前の新しいデバイスドライバをつくってみた。と、いってもubsaとの共有部分も多いのでそのままコピーして変更するのもいまいちな気がしたので、ubsa.cをubsavar.h, ubsa_common.c, ubsa.cの3つに分解して、uhmodem.cからはubsa_common.cを介してubsaの関数を使うことにした。
とりあえずdmesgを載せておく。
uhmodem0 at uhub0 port 1 configuration 1 interface 0
uhmodem0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
uhmodem0: mass storage only mode, reattach to enable modem
uhmodem0: at uhub0 port 1 (addr 2) disconnected
uhmodem0 detached
uhmodem0 at uhub0 port 1 configuration 1 interface 0
uhmodem0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ucom0 at uhmodem0 portno 0: modem
ucom1 at uhmodem0 portno 1: monitor
umass0 at uhub0 port 1 configuration 1 interface 2
umass0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
umass0: using SCSI over Bulk-Only
scsibus0 at umass0: 2 targets, 1 lun per target
cd0 at scsibus0 target 0 lun 0:
attach時のメッセージを見てわかるように、uhmodemではD02HW(E220)が提供しているすべての機能(com *2, umass *1)を同時につかえるようになっている。
現状ではumassがつながっても、windowsのデバイスドライバが見えるだけだが、海外のほうでは、あの領域に好きなものを入れれるようなアクティビティもあるようなのでその時に役にたつかもなーくらいな気持ちでいれてみた。
ucom0, ucom1は前者が通信用のttyで、後者がモデムの状況や通信状況をモニターするための監視用ttyだ。ucom0を使っているときでもucom1を同時にアクセスできるので、メモ:D02HW解析に書いたDSFLOWRPTメッセージとか電波状態問い合わせ(^ANQUERY)の回答などを受け取れる。ユーザーインターフェイスアプリケーションを書くときには便利なはず。
というわけで、patchは大きくなったのでここに乗せるのは割愛しておく。すでにNetBSDにはsend-prしてある(kern/37692)ので急ぐ場合はそちらを参照すると良いかも。
とりあえずやることはおわったかなーーー。。
イーモバイルのD02HW (Huawei E220のOEM)をNetBSDから使えるように触っている過程で調べたことをメモしておく。
マスストレージモード(電源投入時)
普通に認識すると、USB mass strageデバイスに見える。USB class 8(umass), subclass 6(SCSI), proto 80(BBB)。Interface 0: endpoint: 3(in)-4(out)。マウントするとCDROMにみえて、中身にwindows用のユーティリティとデバイスドライバが入っている。役立たず。
モデムモードへの変換
Host-to-DeviceでSet Future: DEVICE_REMOTE_WAKEUP, wIndex=0x2で送りつける。すると同じvender ID, product IDのままで別のデバイスに化ける。
(wIndex=0x1にしてdataに0x1を入れて送っても同じ結果になるが、違いは不明)
モデムモード(変換後)
モードがモデムモードになると、インターフェイスが3つに増える。
^DSFLOWRPT:0000007C, 00000000, 00000000, 000000000001B1AE, 00000000004DD9D2, 0000AA50, 000EBA40
net.inet.tcp.mss_ifmtu=1
net.inet.tcp.init_win=8
net.inet.tcp.sack.maxholes=64
net.inet.tcp.sendspace=65536
net.inet.tcp.recvspace=128000
net.inet.udp.recvspace=65536
kern.sbmax=2048000
# disable compressions
novj
##noccp
##nobsdcomp
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
uca.bulkin = ed->bEndpointAddress;
uca.ibufsize = UGETW(ed->wMaxPacketSize);
} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
uca.bulkout = ed->bEndpointAddress;
uca.obufsize = UGETW(ed->wMaxPacketSize);
}
せっかくそれなりに速い速度のアップリンクを持つイーモバイルのD02HWが使えるようになったので、イーモバイルをアップリンクにする無線アクセスポイントをつくってみた。OSはNetBSD。とりあえず手元にあったusb無線LANアダプタ(バッファローのWLI-U2-SG54HP)をつかっている。ボードはALIX.3C
やることは普通のルータの設定と大体同じ。
NetBSDのpppdのemobileにつなげる方法を教えてもらったので設定してみた。設定をまとめておく。
(まだ最適化はされていないので冗長な記述などがあるかもしれない)
設定するファイルは以下の5つ
dtyU0 460800 crtscts
lock
nodetach
hide-password
local
noauth
# dns routing
usepeerdns
defaultroute
noipdefault
# disable compressions
novj
noccp
nobsdcomp
# auth
user "em"
# misc? (xxx)
ipcp-restart 8
ipcp-max-configure 50
ipcp-accept-local
ipcp-accept-remote
# connect script
connect '/usr/sbin/chat -v -f /etc/ppp/chat-emobile -T *99***1#'
ABORT "NO CARRIER"
ABORT "NO DIALTONE"
ABORT "ERROR"
ABORT "NO ANSWER"
ABORT "BUSY"
ECHO ON
SAY "Connecting"
TIMEOUT 15
"" "\d\d\dat\r\dat"
TIMEOUT 5
"OK-\Kat-OK" "at"
OK "at&FE1V1X1&D2&C1S0=0"
OK "at+ipr=230400"
SAY "Let's go"
OK "atdt\T"
CONNECT ''
#!/bin/sh
~
f [ -f /etc/ppp/resolv.conf ]; then
chmod 644 /etc/ppp/resolv.conf
cp /etc/resolv.conf /etc/resolv.conf-
if [ ${USEPEERDNS:-0} -eq 1 ]; then
cp /etc/ppp/resolv.conf /etc/resolv.conf
chmod 644 /etc/resolv.conf
fi
fi
#!/bin/sh
#
if [ -f /etc/resolv.conf- -a ${USEPEERDNS:-0} -eq 1 ]; then
mv /etc/resolv.conf /etc/resolv.conf.old
cp /etc/resolv.conf- /etc/resolv.conf
chmod 644 /etc/resolv.conf
fi
if [ -f /etc/ppp/resolv.conf ]; then
mv /etc/ppp/resolv.conf /etc/ppp/resolv.conf-
fi
em * em
「NetBSDでD02HWをつかう」では比較的(というかほぼ全部)試行錯誤でデバイスの設定に必要な条件を推測してデバイスドライバへのパッチを作成した。ハードウェアを触るときによく感じることは「見えないものはわからない」ということだ。当たり前のことだが、電気信号は眼にはみえないので、推測するしかなくそれはいつまでも推測のままで推移してしまう。電圧を測るのにテスターが必要で、時間ドメインの波形をみるのにオシロスコープが必要なように、扱う事象を見るための専用の「アナライザ」が必要になることは多い。実はとある目的のためにUSBバスアナライザが手元にあったので、D02HWのデバイスドライバ作成作業を検証してみることにした。手元にあるのはellisysのUSB Explorer 200だ。これは、USB2.0のHighSpeed(480Mbps)に対応したアナライザで、Windowsソフトウェアと組み合わせることでだいたいすべてのUSBイベントを表示してくれる、という便利な逸品である。ソフトウェアは誰でもダウンロードできるようになっていて、USBバスのキャプチャファイルを入手したときに誰でも表示できるのがうれしい(ちなみにキャプチャファイルの拡張子はUFOだ)。
写真でわかるように、アナライザを解析ソフトが入っているPCにつなげて、デバイスとデバイスをつなげるホストの間にはさむようにアナライザを配置する。これで、デバイスとホスト間のデータをインターセプトするという仕組みになっている。
まず、Windowsで動かしてみたときにUSBバスにどんなトランザクションが発生しているかを見てみた。
「NetBSDでEmobile D02HWを使う(3)」にあるUSBのインターフェイスとエンドポイントの情報を見ながら、どのエンドポイントとホストがどんな通信をしているかを眺めてみる。アナライザは、
たとえばこんな情報が表示されて、トランザクションのタイプと中身のデコード結果をだしてくれるので、それとUSB2.0の仕様書(このへんにある)を眺めながら追っていく。赤裸々にどんなトランザクションをやりとりしているかを見ていてわかったことは以下の3つ。
INTERFACE descriptor 1:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=1 bAlternateSetting=0
bNumEndpoints=2 bInterfaceClass=255 bInterfaceSubClass=255
bInterfaceProtocol=255 iInterface=3(Data Interface)
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=5-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=5-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
Windowsやmacのドライバでは抜き差しせずとも使えるということは、なんらかの方法でこの2つのモードの切り替えができるはずだ。機能の切り替え、はデバイス毎の特有の操作なので一般的な方法は存在しない。USBのバスアナライザとかがあれば、windowsのドライバの挙動などを観察して切り替えるための魔法の言葉を見つけることもできるのだが、休日に家で作業をしていたので手元にそんな便利なものはなかった。
というわけでいろいろと試行錯誤してみることにする。
/* Find the endpoints */
usbd_delay_ms(dev, 2000);
id = usbd_get_interface_descriptor(sc->sc_iface);
sc->sc_iface_number = id->bInterfaceNumber;
static usbd_status
ubsa_reset(struct ubsa_softc *sc)
{
usb_device_request_t req;
usbd_status err;
#define UBSA_SET_REQUEST 0x1
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UBSA_SET_REQUEST;
USETW(req.wValue, 0);
USETW(req.wIndex, sc->sc_iface_number);
USETW(req.wLength, 0);
err = usbd_do_request(sc->sc_udev, &req, 0);
if (err) {
printf("%s: send reset fail\n", __func__);
return (EIO);
}
return (0);
}
/*
* Hauwei E220 needs special request to enable modem function
* XXX: is there more smart methods?
*/
Static usbd_status
ubsa_e220_modechange_request(struct ubsa_softc *sc)
{
#define UBSA_E220_MODE_CHANGE_REQUEST 0x2
usb_device_request_t req;
usbd_status err;
req.bmRequestType = UT_WRITE_DEVICE;
req.bRequest = UR_SET_FEATURE;
USETW(req.wValue, 0x1);
USETW(req.wIndex, UBSA_E220_MODE_CHANGE_REQUEST);
USETW(req.wLength, 0);
DPRINTF(("%s: send e220 mode change request\n", __func__));
err = usbd_do_request(sc->sc_udev, &req, 0);
if (err) {
DPRINTF(("%s: E220 mode change fail\n", __func__));
return (EIO);
}
return (0);
#undef UBSA_E220_MODE_CHANGE_REQUEST
}
/* Find the endpoints */
/* Hauwei E220 need special request to change its mode to modem */
if (sc->sc_devtype_e220) {
err = ubsa_e220_modechange_request(sc);
if (err) {
printf("%s: failed to change mode: %s\n",
devname, usbd_errstr(err));
sc->sc_dying = 1;
goto error;
}
}
id = usbd_get_interface_descriptor(sc->sc_iface);
sc->sc_iface_number = id->bInterfaceNumber;
ubsa0 at uhub0 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ubsa0: HUAWEI E220 need to re-attach to enable modem function
ubsa0: at uhub0 port 2 (addr 2) disconnected
ubsa0 detached
ubsa0 at uhub0 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ucom0 at ubsa0
#tip dtyU0
connected
at
OK
atI
Manufacturer: huawei
Model: D02HW
Revision: 11.005.08.00.168
IMEI: 3560600XXXXXXXX
+GCAP: +CGSM,+DS,+ES
OK
インスタントに「NetBSDでイーモバイルを使う方法」が書いてあるかというとそうでもなくて残念だと思う人もいるかもしれない。今回書いたpatchはすでにNetBSDには送ってあるのだがこれが採用されるかどうかはまだ不明なので、最近の-currentをCVSチェックアウトしただけでは現状では使えない。今回も「どうやってUSBデバイスを動かすようにしたか」というネタが続いているのでその辺に興味があるならおつきあい頂きたい。
DEVICE addr 2
DEVICE descriptor:
bLength=18 bDescriptorType=device(1) bcdUSB=1.10 bDeviceClass=0 bDeviceSubClass=
0
bDeviceProtocol=0 bMaxPacketSize=64 idVendor=0x12d1 idProduct=0x1003 bcdDevice=0
iManufacturer=1(HUAWEI Technologies) iProduct=2(HUAWEI Mobile) iSerialNumber=0()
bNumConfigurations=1
CONFIGURATION descriptor 0:
bLength=9 bDescriptorType=config(2) wTotalLength=32 bNumInterface=1
bConfigurationValue=1 iConfiguration=0() bmAttributes=a0 bMaxPower=500 mA
INTERFACE descriptor 0:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=0 bAlternateSetting=0
bNumEndpoints=2 bInterfaceClass=8 bInterfaceSubClass=6
bInterfaceProtocol=80 iInterface=0()
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=3-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=4-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
current configuration 1
DEVICE addr 2
DEVICE descriptor:
bLength=18 bDescriptorType=device(1) bcdUSB=1.10 bDeviceClass=0 bDeviceSubClass=
0
bDeviceProtocol=0 bMaxPacketSize=64 idVendor=0x12d1 idProduct=0x1003 bcdDevice=0
iManufacturer=1(HUAWEI Technologies) iProduct=2(HUAWEI Mobile) iSerialNumber=0()
bNumConfigurations=1
CONFIGURATION descriptor 0:
bLength=9 bDescriptorType=config(2) wTotalLength=85 bNumInterface=3
bConfigurationValue=1 iConfiguration=0() bmAttributes=a0 bMaxPower=500 mA
INTERFACE descriptor 0:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=0 bAlternateSetting=0
bNumEndpoints=3 bInterfaceClass=255 bInterfaceSubClass=255
bInterfaceProtocol=255 iInterface=3(Data Interface)
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=1-in
bmAttributes=interrupt wMaxPacketSize=16 bInterval=128
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=2-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=2-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
INTERFACE descriptor 1:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=1 bAlternateSetting=0
bNumEndpoints=2 bInterfaceClass=255 bInterfaceSubClass=255
bInterfaceProtocol=255 iInterface=3(Data Interface)
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=5-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=5-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
INTERFACE descriptor 2:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=2 bAlternateSetting=0
bNumEndpoints=2 bInterfaceClass=8 bInterfaceSubClass=6
bInterfaceProtocol=80 iInterface=0()
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=3-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=4-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0
current configuration 1
CS5536を扱った時は十分に詳しい仕様書が入手できて先がみえる作業(つまり作ればできる)だったが、今回のD02HW(E220)に関しては外部の仕様書が存在しないため、いろいろと試行錯誤が必要となる。幸いにも、「ごにょごにょすると動く」とか「不十分だけど動くかも」といった情報があるし、Linuxでは動いているらしいのであまり深刻にならずにやってみることにしよう(ま、楽しみでやっているわけだし)。
前回のエントリでも出したが、この段階では接続するとこんなメッセージがkernelから出力される。
ubsa0 at uhub0 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
usbd_set_config_index: illegal index
ubsa0: failed to set configuration: INVAL
問題となっている部分として目につくのが"usbd_set_condig_index: illegal index"の部分だ。ベースとなるsys/dev/usb/ubsa.cを開くと、その部分はUSB_ATTACH(ubsa)の中のこんなコードだった。
/* Move the device into the configured state. */
err = usbd_set_config_index(dev, UBSA_CONFIG_INDEX, 1);
if (err) {
printf("%s: failed to set configuration: %s\n",
devname, usbd_errstr(err));
sc->sc_dying = 1;
goto error;
}
usbd_set_config_index()の実体はsys/deb/usb/usb_subr.cにあるので見てみると、USBのデバイス的に「そんなINDEXはないよ」ということを意味している。ここでデバイスに頑張れといっても無い袖を振ってくれるわけでもないので、ここはUBSA_CONFIG_INDEXの値があっていないのだろうと考えるのが正しそう。もともとの値は1だったので、とりあえず0にしたkernelを作って試してみる。
すると別のメッセージが出るようになる。ubsa.cをみると、それなりにコードの先のほうまで進んでいるようだ。
ubsa0 at uhub1 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ubsa0: Could not find interrupt in
該当部分のコードは、さっきと同じUSB_ATTACH(ubsa)の中の、
if (sc->sc_intr_number == -1) {
printf("%s: Could not find interrupt in\n", devname);
sc->sc_dying = 1;
goto error;
}
である。ちょっと前の方にもどって追ってみると、USBデバイス(D02HW)からエンドポイントを取得してみたけど必要な割り込み用のエンドポイントをもらえなかった、のであきらめちゃう、ということらしい。割り込み用のエンドポイントがもらえないのは一大事なのでデバイスドライバとしてはあきらめるのは当然だとは思うが、動かしたい人としてはそれでは困る。
可能性としては、
イーモバイルのD02HWが手元に来たので、NetBSDで動かせるようにしてみた。今回はそのあたりの話を書いてみることにする。
D02HWはD01HWとほとんど同じハードウェアで、HUAWEIのE220のOEM製品である。今回も手をつける前にとりあえずgoogleとかしてみると、動いたとか動かないとかどうも歯切れがわるい。NetBSDの場合ではcurrent-usersという最新版の開発ブランチのkernelに関する話題を扱うメーリングリストがあるのだが、いくつかのスレッドがあった割には実際のコードには反映されていない状態だった。
まあ、手元に実際のデバイスがあるから、と手をつけてみることにした。
D02HWはUSBのHSDPAモデムだが、モデムの機能の前にデフォルトではUSBストレージデバイスとして認識されるようになっている。たとえば、なんの設定もしていないNetBSDではumassデバイスとして認識・アタッチされて、CDROMとして中身をmountできるようになる。中身はwindows用のデバイスドライバ群で、NetBSDでモデムとして使おうとして思っているときには何の役にもたたない。差し込みのたびにumassとして認識されるのもうるさかったので、まずそのへんをdisableする。umassのdevice driverはsys/dev/usb/umass.cあたりにある。中身をざっと見てみると特定のデバイス毎の特別な設定を書くためのhookが用意されていて、sys/dev/usb/umass_quirks.cに追加できるようになっている。今回は単純にattachされないようにするだけなので、Static const struct umass_quirk umass_quirks[] = {の中に
{ { USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 },
0, 0, 0, 0, 0, NULL, NULL
},
というエントリーを追加した。これで、USBのベンダIDがHUAWEIでプロダクトIDがE220のデバイスはUSBストレージデバイスとして認識されなくなる。NetBSDでは、専用のドライバが存在しないデバイスはugen(汎用USBデバイス)としてattachされるので、umassをdisableすると次からはD02HW(E220)はugenとして認識されるようになる。
このあたりでugenとして認識されたD02HWをユーザーランドからlibusbをつかってなんか操作できないかと、数時間時間を費やしてみたが、得たものは
Static const struct usb_devno ubsa_devs[] = {に
/* HUAWEI E220 / Emobile D0[12]HW */
{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }
ubsa0 at uhub0 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
usbd_set_config_index: illegal index
ubsa0: failed to set configuration: INVAL