Raspberry Piで、UX302NCをEthernetとして認識する

# UX302NC購入
[前回](http://qiita.com/air-gh/items/6add3c0b19e8fe9469aa)でL-03DでのEthernet接続がギブアップ気味となり、UX302NC購入宣言となった。世に情報もあるし、あっさりつながるだろう。Ethernetに見えれば、夢のIPv4/IPv6デュアルスタックだ。たぶん。

# udev設定
[こちら](http://airwhite.net/?p=1422)のありがたい先人の知恵を、まずはそのまま適用してみる。udevのrulesに`ATTR{idVendor}`と`ATTR{idProduct}`でIDを記載するため、ドライバー修正が不要なところがありがたい。

# 動作確認
```shell-session
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=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=11f6 ProdID=1034 Rev=02.32
S:  Manufacturer=NCXX Inc.
S:  Product=NCXX UX302NC
C:  #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
I:  If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qcserial
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=qcserial
```

あれ、qmi_wwanが居ないではないか…。カーネルバージョンで変わったか、デバイスのファームが変わったか、持ち前の運の悪さか。1つずれているのか?

# 修正
結局、If#の0〜2がqcserialで、3がqmi_wwanになるようにudevで認識させる方法が分からないので、ええい、面倒だ、ドライバーをいじってしまえ。まさか前回の調査が生きることになるとは…。

## qcserial.c
前回のL-03DではGOBI1Kの定義をそのまま使ってしまったが、UX302NCの場合、Windowsドライバの認識では、

- HS-USB Diagnostics = MI_00 (COMポート)
- HS-USB Modem = MI_01 (ATコマンドのCOMポート)
- HS-USB Application = MI_02 (COMポート)
- Ethernet Adapter = MI_03

…となっているので、新たなデバイスタイプを定義してみる。

```diff:drivers/usb/serial/qcserial.c
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -30,6 +30,7 @@ enum qcserial_layouts {
        QCSERIAL_G1K = 1,       /* Gobi 1000 */
        QCSERIAL_SWI = 2,       /* Sierra Wireless */
        QCSERIAL_HWI = 3,       /* Huawei */
+       QCSERIAL_NCX = 4,       /* NCXX */
 };

 #define DEVICE_G1K(v, p) \
@@ -38,6 +39,8 @@ enum qcserial_layouts {
        USB_DEVICE(v, p), .driver_info = QCSERIAL_SWI
 #define DEVICE_HWI(v, p) \
        USB_DEVICE(v, p), .driver_info = QCSERIAL_HWI
+#define DEVICE_NCX(v, p) \
+       USB_DEVICE(v, p), .driver_info = QCSERIAL_NCX

 static const struct usb_device_id id_table[] = {
        /* Gobi 1000 devices */
@@ -170,6 +173,10 @@ static const struct usb_device_id id_table[] = {
        /* Huawei devices */
        {DEVICE_HWI(0x03f0, 0x581d)},   /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */

+       /* NCXX devices */
+       {DEVICE_NCX(0x11f6, 0x1034)},   /* NCXX UX302NC */
+       {DEVICE_NCX(0x1004, 0x6326)},   /* NTT docomo L-03D */
+
        { }                             /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, id_table);
@@ -379,6 +386,30 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
                                intf->desc.bInterfaceProtocol);
                }
                break;
+       case QCSERIAL_NCX:
+               /*
+                * NCXX layout:
+                * 0: DM/DIAG (use libqcdm from ModemManager for communication)
+                * 1: AT-capable modem port
+                * 2: Application
+                * 3: QMI/net
+                */
+               switch (ifnum) {
+               case 0:
+                       dev_dbg(dev, "DM/DIAG interface found\n");
+                       break;
+               case 1:
+                       dev_dbg(dev, "Modem port found\n");
+                       break;
+               case 2:
+                       dev_dbg(dev, "Application interface found\n");
+                       break;
+               default:
+                       /* don't claim any unsupported interface */
+                       altsetting = -1;
+                       break;
+               }
+               break;
        default:
                dev_err(dev, "unsupported device layout type: %lu\n",
                        id->driver_info);
```

L-03Dも、結局0〜2がserialで3がEthernetという配置は同じなので、dev_dbg()のメッセージの違いは無視して、定義をまとめてしまった。
この直し方が正しいかは不明。前にリセットした気がする…。

## qmi_wwan.c

こちらは、

```c:drivers/net/usb/qmi_wwan.c
/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
#define QMI_GOBI1K_DEVICE(vend, prod) \
        QMI_FIXED_INTF(vend, prod, 3)
```

…とのことなので、これをそのまま活用して、

```diff:drivers/net/usb/qmi_wwan.c
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -812,6 +812,8 @@ 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 */
+       {QMI_GOBI1K_DEVICE(0x11f6, 0x1034)},    /* NCXX UX302NC */

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

…としてみる。

# udev
UX302NCは、`eject`によるイジェクトがうまく行かなかったので、rulesの`sg_raw`の行だけは生かしておき、CD-ROM認識時にイジェエクトさせる。

# 再び動作確認

## Ethernet認識
```shell-session
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=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=11f6 ProdID=1034 Rev=02.32
S:  Manufacturer=NCXX Inc.
S:  Product=NCXX UX302NC
C:  #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=qcserial
I:  If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qcserial
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
```

おお。今度はうまく行きそうだ。

## 接続
```/etc/qmi-network.conf
APN=iijmio.jp,CHAP,mio@iij,iij
```

```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: 10)
Saving state... (PDH: 90627728)
Network started successfully
```

めでたしめでたし。

# IPv4/IPv6デュアルスタック接続への道
Ethernetで認識できたので、PDPtype=IPV4V6にして、めでたくデュアルスタックの運び…となるはずだったのに、ならないのだ、これが。
まず、OSXのUX302NC Data Connection Managerは、IPv4, IPv6, PPPの3択で、IPv4v6が無い。
さらに、libqmiの`QmiWdsIpFamily`では、`QMI_WDS_IP_FAMILY_IPV4`, `QMI_WDS_IP_FAMILY_IPV6`, `QMI_WDS_IP_FAMILY_UNSPECIFIED`の3択。
しかも、Raspberry PiからATコマンドが使えない…このドライバーの修正が悪いのか?
[このあたり](https://lists.freedesktop.org/archives/libqmi-devel/2014-December/001038.html)を読むと、QMIの深い罠が…という話は、成功時に別途。
まだデュアルスタック接続に成功していない、というオチであった…。