Raspberry Pi Zero用のカーネルをクロスコンパイルし、ドライバモジュールを作成する

Raspberry Pi Zeroを5GHzの無線LANに参加させたいのですが、2015/11/21にリリースされたJessieだと、手持ちのWifiアダプタ[GW-450S](https://www.planex.co.jp/products/gw-450s/)は使用できませんでした。githubにあるドライバ (https://github.com/gnab/rtl8812au) をカーネルモジュールとしてコンパイルする必要があるようです。

本稿では、他のマシンでRaspberry Pi Zero用のカーネルをクロスコンパイルする手順と、今回のGW-450Sを例に外部ドライバをカーネルモジュールとして導入する手順を紹介したいと思います。

※カーネルのコンパイルをRaspberry Pi Zeroでも行えますが、Raspberry Pi Zeroのカーネルイメージにデフォルトで使われている.configを用いたところ、14時間かかりました。。。作成モジュールが相当多いため、時間がかかるようです。

---

2017/2追記
Raspberry Pi Zero上で同一バージョンのカーネルソースを取得し、モジュールのみコンパイル、という方法で短時間で達成できます。

参考
https://www.max2play.com/en/forums/topic/howto-raspberry-pi-3-realtek-802-11ac-rtl8812au/

こちらの方がオススメです。

---


環境はUbuntu 14.04 LTSです。以下、64bitマシンで作業する前提で記述します。


# カーネルのクロスコンパイル

「石を売る」さんの「[Raspberry Pi2のカーネルの再構築とGW-450Dを使えるようにする。](http://blog.mamemomonga.com/2015/02/raspberry-pi2gw-450d.html)」を参考にさせていただきました。

## 作業フォルダの作成

```
$ mkdir -p $HOME/src/Raspberry_Pi_Zero
$ cd $HOME/src/Raspberry_Pi_Zero
```

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

たまたまIntel Edisonのカーネルビルドも行っていたので、インストールが必要なパッケージはありませんでしたが、以下のパッケージが必要になります。

```
$ sudo aptitude install build-essential git openssh-server ncurses-dev
```

## toolchainとkernel sourceのダウンロード

クロスコンパイルに必要なツールと、Raspberry Piのカーネルソースをダウンロードします。

```
$ git clone --depth=1 git://github.com/raspberrypi/tools.git
$ git clone --depth=1 git://github.com/raspberrypi/linux.git
```

## 環境変数の設定

```
$ ls tools/arm-bcm2708
$ export PATH=`pwd`/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH
$ export ARCH=arm
$ export CROSS_COMPILE=arm-linux-gnueabihf-
```

クロスコンパイラが使えるか確認します。

```
$ arm-linux-gnueabihf-gcc -v
```

## Raspberry Pi Zeroより.config取得

Raspberry Pi Zeroから取得します。

```
$ cd linux
$ ssh pi@(Raspberry Pi Zeroのアドレス) 'sudo modprobe configs'
$ ssh pi@(Raspberry Pi Zeroのアドレス) 'zcat /proc/config.gz' > config-pizero
$ mv config-pizero .config
```

## カーネルに命名

Makefile内の EXTRAVERSION を指定すると、カーネル名の後ろに名前がつけられます。今回は mod としました。カーネルのバージョンが4.1.15だったので、4.1.15-modになります。(後でカーネル名やフォルダ名としてまた出てきます)

```
$ vim Makefile
EXTRAVERSION = -mod
```

## menuconfigの実行

別途、使用したいデバイス等があれば設定します。menuconfigのお作法に倣って設定、保存をしてください。

```
$ make menuconfig
```

## カーネルのビルド

```
$ make -j 4
```
-jの後の数字はCPUコア数に応じて変えます。2倍程度が一般的です。
Core2 Duo T7600(2.33GHz)のデュアルコアなマシンで30分弱で終わりました。

## モジュールとカーネルをまとめる

```
$ cat include/config/kernel.release
4.1.15-mod+
$ export KERNEL_RELEASE=`cat include/config/kernel.release`
$ export INSTALL_MOD_PATH=../$KERNEL_RELEASE
$ mkdir -p $INSTALL_MOD_PATH
$ mkdir -p ../$KERNEL_RELEASE/boot
$ make modules
$ make modules_install
$ cp arch/arm/boot/Image ../$KERNEL_RELEASE/boot/kernel-$KERNEL_RELEASE.img
$ cd ..
```

## Raspberry Pi Zeroで一度動作確認

出来上がったカーネルが動作するか確認します。

```
$ cd $KERNEL_RELEASE
$ tar zcvf $KERNEL_RELEASE.tar.gz boot lib
$ scp $KERNEL_RELEASE.tar.gz pi@(Raspberry Pi ZeroのIPアドレス):/home/pi/
$ ssh pi@(Raspberry Pi ZeroのIPアドレス)
```

ログイン後

```
$ sudo tar zxC / -f 4.1.15-mod+.tar.gz
$ ls /boot
(今回作成したkernel-4.1.15-mod+.imgがあることを確認)

$ sudo vim /boot/config.txt
末尾に以下を追加
kernel=kernel-4.1.15-mod+.img
```

Raspberry Pi Zeroを再起動します。`uname -a`で今回設定したカーネル名が表示されるか確認してください。
起動に失敗した場合、SDカードを取り出して、先ほどのconfig.txtを元に戻せば修正前の状態で起動できます。

# rtl8812モジュールのビルド

前項の「カーネルのクロスコンパイル」を実施済みが前提になります。このパートのみ再度行う場合は、前項中にある「環境変数の設定」を実施して、クロスコンパイラが使用できる状態にしてください。

githubにあるドライバのページ(https://github.com/gnab/rtl8812au) にある手順に従います。

## ドライバソースのダウンロード

```
$ cd $HOME/src/Raspberry_Pi_Zero
$ git clone https://github.com/gnab/rtl8812au
```

## 設定の変更

```
$ vim Makefile
```

ターゲットをI386_PCからARM_RPIに変更します。

```
(変更前)
CONFIG_PLATFORM_I386_PC = y
...
CONFIG_PLATFORM_ARM_RPI = n

(変更後)
CONFIG_PLATFORM_I386_PC = n
...
CONFIG_PLATFORM_ARM_RPI = y
```

続いて、クロスコンパイラ(CROSS_COMPILE)、カーネルバージョン(KVER)とこれに付随して指定されるディレクトリ(KSRC, MODDESTDIR)を変更します。ディレクトリは絶対参照から相対参照に切り替えています。

カーネルバージョンは「4.1.15-mod+」を決め打ちしていますので、適宜変更してください。(以後も登場します)

```
(変更前)
ifeq ($(CONFIG_PLATFORM_ARM_RPI), y)
EXTRA_CFLAGS += -DCONFIG_LITTLE_ENDIAN
ARCH := arm
CROSS_COMPILE :=
KVER  := $(shell uname -r)
KSRC ?= /lib/modules/$(KVER)/build
MODDESTDIR := /lib/modules/$(KVER)/kernel/drivers/net/wireless/
endif

(変更後)
ifeq ($(CONFIG_PLATFORM_ARM_RPI), y)
EXTRA_CFLAGS += -DCONFIG_LITTLE_ENDIAN
ARCH := arm
CROSS_COMPILE := arm-linux-gnueabihf-
KVER  := 4.1.15-mod+
KSRC := ../$(KVER)/lib/modules/$(KVER)/build
MODDESTDIR := ../$(KVER)/lib/modules/$(KVER)/kernel/drivers/net/wireless/
endif
```

## ドライバモジュールのビルド

```
$ make
```

## ドライバのコピー

```
$ cp 8812au.ko ../4.1.15-mod+/lib/modules/4.1.15-mod+/kernel/drivers/net/wireless
```

## Raspberry Pi Zeroへの転送

前項「Raspberry Pi Zero側で一度動作確認」を実施して、カーネルイメージを再送、再起動してください。

## 再起動後

```
$ sudo depmod
```

このあと、GW-450SをUSBポートに刺すと無事使えるようになるはずです。

# おまけ

## カーネルコンパイルをRaspberry Pi Zero上で行いたい場合

いばらの道です。

```
$ cd $HOME
$ bash -c "$(curl -fsSSL https://raw.githubusercontent.com/moutend/raspi-kernel/master/raspi-kernel)"
$ cd /usr/src/linux-...
$ sudo modprobe configs
$ zcat /proc/config.gz > .config
$ time make

real    742m7.551s
user    700m51.830s
sys     29m42.700s

$ make modules
$ make modules_install
```

## 現在使用中のカーネルからconfigを取得

configを取得できるカーネルコンフィグがあることを初めて知りました。

```
CONFIG_IKCONFIG=m
CONFIG_IKCONFIG_PROC=y
```

このオプションが組み込まれたカーネルで、以下を実行します。

```
$ sudo modprobe configs
$ zcat /proc/config.gz > .config
```

# Todo

最初のインストールイメージに対して更新したい。
PiTFTの変更を反映したい(本稿で作成するGW-450S等のモジュールと一緒に用いたい)