はじめに
Brainux(Freescale MXS 系 SoC 向け Linux)で RTL8188CUS / RTL8192CU USB WiFi ドングルを使おうとしたところ、想像以上に深い沼にハマりました。
この記事は、カーネル再ビルド(Module.symvers問題)からUSB 抜き差し対応(systemdとudev)までの備忘メモです。
環境
実機
- OS: brainux
- SoC: Freescale MXS
- Kernel: Linux 5.4.149
- WiFi: RTL8188CUS / RTL8192CU USB ドングル (PLANEX GW-USNano2 802.11n Wireless Adapter)
- init: systemd
- NetworkManager: 使用しない
ビルド環境
- OS: Ubuntu 18.04.6 LTS
- Arch: x86_64
- Kernel: 4.15.0-171-generic
第1章:まずは「カーネル」を作るところから
1.1 実機のカーネル設定を取得する
実機(Brainux)で動いているカーネル設定を取得します。
Module.symversを合わせるために必要な情報です。
zcat /proc/config.gz > brain.config
1.2 out-of-tree ビルド環境の構築
実機(Brainux)で取得したbrain.configがビルド環境のホームディレクトリに存在する前提で説明します。
# 必要なパッケージのインストール
sudo apt install -y git build-essential bc bison flex libssl-dev libelf-dev gcc-arm-linux-gnueabi
# ソースコードの取得
mkdir -p ~/brainux-build
cd ~/brainux-build
git clone https://github.com/brain-hackers/linux-brain.git
git clone https://github.com/brain-hackers/buildbrain.git
cd ~/brainux-build/linux-brain
git checkout aa3edcd5fcbc
# ビルド設定
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-
export SRCTREE=~/brainux-build/linux-brain
export KOUT=~/brainux-build/build-linux-brain
# ビルド
mkdir -p "$KOUT"
cp ~/brain.config $KOUT/.config
# 実行前に第2章と第4章を確認
make -C "$SRCTREE" O="$KOUT" oldconfig
make -C "$SRCTREE" O="$KOUT" prepare
make -C "$SRCTREE" O="$KOUT" modules_prepare
第2章:kernelrelease に「+」が付く問題
最初にハマった点(試行錯誤)を記載します。結論だけ知りたい方は原因を参照してください。
cd $KOUT
sed -i 's/^CONFIG_LOCALVERSION_AUTO=.*/CONFIG_LOCALVERSION_AUTO=n/' .config
sed -i 's/^CONFIG_LOCALVERSION=.*/CONFIG_LOCALVERSION="-gaa3edcd5fcbc"/' .config
- make oldconfig実行前に実施
-
.configのCONFIG_LOCALVERSION_AUTOを無効化、CONFIG_LOCALVERSIONを指定
echo -n "5.4.149-gaa3edcd5fcbc" > "$KOUT/include/config/kernel.release"
sed -i 's/^#define UTS_RELEASE.*/#define UTS_RELEASE "5.4.149-gaa3edcd5fcbc"/' "$KOUT/include/generated/utsrelease.h"
- make prepare実行後に実施
- kernel.releaseとutsrelease.hに直接カーネルリリース番号を指定
それでも「+」が付く問題。
make -C "$SRCTREE" O="$KOUT" kernelrelease
5.4.149-gaa3edcd5fcbc+
原因:scripts/setlocalversion
スクリプト(setlocalversion)の中で「+」を付与していました。スクリプトの内容を修正して対応しました。
vi $SRCTREE/scripts/setlocalversion
# 該当箇所を書き換え
if [ -z "`git describe --exact-match`" ]; then
echo "+"
↓
echo ""
fi
修正後、ビルドを再度実行します。
cd $KOUT
make
生成されたモジュールのバージョンも問題ないことを確認しました。
MOD=$(find "$KOUT" -name '*.ko' | head -1)
modinfo -F vermagic "$MOD"
5.4.149-gaa3edcd5fcbc
カーネルモジュールをインストールします。
STAGE=$HOME/brainux-build/stage
KREL="5.4.149-gaa3edcd5fcbc"
mkdir -p "$STAGE"
make -C "$SRCTREE" O="$KOUT" INSTALL_MOD_PATH="$STAGE" modules_install
第3章:ドライバのカーネルモジュール(.ko) が生成されない問題
RTL8188CUS (RTL8192CU)のドライバ(カーネルモジュール)をビルドします。
cd ~/brainux-build
git clone https://github.com/pvaret/rtl8192cu-fixes.git
cd rtl8192cu-fixes
usb_intf.c ファイルにUSB WiFiドングルのベンダーIDとプロダクトIDを追加します。
(PLANEX GW-USNano2の場合、0x2019 と 0xab2a)
# ベンダーIDとプロダクトIDを追加
vi os_dep/linux/usb_intf.c
# 差分を確認
git diff
diff --git a/os_dep/linux/usb_intf.c b/os_dep/linux/usb_intf.c
index 3115eac..432da1c 100755
--- a/os_dep/linux/usb_intf.c
+++ b/os_dep/linux/usb_intf.c
@@ -138,6 +138,7 @@ static void rtw_dev_remove(struct usb_interface *pusb_intf);
{USB_DEVICE(0x04F2, 0xAFFB)},/* XAVI - XAVI */ \
{USB_DEVICE(0x04F2, 0xAFFC)},/* XAVI - XAVI */ \
{USB_DEVICE(0x2019, 0x1201)},/* Planex - Vencer */ \
+ {USB_DEVICE(0x2019, 0xab2a)},/* PLANEX GW-USNano2 */ \
/****** 8192CUS Dongle ********/ \
{USB_DEVICE(0x2001, 0x3307)},/* D-Link - Cameo */ \
{USB_DEVICE(0x2001, 0x330A)},/* D-Link - Alpha */ \
準備ができたのでビルドを実行します。
make -C "$SRCTREE" O="$KOUT" M="$PWD" modules
make: Entering directory '/home/ubuntu/brainux-build/linux-brain'
make[1]: Entering directory '/home/ubuntu/brainux-build/build-linux-brain'
Building modules, stage 2.
MODPOST 0 modules
make[1]: Leaving directory '/home/ubuntu/brainux-build/build-linux-brain'
make: Leaving directory '/home/ubuntu/brainux-build/linux-brain'
ubuntu@tk2-401-41617:~/brainux-build/rtl8192cu-fixes$ pwd
モジュールが生成されません。(MODPOST 0 modules)
原因
ドライバ側の Makefile が kbuild 形式ではないことが原因です。
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-
export KSRC=$HOME/brainux-build/linux-brain
export KOUT=$HOME/brainux-build/build-linux-brain
make ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE KSRC="$KSRC" KBUILD_OUTPUT="$KOUT" V=1
第4章:ドライバのビルドでUndefined symbolが発生
ドライバのビルドは途中まで進み、モジュール(.ko)は生成されましたが、Undefined symbolのエラーが大量に発生します。
(省略)
ERROR: "register_netdevice" [/home/ubuntu/brainux-build/rtl8192cu-fixes/8192cu.ko] undefined!
ERROR: "msleep" [/home/ubuntu/brainux-build/rtl8192cu-fixes/8192cu.ko] undefined!
ERROR: "complete_and_exit" [/home/ubuntu/brainux-build/rtl8192cu-fixes/8192cu.ko] undefined!
ERROR: "wiphy_free" [/home/ubuntu/brainux-build/rtl8192cu-fixes/8192cu.ko] undefined!
(省略)
調査
grep msleep $KOUT/Module.symvers
(何も出力されない)
原因
使われていないと判断された symbol が削除されていたのが原因でした。
CONFIG_TRIM_UNUSED_KSYMS=y
.configの設定を変更してカーネルビルドを再実行します。
sed -i 's/^CONFIG_TRIM_UNUSED_KSYMS=.*/CONFIG_TRIM_UNUSED_KSYMS=n/' "$KOUT/.config"
make -C "$SRCTREE" O="$KOUT" olddefconfig
make -C "$SRCTREE" O="$KOUT" -j"$(nproc)" zImage modules
ドライバのビルドを再度実行し、エラーが発生しないことを確認します。
make ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE KSRC="$KSRC" KBUILD_OUTPUT="$KOUT" USER_MODULE_NAME=8192cu V=1
生成されたモジュールのバージョンも問題ないことを確認しました。
find . -maxdepth 3 -name '*.ko' -ls
30292091 612 -rw-rw-r-- 1 ubuntu ubuntu 623356 Jan 3 20:59 ./8192cu.ko
modinfo -F vermagic ./8192cu.ko
5.4.149-gaa3edcd5fcbc preempt mod_unload modversions ARMv5 p2v8
生成されたドライバモジュールをカーネルモジュールへ追加します。
mkdir -p "$STAGE/lib/modules/$KREL/extra"
cp ./*.ko "$STAGE/lib/modules/$KREL/extra/"
tar -C "$STAGE/lib/modules" -czf "$HOME/brainux-build/modules-$KREL.tgz" "$KREL"
ls -lh "$HOME/brainux-build/modules-$KREL.tgz"
-rw-rw-r-- 1 ubuntu ubuntu 330K Jan 3 21:02 /home/ubuntu/brainux-build/modules-5.4.149-gaa3edcd5fcbc.tgz
生成されたmodules-5.4.149-gaa3edcd5fcbc.tgzをデバイス(Brainux)へコピーします。(デバイスの/tmpへコピーした前提で説明します)
デバイス側でモジュールを展開します。
sudo mkdir -p /lib/modules
sudo tar -C /lib/modules -xzf /tmp/modules-5.4.149-gaa3edcd5fcbc.tgz
sudo depmod -a
第5章:カーネルを入れ替える
ドライバモジュール(8192cu.ko)をロードするためにはデバイスのカーネルを入れ替える必要があります。
ls -lh ~/brainux-build/build-linux-brain/arch/arm/boot/zImage
-rwxrwxr-x 1 ubuntu ubuntu 5.7M Jan 3 20:51 /home/ubuntu/brainux-build/build-linux-brain/arch/arm/boot/zImage
zImageをデバイス(Brainux)へコピーします。(デバイスの/tmpへコピーした前提で説明します)
# ro(読み出し専用)からrw(読み書き可能)へ再マウント
sudo mount -o remount,rw /boot
# バックアップ
sudo cp -av /boot/zImage /boot/zImage.bak.$(date +%Y%m%d%H%M%S)
# 新しいカーネルをコピー (エラーが出るが問題なし。cp: failed to preserve ownership for '/boot/zImage': Operation not permitted)
sudo cp -av /tmp/zImage /boot/zImage
# ro(読み出し専用)へ再マウントして再起動
sudo mount -o remount,ro /boot
sudo reboot
デバイスを再起動します。USB WiFiドングルをデバイスへ挿して認識されることを確認します。
(wlx0022cffbd898として認識された例)
$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
3: enx0080808a41fc: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:80:80:8a:41:fc brd ff:ff:ff:ff:ff:ff
4: wlx0022cffbd898: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN mode DORMANT group default qlen 1000
link/ether 00:22:cf:fb:d8:98 brd ff:ff:ff:ff:ff:ff
第6章:WiFi接続
WiFiのSSID、パスワードを設定し、WiFi接続できることを確認します。
sudo wpa_passphrase "SSID" "PASSWORD" | sudo tee /etc/wpa_supplicant.conf >/dev/null
sudo wpa_supplicant -B -i wlx0022cffbd898 -c /etc/wpa_supplicant.conf
sudo dhclient wlx0022cffbd898
確認
# IPアドレス(inet)を取得できていることを確認
sudo ifconfig
wlx0022cffbd898: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.3.11 netmask 255.255.255.0 broadcast 192.168.3.255
ether 00:22:cf:fb:d8:98 txqueuelen 1000 (Ethernet)
RX packets 128 bytes 26144 (25.5 KiB)
RX errors 0 dropped 62 overruns 0 frame 0
TX packets 49 bytes 7686 (7.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
# pingでインターネットへの到達性を確認
ping -c 3 8.8.8.8
第7章:udev + systemd で再設計
USBドングルを抜き挿ししても動作するように udev → systemd へ連携するように再設計します。
オリジナルのwpa_supplicant.serviceを停止・マスクします。(二重起動の抑止)
sudo systemctl disable --now wpa_supplicant.service
sudo systemctl mask wpa_supplicant.service
省電力設定をOFFにします。(念のため)
options 8192cu rtw_power_mgnt=0 rtw_enusbss=0 rtw_ips_mode=0
udevを設定します。
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="00:22:cf:fb:d8:98", NAME="wifi0"
ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="00:22:cf:fb:d8:98", \
TAG+="systemd"
設定を反映します。
sudo udevadm control --reload
sudo udevadm trigger
systemdを設定します。
[Unit]
[Unit]
Description=WiFi (%I)
BindsTo=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
Wants=sys-subsystem-net-devices-%i.device
[Service]
Type=simple
ExecStartPre=/sbin/ip link set %i up
ExecStart=/sbin/wpa_supplicant -i %i -c /etc/wpa_supplicant.conf
ExecStartPost=/bin/sh -c 'for i in $(seq 1 30); do iw dev %i link | grep -q "Connected" && exit 0; sleep 1; done; exit 1'
ExecStartPost=/bin/sleep 3
ExecStartPost=-/sbin/dhclient %i
ExecStop=-/sbin/dhclient -r %i
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
ビルド済カーネルモジュール
さいごに
有線LAN(AX887728LF)が問題なく使えていたので、正直、途中で何度も「もう有線でいいのでは?」と思いましたが、最後までやり切ってよかったです。
同じ SoC・古い WiFi チップ・組み込み Linux を触っている人の助けになれば幸いです。
