2015年9月2日水曜日

Raspberry Pi 2 + kernel 4.1 / 4.4 系列で5GHz対応WifiドングルGW-450Dを動かした

本ページで目指すもの

本ページでは、Raspberry Piを5GHz帯のネットワークに接続することを目標にします。必要なものは下記のどちらかの無線LAN USBアダプタです。
なお、 I-O DATA製WN-AC433UKでも検証を行ったのですが、安定性が良くなかったのでこちらは非推奨とします。

はじめに

Raspberry PiなどのLinuxボードをAndroid Bazaar and ConferenceやMaker Faireなどで展示する際、Wifiの混線を避けるために5GHz対応のWifiドングルPlanex製GW-450Dを使うことがよくありました。

kernelの再構築が必要な場合が多いなど面倒ではありますが、ネットを探せばその方法はたくさん見つかりますので、それほどハードルは高くありません。

しかし、Jessie以降 (NOOBS 1.4.2以降) のRaspbianで用いられているkernel 4.1や4.4系列でPlanex製GW-450Dを動かそうとしたところ、これまで知られた方法ではうまく動作しない部分があったので対処法をメモします。

解説は、下記の順で行います。
  1. Raspberry Pi用のカーネルの再構築
  2. Planex製GW-450Dのドライバのインストール
  3. I-O DATA製WN-AC433UKについての注意
  4. Buffalo製WI-U2-433DMのドライバのインストール
最初に行うカーネルの再構築はPlanex製GW-450DでもBuffalo製WI-U2-433DMでも必要となりますのでご注意ください。

なお、本ページの解説は「Sionの日記 | RaspberryPi:GW-450Dを使う」で解説されている手法に準拠しています。Sionさんに感謝します。

私が検証に用いたのはRaspberry Pi 2ですが、ページ中の読み替えの指示に従えばRaspberry Pi Model B+でも可能なはずです。Raspberry Pi 3では試していませんが、Raspberry Pi 2と同じ手順でできるのではないかと思っています。


必要なパッケージをインストール

まず、kernel再構築に必要なパッケージをインストールします。
$ sudo apt-get update
$ sudo apt-get install gcc make bc screen ncurses-dev
お好みで、下記のコマンドによりkernelも最新にしておきます。なお、2017年3月現在、kernelを最新にしようとすると4.9系列にアップデートされますので、無理に更新しない方がよいかもしれません(4.1、4.4系列でのみ動作を確認しています)。
$ sudo rpi-update
$ sudo reboot

カーネルのソースを入手

次に、kernelのソースをダウンロードします。まず、Raspberry Piで動作しているkernelのバージョンをあらかじめ知るために、ターミナル上で下記のコマンドを実行しましょう。
$ uname -r
Jessie系列では「4.1.X-v7+」や「4.4.X-v7+」となるでしょう(「-v7+」はRaspberry Pi 2用のkernelであることを表す)。

次に、以下のコマンドでkernelのソースをダウンロードします。3つ目のコマンドでkernelのブランチをrpi-4.1.yと指定していますが、これは、ビルドしたいkernelが4.1系列である場合です。上のkernelのバージョンチェックでkernel 4.4系列が用いられていた場合はrpi-4.4.yとしてください。こちらのページの「Branch:」の部分で得られるkernelソースの選択肢を見ることができます。

(2015.9.11 執筆直後の方法だと、学習リモコン用のlircが動かないことに気づいたので、ビルド方法をなるべく公式準拠になるよう変更しました)
$ cd /usr/src
$ sudo su
# git clone --depth 1 https://github.com/raspberrypi/linux.git -b rpi-4.1.y
# git clone --depth 1 https://github.com/raspberrypi/firmware.git
# cd linux
# modprobe configs
# zcat /proc/config.gz > .config
# cp ../firmware/extra/Module7.symvers Module.symvers
(Model B+の場合、Module7.symversをModule.symversに読み替えます)

カーネルのコンパイルから再起動まで

kernelのビルド、インストールは下記のようにします。make oldconfigの際に、新しい設定項目をどうするか聞かれた場合は、yかMを指定しました。

ビルドには数時間かかるので気長に待ちます。最後に再起動しています。
# make oldconfig
# make -j 4 zImage modules dtbs
# make modules_install
# cp arch/arm/boot/dts/*.dtb /boot/
# cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
# cp arch/arm/boot/dts/overlays/README /boot/overlays/
# cp /boot/kernel7.img /boot/kernel7.img.old
# scripts/mkknlimg arch/arm/boot/zImage /boot/kernel7.img
# reboot
(Model B+の場合、kernel7.imgをkernel.imgに読み替えます)
以上で自前ビルドのkernelでRaspberry Pi 2が動作するようになりました。ここから、Planex製GW-450Dのドライバーのインストールに入ります。

Planex製GW-450Dのドライバの入手

http://www.planex.co.jp/support/download/gw-450d/driver_linux.shtml
より、gw-450d_driver_linux_v3002.zipをダウンロードし、/usr/srcに移動しておきます。

