2015年9月2日水曜日

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

本ページで目指すもの

本ページでは、Raspberry Piを5GHz帯のネットワークに接続することを目標にします。必要なものは下記のどれかの無線LAN USBアダプタです。

はじめに

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のドライバのインストール
最初に行うカーネルの再構築は全ての無線LAN USBアダプタで必要となりますのでご注意ください。

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

私が検証に用いたのはRaspberry Pi 2 および 3ですが、ページ中の読み替えの指示に従えばRaspberry Pi Model B+でも可能なはずです。


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

まず、kernel再構築に必要なパッケージをインストールします。
$ sudo apt-get update
$ sudo apt-get install gcc make bc screen ncurses-dev
お好みで、下記のコマンドによりkernelも最新にしておきます。なお、2017年5月現在、kernelを最新にすると4.9系列にアップデートされます。
$ sudo rpi-update
$ sudo reboot

カーネルのソースを入手

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

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

(ビルド方法はなるべく公式準拠になるようにしています)
$ 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 が動作するようになりました。ここから、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、4.9で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 を再起動します。

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でも行います。実はI-O DATA製WN-AC433UK用の変更も加えています。
@@ -36,6 +36,8 @@
 /* module table */
 USB_DEVICE_ID rtusb_dev_id[] = {
 #ifdef MT76x0
+ {USB_DEVICE(0x2019,0xab31)}, /* GW-450D */
+ {USB_DEVICE(0x04bb,0x0951)}, /* WN-AC433UK */
  {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についての注意を記します。上記のPlanex製GW-450D用の手順を行うことで、この無線LAN USBアダプタを動作させることができます。筆者の場合、Raspberry Pi 3で動作を確認することができました。ただし、過去にRaspberry Pi 2で試したところ、下記のように非常に安定性が悪いことがありました。
  • 流せる電流の大きな電源と、高品質な電源ケーブルを用いないと動作しないことが多い
  • Raspberry Piの起動前にI-O DATA製WN-AC433UKを差しておいても、認識に失敗することがある
  • Raspberry Pi起動後にI-O DATA製WN-AC433UKを抜き差しすると、認識に成功するときとしないときとがある。
Pi 3で安定動作したので今となってはあまり気にしていませんが、似たような状況の方はご参考ください。

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と比較した場合、Raspberry Pi 2の場合は無線LANのインターフェイスとしてwlan0が使われるため、通常のWifi設定法がそのまま使えるのが良いですね。

なお、Raspbedrry Pi 3の場合、下記の点が厄介です。
  • オンボードのWifiデバイスとWI-U2-433DMに対してwlan0とwlan1のように同じデバイス名が使われ、さらにこの番号が変わることがある
  • wlan0とwlan1に対して、同じ設定ファイル (/etc/wpa_supplicant/wpa_supplicant.conf) が使われるので、2.4GHzのルーターに接続しようとすると、両方のデバイスがつながってしまう
この問題に対しては、恐らく下記で対処できると思います(簡略化して書きます) 設定ファイル /lib/udev/rules.d/75-persistent-net-generator.rules を管理者権限のテキストエディタで開き、下記の太字部を追加して保存します。さらにRaspberry Piを再起動します。
# device name whitelist
 KERNEL!="ath*|msh*|ra*|sta*|ctc*|lcs*|hsi*|wlan*", \
                                         GOTO="persistent_net_generator_end"
再起動が終わると、/etc/udev/rules.d/70-persistent-net.rules というファイルが生成されています。 これを管理者権限のテキストエディタで開くと、下記のようになっています。
# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.

# Unknown net device (/devices/platform/soc/3f300000.mmc/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0) (brcmfmac)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="xx:xx:xx:xx:xx:xx", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan0"

# USB device 0x:0x (rtl8812au)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="xx:xx:xx:xx:xx:xx", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan1"
ATTR{address}の部分は人により異なるので伏字にしました。 「# Unknown net device」に対応するNAMEがwlan0、「# USB device 0x:0x (rtl8812au)」に対応するNAMEがwlan1となっていることがポイントで、前者がRaspberry Piのボード上のWifiデバイス、後者がWI-U2-433DMを指し、それぞれwlan0とwlan1に指定されています。この/etc/udev/rules.d/70-persistent-net.rules が存在することで、以後デバイスの名前が固定されます。wlan0とwlan1の名前を入れ替えたい場合は適切に編集して保存してください。

以上で、デバイス名の固定が完了しました。後は、二つのデバイスでネットワークの設定ファイルを分けるとよいでしょう。管理者権限のテキストエディタで /etc/network/interfaces を開きます。wlan0とwlan1とで同一の設定ファイル「/etc/wpa_supplicant/wpa_supplicant.conf」が指定されていることがわかります。この設定ファイルを二つのデバイスで別のものにするとか、あるいはボード上の無線LANデバイスwlan0についての設定3行をコメントアウトするなどが良いでしょう。
allow-hotplug wlan0
iface wlan0 inet manual
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

allow-hotplug wlan1
iface wlan1 inet manual
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf



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


4 件のコメント:

  1. 大変参考になりました。
    WN-AC433UKも元気に動きました(RPi3)
    3なので供給される電力も5V/2.5Aとなったので安定しているのかもしれません。

    もしご存知であれば、channelの固定化をご存知であればご教示いただけないでしょうか。ネットワーク解析勉強用のSnifferアプライアンス化したいと考えている次第です。

    返信削除
    返信
    1. 試したわけではありませんが、「恐らくこうではないか」
      という方法を記します。

      gw-450d_driver_linux_v3002.zipの中に含まれる
      README_STA_usbというテキストファイルの中に、
      設定ファイル
      /etc/Wireless/RT2870STA/RT2870STA.dat
      を用いた様々なパラメータの設定方法が書かれています。

      その中に
      「# 2.) set Channel to "0" for auto-select on Infrastructure mode」
      という行があります。これに基づき、
      /etc/Wireless/RT2870STA/RT2870STA.dat
      の中の
      Channel=0
      という行の数字を0以外に変更すれば良いのではないでしょうか。

      README_STA_usbというファイルには他にも様々なパラメータの説明があるようです。

      削除
    2. ご丁寧にありがとうございます。
      readme確認してみます。
      iwconfigだとリブート後にChannel「1」に戻ってしまい困っていた次第です。

      削除
  2. readme確認し、例えばchannel=36とすれば、reboot後にも36チャンネル固定できました。
    複数枚同じドングルを挿した場合に、それぞれがチャンネル固定できるのかは、もう一つのドングルを買ってから試してみたいと思います。m(_ _)m感謝

    ikeda

    返信削除