Raspberry Piで、L-03DをEthernetとして認識する

# やっぱり
[前回](http://qiita.com/air-gh/items/93c67c175e6519129a7f)USBモデムL-03Dの認識ができたが、ttyとして認識し、PPPを使う。世は既にPDPtype=IPで、本来PPPは不要。さらに、[L-03Dの取説](https://www.nttdocomo.co.jp/support/trouble/manual/download/l03d/index.html)によると、

>L-03D Connection Manager でのパケット通信は、より高速な NDIS 方式(無線LANのような方式)となっております。従来のダイヤルアップでの接続を行うこともできます。

とあり、NDIS方式のほうが高速そうな書きぶりだ。

# QMI
Qualcomm社のモデムインターフェースはQMIで、ttyとは異なる専用インターフェース。これを扱えるオープンソース[libqmi](https://www.freedesktop.org/wiki/Software/libqmi/)があると友人が教えてくれた。ありがとう & びっくり。
Raspberry Pi + UX302NCの例は[こちら](http://airwhite.net/?p=1422)が答えそのもので、このエントリーも不要なくらい。これをL-03Dでやってみる。

# まるっきり真似
登録するドライバーは2つ。

1. qmi_wwan: 取説で言うNDIS相当のドライバーで、制御用の/dev/cdc-wdm0とNICのwwan0ができる
2. qcserial: ATコマンド等が使える従来のttyUSB*ができる

```/etc/udev/rules.d/99-l03d.rules
ATTRS{idVendor}=="1004", ATTRS{idProduct}=="6327", RUN+="/usr/bin/eject /dev/sr0"
SUBSYSTEM=="usb", ATTRS{idVendor}=="1004", ATTRS{idProduct}=="6326", RUN+="/sbin/modprobe -b qmi_wwan"
SUBSYSTEM=="drivers", ENV{DEVPATH}=="/bus/usb/drivers/qmi_wwan", ATTR{new_id}="1004 6326"
ATTRS{idVendor}=="1004", ATTRS{idProduct}=="6326", RUN+="/sbin/modprobe -b qcserial"
SUBSYSTEM=="drivers", ENV{DEVPATH}=="/bus/usb-serial/drivers/qcserial", ATTR{new_id}="1004 6326"
```

`ATTR{new_id}`は、VID/PIDをL-03Dに合わせて変更した。

# 動作確認

```shell-session
pi@raspberrypi:~ $ lsmod
Module                  Size  Used by
:
qcserial                5129  0
usb_wwan                7104  1 qcserial
usbserial              29640  2 qcserial,usb_wwan
:
qmi_wwan               11679  
cdc_wdm                10749  1 qmi_wwan
:
pi@raspberrypi:~ $ usb-devices
:
T:  Bus=01 Lev=02 Prnt=02 Port=01 Cnt=02 Dev#=  5 Spd=480 MxCh= 0
D:  Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1004 ProdID=6326 Rev=00.00
S:  Manufacturer=NTT DOCOMO, INC.
S:  Product=docomo L03D
C:  #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
I:  If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=qcserial
I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan
I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan
```

うーむ、qmi_wwanが2個できるのは変。

```shell-session
root@raspberrypi:~# qmicli -d /dev/cdc-wdm0 --dms-get-manufactuarer
(しーん)
^Z
root@raspberrypi:~# qmicli -d /dev/cdc-wdm1 --dms-get-manufactuarer
[/dev/cdc-wdm1] Device manufacturer retrieved:
	Manufacturer: 'QUALCOMM INCORPORATED'
```

1つは生きているようだが、気持ち悪い。

# 再調査
## Windowsでは
Windowsの公式ドライバではどうか。
ドライバのプロパティの詳細などを見ると…

- USB Serial Port = MI_00 (COMポート)
- NMEA Serial Port = MI_01 (COMポート)
- Modem Driver = MI_02 (ATコマンドのCOMポート)
- USB NDIS Driver = MI_03

というUSB Composite Deviceの構成。これを認識するには…。

## udevのnew_id
そもそも、udevで設定している`ATTR{new_id}="1004 6326"`とは何なのか。
[こちら](http://harakire.tripod.com/junkies/sysfs_usb_new_id.html])にある通り、ドライバーに書かれていないVID/PIDを持ったデバイスを、特定のドライバーで登録できるようになっている。これは便利。だが、USB Composite Deviceの順番は指定できないのか。
[本家](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-usb)によると、

> idVendor idProduct bInterfaceClass RefIdVendor RefIdProduct

の並びが指定できる。今回はClassは全部0xffっぽいし、ダメか…。

## ドライバーのソースを読む
```c:linux/drivers/usb/serial/qcserial.c
        /*
         * Composite mode; don't bind to the QMI/net interface as that
         * gets handled by other drivers.
         */

        switch (id->driver_info) {
        case QCSERIAL_G1K:
                /*
                 * Gobi 1K USB layout:
                 * 0: DM/DIAG (use libqcdm from ModemManager for communication)
                 * 1: serial port (doesn't respond)
                 * 2: AT-capable modem port
                 * 3: QMI/net
                 */
                if (nintf < 3 || nintf > 4) {
                        dev_err(dev, "unknown number of interfaces: %d\n", nintf);
                        altsetting = -1;
                        goto done;
                }

                if (ifnum == 0) {
                        dev_dbg(dev, "Gobi 1K DM/DIAG interface found\n");
                        altsetting = 1;
                } else if (ifnum == 2)
                        dev_dbg(dev, "Modem port found\n");
                else
                        altsetting = -1;
                break;
```
`nintf`の0〜2がCOMポートと認識するようにすれば良さそうだが、同じ並びのものはこのソースには無さそう…。新しい`driver_info`を定義しようと思ったが、面倒になった。そこで…。

# ドライバ改造(インチキ)
何故インチキかと言うと、L-03DはGOBI1Kではないが、USB Composite Deviceの構成が似ているGOBI1Kの定義をそのまま使っているから…。

## qcserial
```diff:drivers/usb/serial/qcserial.c
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -74,6 +74,7 @@ static const struct usb_device_id id_table[] = {
        {DEVICE_G1K(0x05c6, 0x9231)},   /* Generic Gobi QDL device */
        {DEVICE_G1K(0x1f45, 0x0001)},   /* Unknown Gobi QDL device */
        {DEVICE_G1K(0x1bc7, 0x900e)},   /* Telit Gobi QDL device */
+       {DEVICE_G1K(0x1004, 0x6326)},   /* NTT docomo L-03D */

        /* Gobi 2000 devices */
        {USB_DEVICE(0x1410, 0xa010)},   /* Novatel Gobi 2000 QDL device */
```

## qmi_wwan
```diff:drivers/net/usb/qmi_wwan.c
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -812,6 +812,7 @@ static const struct usb_device_id products[] = {
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9203)},    /* Generic Gobi Modem device */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9222)},    /* Generic Gobi Modem device */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9009)},    /* Generic Gobi Modem device */
+       {QMI_GOBI1K_DEVICE(0x1004, 0x6326)},    /* NTT docomo L-03D */

        /* 5. Gobi 2000 and 3000 devices */
        {QMI_GOBI_DEVICE(0x413c, 0x8186)},      /* Dell Gobi 2000 Modem device (N0218, VU936) */
```

# 動作再確認
## デバイス認識
```shell-session
pi@raspberrypi:~ $ lsmod
Module                  Size  Used by
qcserial                5225  0
usb_wwan                7104  1 qcserial
qmi_wwan               11703  0
cdc_wdm                10749  1 qmi_wwan
usbserial              29640  2 qcserial,usb_wwan
:
pi@raspberrypi:~$ usb-devices
:
T:  Bus=01 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  5 Spd=480 MxCh= 0
D:  Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1004 ProdID=6326 Rev=00.00
S:  Manufacturer=NTT DOCOMO, INC.
S:  Product=docomo L03D
C:  #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
I:  If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qcserial
I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan
```

If#=0と1が(none)になっているが、とりあえず、If#=2のATコマンドポートと、If#=3のwwanが現れた。

## iijmioで接続

### qmicliインストール
`sudo apt-get install libqmi-utils`

### 設定ファイル設置
```/etc/qmi-network.conf
APN=iijmio.jp,CHAP,mio@iij,iij
```

### APN接続
```shell-session
pi@raspberrypi:~$ sudo qmi-network /dev/cdc-wdm0 start
Loading profile...
    APN: iijmio.jp,CHAP,mio@iij,iij
Starting network with 'qmicli -d /dev/cdc-wdm0 --wds-start-network=iijmio.jp,CHAP,mio@iij,iij  --client-no-release-cid'...
error: couldn't start network: QMI protocol error (14): 'CallFailed'
call end reason (3): generic-no-service
verbose call end reason (3,2001): [cm] no-service
Saving state... (CID: 1)
error: network start failed, no packet data handle
Clearing state...
```

つながらん。`qmicli --help-all`したコマンドを片っ端から打っても、つながらん。[最新のlibqmi](https://cgit.freedesktop.org/libqmi)をビルドして使っても、つながらん。

```shell-session
pi@raspberrypi:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-system-info
[/dev/cdc-wdm0] Successfully got system info:
	GSM service:
		Status: 'none'
		True Status: 'none'
		Preferred data path: 'no'
	WCDMA service:
		Status: 'none'
		True Status: 'none'
		Preferred data path: 'no'
	LTE service:
		Status: 'limited'
		True Status: 'limited-regional'
		Preferred data path: 'no'
		Domain: 'unknown'
		Service capability: 'ps'
		Roaming status: 'off'
		Forbidden: 'no'
		Location Area Code: 'xxxxx'
		Cell ID: 'xxxxxxxx'
		MCC: '440'
		MNC: '10'
		Tracking Area Code: '0'
		Voice support: 'yes'
```
この`limited`が問題のようだ。

### PPP接続
この状態で、PPP接続してみる。

```shell-session
pi@raspberrypi:~ $ sudo pon iijmio.jp
pi@raspberrypi:~ $ ifconfig ppp0
ppp0      Link encap:Point-to-Point Protocol
          inet addr:100.xx.xx.xx  P-t-P:10.64.64.64  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:17 errors:0 dropped:0 overruns:0 frame:0
          TX packets:18 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3
          RX bytes:1435 (1.4 KiB)  TX bytes:941 (941.0 B)
```

ちゃんとつながる。

`sudo poff`した後、もう一度`sudo qmicli -d /dev/cdc-wdm0 --nas-get-system-info`すると、`LTE service`が`Status: 'available'`となる。一旦`available`になると、

```shell-session
pi@raspberrypi:~ $ sudo qmi-network /dev/cdc-wdm0 start
Loading profile...
    APN: iijmio.jp,CHAP,mio@iij,iij
Starting network with 'qmicli -d /dev/cdc-wdm0 --wds-start-network=iijmio.jp,CHAP,mio@iij,iij  --client-no-release-cid'...
Saving state... (CID: 1)
Saving state... (PDH: 36003144)
Network started successfully
```

…となり、QMIでつながる。

```shell-session
pi@raspberrypi:~ $ ifconfig wwan0
wwan0     Link encap:Ethernet  HWaddr xx:xx:xx:xx:xx:xx
          inet addr:100.xx.xxx.xxx  Bcast:100.255.255.255  Mask:255.0.0.0
          inet6 addr: fe80::d058:5531:253a:32e3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:34 errors:0 dropped:0 overruns:0 frame:0
          TX packets:98 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:3525 (3.4 KiB)  TX bytes:14876 (14.5 KiB)
```

うーむ、何をすれば`available`になるのか。

## USBキャプチャ
WindowsのWiresharkでは、[usbpcap](http://desowin.org/usbpcap/)が使える。これと同じQMI制御を行えば、PPP接続しなくてもネットワークにつながるはず。
実際にUSBキャプチャは動作したのだが、ここまでやって、気づいてしまったのだ。

```shell-session
pi@raspberrypi:~ $ sudo qmicli -d /dev/cdc-wdm0 --dms-get-band-capabilities
[/dev/cdc-wdm0] Device band capabilities retrieved:
	Bands: 'gsm-dcs-1800, gsm-900-extended, gsm-900-primary, gsm-pcs-1900, wcdma-2100, wcdma-850-us, wcdma-800'
	LTE bands: '1'
```

Band1しか使えないのかよ! そもそも、2012年発売と、かなり古いのであった…。

# オチ
面倒なので、[UX302NC](https://www.ncxx.co.jp/product/ux302nc/feature/)買います!
でも、[YAMAHA](http://www.rtpro.yamaha.co.jp/RT/docs/mobile-internet/#cellphones)も[SEIL](http://www.seil.jp/doc/index.html#fn/usb/usb_mobile.html)(L-02Cだが)も使えるので、絶対使う方法はあるはずなのだが…くやしい。