そして、ドライバを下記のように展開します。
$ cd /usr/src
$ sudo su
# unzip gw-450d_driver_linux_v3002.zip
# cd gw-450d_driver_linux_v3002
# tar xf mt7610u_wifi_sta_v3002_dpo_20130916.tar.bz2
# cd mt7610u_wifi_sta_v3002_dpo_20130916
展開が終わったら、パッチをダウンロードして適用します。パッチが何をやっているのか見たい方はこちらのリンク先がわかりやすいでしょう。
# wget https://raw.githubusercontent.com/neuralassembly/raspi/master/gw-450d/gw-450d-rpi-kernel41.patch
# patch -p0 < gw-450d-rpi-kernel41.patch
このパッチ当てがkernel 4.1や4.4でGW-450Dを動かす上で重要なのですが、解説はページ末尾で行い、まずは手順の解説を全て終わらせます。

以上が終わったら、コンパイルを行います。
# make
コンパイルが終わったら、モジュールをインストールします。kernelのバージョン(「4.1.21-v7+」の部分)は自分の環境に応じて読み替えてください。
# cp -p os/linux/mt7650u_sta.ko /lib/modules/4.1.21-v7+/kernel/drivers/net/wireless
# depmod -a
次に、設定ファイルのコピーを行います。
# mkdir -p /etc/Wireless/RT2870STA
# cp RT2870STA.dat /etc/Wireless/RT2870STA/RT2870STA.dat
最後に、ネットワークの設定を記述します。1つ目の命令はテキストエディタleafpadで/etc/network/interfacesを開いていますが、もちろんここはお好みのテキストエディタを用いて構いません。
# leafpad /etc/network/interfaces
(末尾に以下を記述)
allow-hotplug ra0
auto ra0
iface ra0 inet manual
wpa-ssid "your-ssid"
wpa-psk "your-passwd"
以上が終わったら、Planex製GW-450Dをさし、Raspberry Pi 2を再起動します。

Planex製GW-450Dでネットワークにつながっていると思います。

なお、割り当てられるIPアドレスを固定したい場合、「Raspberry PiのIPアドレスを固定する | 「Raspberry Piで学ぶ電子工作」補足情報」で紹介した方法が使えますので必要な方はご参照ください。

Planex製GW-450Dのドライバ用パッチの解説

ここで、用いたパッチgw-450d-rpi-kernel41.patchの解説を簡単にしておきます。

変更されるファイルは下記の6つです。
common/rtusb_dev_id.c
os/linux/config.mk
include/os/rt_linux.h
os/linux/rt_linux.c
sta/sta_cfg.c
common/cmm_info.c

順に見ていきます。

common/rtusb_dev_id.c

Planex製GW-450Dの情報を追加する変更です。これはどのバージョンのkernelでも行います。
@@ -36,6 +36,7 @@
 /* module table */
 USB_DEVICE_ID rtusb_dev_id[] = {
 #ifdef MT76x0
+        {USB_DEVICE(0x2019,0xab31)}, /* GW-450D */
  {USB_DEVICE(0x148F,0x7610)}, /* MT7610U */
  {USB_DEVICE(0x0E8D,0x7610)}, /* MT7610U */
  {USB_DEVICE_AND_INTERFACE_INFO(0x0E8D, 0x7630, 0xff, 0x2, 0xff)}, /* MT7630U */

os/linux/config.mk

これは、HAS_WPA_SUPPLICANTやHAS_NATIVE_WPA_SUPPLICANT_SUPPORTを有効にしています。これもどのバージョンでも行う変更です。
@@ -23,12 +23,12 @@
 
 # Support Wpa_Supplicant
 # i.e. wpa_supplicant -Dralink
-HAS_WPA_SUPPLICANT=n
+HAS_WPA_SUPPLICANT=y
 
 
 # Support Native WpaSupplicant for Network Maganger
 # i.e. wpa_supplicant -Dwext
-HAS_NATIVE_WPA_SUPPLICANT_SUPPORT=n
+HAS_NATIVE_WPA_SUPPLICANT_SUPPORT=y
 
 #Support Net interface block while Tx-Sw queue full
 HAS_BLOCK_NET_IF=n

include/os/rt_linux.h

これは、fsuidとfsgidの型を変更しています。これはkernel 3.18.xでも必要な変更です。こちらを参考にしました。
@@ -277,8 +277,8 @@
 
 typedef struct _OS_FS_INFO_
 {
- int    fsuid;
- int    fsgid;
+ kuid_t    fsuid;
+ kgid_t    fsgid;
  mm_segment_t fs;
 } OS_FS_INFO;
 

os/linux/rt_linux.c

次は、kernel 4.1.x でPlanex製GW-450Dを動かす変更です。長くなるので全てを引用することはしませんが、典型的な変更点は下記の通りです。

関数ポインタ f_op->read がNULLを返すようになったので、かわりに __vfs_read を用いるように変更しています。この変更を行わないと、/etc/Wireless/RT2870STA/RT2870STA.dat の読み込みなどに失敗します。
他にも、f_op->writeを__vfs_writeに変更していますので、詳細はパッチgw-450d-rpi-kernel41.patchを見てください。

こちらこちらを参考にしました。
@@ -1082,9 +1082,8 @@
 
 int RtmpOSFileRead(RTMP_OS_FD osfd, char *pDataPtr, int readLen)
 {
- /* The object must have a read method */
- if (osfd->f_op && osfd->f_op->read) {
-  return osfd->f_op->read(osfd, pDataPtr, readLen, &osfd->f_pos);
+ if (osfd->f_op) {
+  return __vfs_read(osfd, pDataPtr, readLen, &osfd->f_pos);
  } else {
   DBGPRINT(RT_DEBUG_ERROR, ("no file read method\n"));
   return -1;

sta/sta_cfg.cおよびcommon/cmm_info.c

次はgcc4.9で__DATE__マクロと__TIME__マクロを使用するとエラーになることの対策です。2つのファイルを変更しています。
--- sta/sta_cfg.c.orig  2015-10-16 10:51:29.744391038 +0900
+++ sta/sta_cfg.c       2015-10-16 10:52:03.414417321 +0900
@@ -5398,7 +5398,7 @@
             wrq->u.data.length = strlen(extra) + 1; /* 1: size of '\0' */
             break;
         case SHOW_DRVIER_VERION:
-            snprintf(extra, size, "Driver version-%s, %s %s\n", STA_DRIVER_VERSION, __DATE__, __TIME__ );
+            snprintf(extra, size, "Driver version-%s\n", STA_DRIVER_VERSION);
             wrq->u.data.length = strlen(extra) + 1; /* 1: size of '\0' */
             break;
 #ifdef DOT11_N_SUPPORT
--- common/cmm_info.c.orig      2015-10-16 10:51:43.974402146 +0900
+++ common/cmm_info.c   2015-10-16 10:53:08.554468170 +0900
@@ -44,7 +44,7 @@

 #ifdef CONFIG_STA_SUPPORT
        IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
-               DBGPRINT(RT_DEBUG_TRACE, ("Driver version-%s %s %s\n", STA_DRIVER_VERSION, __DATE__, __TIME__));
+               DBGPRINT(RT_DEBUG_TRACE, ("Driver version-%s\n", STA_DRIVER_VERSION));
 #endif /* CONFIG_STA_SUPPORT */

     return TRUE;

I-O DATA製WN-AC433UKについての注意

次に、I-O DATA製WN-AC433UKについての注意を記します。この無線LAN USBアダプタも、上記とほぼ同じ方法で動作させることができます。ただし、ドライバインストール時にcommon/rtusb_dev_id.c内のGW-450Dの下の行に、下記を追加する必要があります。
        {USB_DEVICE(0x04bb,0x0951)}, /* WN-AC433UK */
しかし、私が確認したところ、非常に安定性が悪く、下記の問題がありました。
  • 流せる電流の大きな電源と、高品質な電源ケーブルを用いないと動作しないことが多い
  • Raspberry Piの起動前にI-O DATA製WN-AC433UKを差しておいても、認識に失敗することがある
  • Raspberry Pi起動後にI-O DATA製WN-AC433UKを抜き差しすると、認識に成功するときとしないときとがある。
これらの問題はPlanex製GW-450Dでは起こらないものです。そのため、私としてはPlanex製GW-450Dをお勧めしたいと思います。

Buffalo製WI-U2-433DMのドライバのインストール

Buffalo製WI-U2-433DMについても動作を確認してみました。こちらについては、本ページのカーネル再構築までは共通手順で、ドライバのインストールからが異なります。

以下の作業は「Raspberry Pi 2でWiFiドングル BUFFALO WI-U2-433DMを使う」を参考にしました。

まず、カーネルのコンパイルから再起動までを行なってください。その後、 下記の手順で作業を進めます。
$ cd /usr/src
$ sudo su
# git clone https://github.com/gnab/rtl8812au.git
# cd rtl8812au
ここで、このディレクトリにあるMakefileを下記のように2箇所変更します。
...
CONFIG_PLATFORM_I386_PC = n
...
CONFIG_PLATFORM_ARM_RPI = y
すなわち、CONFIG_PLATFORM_I386_PCの値を「n」に、CONFIG_PLATFORM_ARM_RPIの値を「y」にするだけです。あとは下記のように作業を進めます。kernelのバージョン(「4.1.21-v7+」の部分)は各自読み替えてください。
# make
# cp 8812au.ko /lib/modules/4.1.21-v7+/kernel/drivers/net/wireless
# depmod -a
以上で、Buffalo製WI-U2-433DMを差すと認識されネットワークにつながります。まだあまり使っていませんが、結構安定しているように思えます。

Planex製GW-450Dと比較した場合、無線LANのインターフェイスとしてwlan0が使われるため、通常のWifi設定法がそのまま使えるのも良いですね。


「カラー図解 最新 Raspberry Piで学ぶ電子工作」、「実例で学ぶRaspberry Pi電子工作」を執筆しました。