Lichee Nano - Wi-Fi接続とクロスコンパイルしたプログラムの実行

# Lichee Nano

Lichee Nano は Allwinner F1C100s を搭載した開発ボードです.
[Lichee Nano](http://nano.lichee.pro/)

公式サイトの方法では, USB が認識できない等の問題があったため Linux 5.2 のビルドを行い USB の認識及び Wi-Fi 接続までの方法とクロスコンパイルしたプログラムの実行までを記載.

![output.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33399/7aca66c6-520c-9a70-8af0-dd0461b1e2dd.jpeg)

# ビルド環境 (uboot, Linux 5.2, buildroot)
公式サイトでも記されている通り, ビルド環境は `Ubuntu 16.04 LTS 64bit` の使用をおすすめします.
使用している環境が Arch Linux であったため, Docker で Ubuntu 16.04 イメージを使用してビルドを行いました.
Ubuntu 16.04 のイメージの取得, 実行は下記リンクを参照してください.
[DockerでUbuntu 16.04 LTSのイメージを利用してみよう](https://weblabo.oscasierra.net/docker-ubuntu1604/)
また, Docker を利用した方法ではビルドしたファイルの転送を事前に考えておく必要があります. `docker run` コマンドでコンテナを作成する際に `-v` オプションを使用してホストディレクトリをコンテナ内にマウントする方法が一番楽かもしれません.
ちなみに私は, すべてビルドし終えたあとにファイル転送が必要なことに気づいたため `sshd`, `scp` を使用してファイルの送受信を行いました.


# uboot, Linux, buildroot のビルド
各ビルド対象ビルド時にエラーが発生する場合, 表示される内容を確認して問題の対処を行ってください.
Ubuntu 16.04 LTS を使用している場合, ビルドに必要なパケージがインストールされていないことをはじめに疑ってください.

## Toolchain のインストール
下記の方法より Toolchain のダウンロードと展開, `/opt` ディレクトリへの移動を行います.

```sh
wget http://releases.linaro.org/components/toolchain/binaries/7.2-2017.11/arm-linux-gnueabi/gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi.tar.xz

tar -vxJf gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi.tar.xz
sudo cp -r ./gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi /opt/
```

次に, `/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabi/bin` へのパスを通します.

```sh
export PATH=$PATH:/opt/gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi/bin
```

`~/.bashrc` への書き込みを行う方法でも問題ありません. 書き込み後設定の反映を行ってください.

```sh
source ~/.bashrc
```

## uboot
uboot のクローンを作成
`Lichee-Pi/u-boot.git` プロジェクトから `nano-v2018.01` ブランチのクローンを作成.

```sh
git clone https://github.com/Lichee-Pi/u-boot.git -b nano-v2018.01
```

Micro SD(TF Card) より開始する u-boot のビルド

```sh
ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make licheepi_nano_defconfig
```

`boot.scr` 生成のため, 下記 `boot.cmd` を保存.

```boot.cmd
setenv bootargs console=tty0 console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 rw
load mmc 0:1 0x80C00000 suniv-f1c100s-licheepi-nano.dtb
load mmc 0:1 0x80008000 zImage
bootz 0x80008000 - 0x80C00000
```

`u-boot/tools/mkimage` より `boot.scr` の生成.

```sh
tools/mkimage -C none -A arm -T script -d boot.cmd boot.scr
```

## Linux
Linux(nano-5.2-tf) のクローンを作製

```sh
git clone https://github.com/Lichee-Pi/linux.git --depth=1 -b nano-5.2-tf
```

デフォルト構成ファイルのダウロードと名称の変更

```sh
# wget http://nano.lichee.pro/_static/step_by_step/lichee_nano_linux.config
wget https://raw.githubusercontent.com/sipeed/Nano-Doc-Backup/master/_static/step_by_step/lichee_nano_linux.config
mv lichee_nano_linux.config .config
```

もしくは

```sh
#curl -L http://nano.lichee.pro/_static/step_by_step/lichee_nano_linux.config > .config
curl -L 'https://raw.githubusercontent.com/sipeed/Nano-Doc-Backup/master/_static/step_by_step/lichee_nano_linux.config' > .config
```

コンパイル

```sh
ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make
```

### 任意: USB デバイスを認識させる
コンパイルを実行する前に, USB デバイスを認識させるためのパッチを当てる必要があります.
USBデバイスを認識させる方法は, 下記の記事に詳細が記載されています.

[LicheePi NanoでUSBデバイスを認識させる - Qiita](https://qiita.com/aselectroworks/items/dc345b1b3fc84ec3b7f1)
[whycna.com](https://whycan.com/t_3087.html)

[[v2,5/5] ARM: suniv: f1c100s: enable USB on Lichee Pi Nano - Patchwork](https://patchwork.kernel.org/project/linux-usb/patch/20200331170219.267732-6-thirtythreeforty@gmail.com/)
ここで提示されているパッチは, 上記の中に含まれていますので参考まで.

```patch
diff --git a/arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dts b/arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dts
index ca644541a..890a57717 100644
--- a/arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dts
+++ b/arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dts
@@ -20,7 +20,7 @@
 	};
 
 	panel: panel {
-		compatible = "lg,lb070wv8", "simple-panel";
+		compatible = "qiaodian,qd43003c0-40", "simple-panel";
 		#address-cells = <1>;
 		#size-cells = <0>;
 		enable-gpios = <&pio 4 6 GPIO_ACTIVE_HIGH>;
@@ -79,3 +79,17 @@
 	pinctrl-0 = <&uart0_pe_pins>;
 	status = "okay";
 };
+
+&otg_sram {
+	status = "okay";
+};
+
+&usb_otg {
+	dr_mode = "otg";
+	status = "okay";
+};
+
+&usbphy {
+	usb0_id_det-gpio = <&pio 4 2 GPIO_ACTIVE_HIGH>; /* PE2 */
+	status = "okay";
+};
diff --git a/arch/arm/boot/dts/suniv-f1c100s.dtsi b/arch/arm/boot/dts/suniv-f1c100s.dtsi
index 26d8e5577..437bdf7d9 100644
--- a/arch/arm/boot/dts/suniv-f1c100s.dtsi
+++ b/arch/arm/boot/dts/suniv-f1c100s.dtsi
@@ -221,6 +221,31 @@
 			resets = <&ccu RST_BUS_UART2>;
 			status = "disabled";
 		};
+		usb_otg: usb@1c13000 {
+			compatible = "allwinner,suniv-musb";
+			reg = <0x01c13000 0x0400>;
+			clocks = <&ccu CLK_BUS_OTG>;
+			resets = <&ccu RST_BUS_OTG>;
+			interrupts = <26>;
+			interrupt-names = "mc";
+			phys = <&usbphy 0>;
+			phy-names = "usb";
+			extcon = <&usbphy 0>;
+			allwinner,sram = <&otg_sram 1>;
+			status = "disabled";
+		};
+
+		usbphy: phy@1c13400 {
+			compatible = "allwinner,suniv-usb-phy";
+			reg = <0x01c13400 0x10>;
+			reg-names = "phy_ctrl";
+			clocks = <&ccu CLK_USB_PHY0>;
+			clock-names = "usb0_phy";
+			resets = <&ccu RST_USB_PHY0>;
+			reset-names = "usb0_reset";
+			#phy-cells = <1>;
+			status = "disabled";
+		};
 		fe0: display-frontend@1e00000 {
 			compatible = "allwinner,suniv-f1c100s-display-frontend";
 			reg = <0x01e00000 0x20000>;
diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
index 856927382..3cd9e946a 100644
--- a/drivers/phy/allwinner/phy-sun4i-usb.c
+++ b/drivers/phy/allwinner/phy-sun4i-usb.c
@@ -98,6 +98,7 @@
 #define POLL_TIME			msecs_to_jiffies(250)
 
 enum sun4i_usb_phy_type {
+	suniv_phy,
 	sun4i_a10_phy,
 	sun6i_a31_phy,
 	sun8i_a33_phy,
@@ -859,6 +860,14 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct sun4i_usb_phy_cfg suniv_cfg = {
+	.num_phys = 1,
+	.type = suniv_phy,
+	.disc_thresh = 3,
+	.phyctl_offset = REG_PHYCTL_A10,
+	.dedicated_clocks = true,
+};
+
 static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
 	.num_phys = 3,
 	.type = sun4i_a10_phy,
@@ -973,6 +982,7 @@ static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
 };
 
 static const struct of_device_id sun4i_usb_phy_of_match[] = {
+	{ .compatible = "allwinner,suniv-usb-phy", .data = &suniv_cfg },
 	{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
 	{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
 	{ .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg },
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index 832a41f9e..83f3fa5b0 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -714,14 +714,17 @@ static int sunxi_musb_probe(struct platform_device *pdev)
 	INIT_WORK(&glue->work, sunxi_musb_work);
 	glue->host_nb.notifier_call = sunxi_musb_host_notifier;
 
-	if (of_device_is_compatible(np, "allwinner,sun4i-a10-musb"))
+	if (of_device_is_compatible(np, "allwinner,sun4i-a10-musb") ||
+	    of_device_is_compatible(np, "allwinner,suniv-musb")) {
 		set_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags);
+	}
 
 	if (of_device_is_compatible(np, "allwinner,sun6i-a31-musb"))
 		set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags);
 
 	if (of_device_is_compatible(np, "allwinner,sun8i-a33-musb") ||
-	    of_device_is_compatible(np, "allwinner,sun8i-h3-musb")) {
+	    of_device_is_compatible(np, "allwinner,sun8i-h3-musb") ||
+	    of_device_is_compatible(np, "allwinner,suniv-musb")) {
 		set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags);
 		set_bit(SUNXI_MUSB_FL_NO_CONFIGDATA, &glue->flags);
 	}
@@ -812,6 +815,7 @@ static int sunxi_musb_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id sunxi_musb_match[] = {
+	{ .compatible = "allwinner,suniv-musb", },
 	{ .compatible = "allwinner,sun4i-a10-musb", },
 	{ .compatible = "allwinner,sun6i-a31-musb", },
 	{ .compatible = "allwinner,sun8i-a33-musb", },
```

### 任意: MT7601U チップが搭載された Wi-Fi 子機の認識
はじめに, Raspberry Pi 対象の記事であるが MT7601U 対象の記事.
[Raspberry Piでmt7601uの無線LAN子機が認識されない問題を解決 | クソざこCoding](https://www.zacoding.com/post/raspberrypi-mt7601u-error/)

私の対処に問題があったのか, 上記の方法では解決できなかった. 出典は失念してしまいましたがエラーメッセージを検索して成功した例を下記のパッチに記します.
このパッチを当てた状態で再度ビルドを行ってください.
最後に MT7601U を動作させるためにドライバが必要となります. Meditek 社の MT7601U のページよりダウンロードを行います.
[MediaTek | MT7601U](https://www.mediatek.jp/products/mt7601u)
ダウンロードしたファイル `DPO_MT7601U_LinuxSTA_3.0.0.4_20130913.tar.bz2` を展開し, `DPO_MT7601U_LinuxSTA_3.0.0.4_20130913/mcu/bin/MT7601_formal_1.7.bin` を Lichee Nano の `/lib/firmware/mt7601u.bin` として保存を行います.

```mt76x0_usb.patch
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
index 2dc67e6..63bb552 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
@@ -136,11 +136,13 @@ static const struct ieee80211_ops mt76x0u_ops = {
 	.release_buffered_frames = mt76_release_buffered_frames,
 };
 
-static int mt76x0u_init_hardware(struct mt76x02_dev *dev)
+//static int mt76x0u_init_hardware(struct mt76x02_dev *dev)
+static int mt76x0u_init_hardware(struct mt76x02_dev *dev, bool reset)
 {
 	int err;
 
-	mt76x0_chip_onoff(dev, true, true);
+	//mt76x0_chip_onoff(dev, true, true);
+	mt76x0_chip_onoff(dev, true, reset);
 
 	if (!mt76x02_wait_for_mac(&dev->mt76))
 		return -ETIMEDOUT;
@@ -173,7 +175,8 @@ static int mt76x0u_register_device(struct mt76x02_dev *dev)
 	if (err < 0)
 		goto out_err;
 
-	err = mt76x0u_init_hardware(dev);
+	//err = mt76x0u_init_hardware(dev);
+	err = mt76x0u_init_hardware(dev, true);
 	if (err < 0)
 		goto out_err;
 
@@ -309,7 +312,8 @@ static int __maybe_unused mt76x0_resume(struct usb_interface *usb_intf)
 	if (ret < 0)
 		goto err;
 
-	ret = mt76x0u_init_hardware(dev);
+	//ret = mt76x0u_init_hardware(dev);
+	ret = mt76x0u_init_hardware(dev, false);
 	if (ret)
 		goto err;
```

変更部分をコメントアウトして修正したため, 多少変なパッチファイルとなっています.

## buildroot
buildroot のダウンロードと展開

```sh
wget https://buildroot.org/downloads/buildroot-2017.08.tar.gz
tar xvf buildroot-2017.08.tar.gz
cd buildroot-2017.08/
```

[ルートファイルシステムのコンパイル](http://nano.lichee.pro/build_sys/rootfs.html) でデフォルト構成ファイルがダウンロードする指示がありますがアクセスができないため, `make menuconfig` より構成を行ってください.
最小の構成は, 上記のページに記されているもののみです. 一応これだけでも動作の確認は行なえます.
buildroot のコンパイルには時間がかかります.

```
Target options  --->
    Target Architecture Variant (arm926t)  --->
[ ] Enable VFP extension support
    Target ABI (EABI)  --->
    Floating point strategy (Soft float)  --->

System configuration  --->
    (Lichee Pi) System hostname
    (licheepi) Root password
    [*] remount root filesystem read-write during boot 
```

menuconfig より buildroot の構成を編集.

```bash
make menuconfig
```

ビルドの実行

```bash
make
```

ビルド成功後, `buildroot-2017.08/output/images/roots.tar` が作成されます.

# SD カードへのインストール.

下記のビルドしたファイル及び必要なファイルをホスト環境に複製します.

* u-boot/u-boot-sunxi-with-spl.bin
* u-boot/boot.scr
* linux/arch/arm/boot/zImage
* linux/arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dtb
* buildroot-2017.08/output/images/rootfs.tar

## 
## SDカードのパーティション設定とフォーマット
認識順序によって `sdX` の `X` 部分の名称が固定でないため, ここでは `X` としています. USBメモリ等記憶メディアが認識されていない場合, `sdb` が SDカードになると思われます.
パーディション `sdX1` はビルドしたカーネルとデバイスツリーを格納できる容量で設定を行ってください.
残りのパーティションは任意に設定を行うことができます. パーティション `sdX2` に残りすべてを割り当てても問題ありません.

パーティション `sdX1` は vfat, パーティション `sdX2` は ext4 のファイルシステムでフォーマットを行います.

```console
# fdisk /dev/sdX

# mkfs.vfat /dev/sdX1
# mkfs.ext4 /dev/sdX2
```

## u-boot の書き込み

```console
# dd if=u-boot-sunxi-with-spl.bin of=/dev/sdX bs=1024 seek=8
```

## Kernel, rootfs のインストール
### マウントポイントの作製とマウント
***NOTE: ここで現在のディレクトリに対してマウントポイントとなるディレクトリの作製を行っていますが, 特別な意味は存在しません. SDカードのパーティションをマウントできれば良いのです.*** 好みの問題です.

```console
# mkdir -pv mnt/rootfs mnt/boot
# mount /dev/sdX1 mnt/boot
# mount /dev/sdX2 mnt/rootfs
```

### カーネル, ブートファイルのコピー
```console
# cp -v zImage mnt/boot
# cp -v boot.scr mnt/boot
# cp -v u-boot-sunxi-with-spl.bin mnt/boot
```

### rootfs の展開と umount
```console
# tar xvf roots.tar -C mnt/rootfs
# sync
# umount mnt/rootfs
# umount mnt/boot
```

# Wi-Fi への接続
Wi-Fi への接続は, `wpa_supplicant` を使用した方法を記載します.
`wpa_supplicant` や `wpa_passphrase` を有効にするには `buildroot` ビルド時に構成より指定する必要があります.

```
Target packages --->
  Networking applications -->
    [*] wpa_supplicant
    [*]   Enable nl80211 support
    [*]   Install wpa_cli binary
    [*]   Install wpa_client shaed library
    [*]   Install wpa_passphrage
```

## デバイスの確認
`ip` コマンドより認識されているネットワークデバイスの確認を行う. ここで使用想定するデバイス名は `wlan0` とします.

```console
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: usb0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop qlen 1000
    link/ether 82:6a:7e:xx:xx:xx brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop qlen 1000
    link/ether 1c:bf:ce:8a:ee:17 brd ff:ff:ff:ff:ff:ff

# ip link set dev wlan0 up
```

## wpa_supplicant の実行
はじめに `wpa_supplicant` の設定ファイルを作成します.

```console
# mkdir -v /etc/wpa_supplicant
# wpa_passphrage [SSID] [PASSWORD] >> /etc/wpa_supplicant/wlan0.conf
```

`wpa_supplicant` の実行及び動的 IP を割り当てる場合ならば `dhcpcd` も実行.

```console
# wpa_supplicant -Dnl80211 -i wlan0 -c /etc/wpa_supplicant/wlan0.conf -B
Successfully initialized wpa_supplicant
rfkill: Cannot open RFKILL control device
[  132.223615] wlan0: authenticate with a4:71:74:xx:xx:xx
[  132.292065] wlan0: send auth to a4:71:74:xx:xx:xx (try 1/3)
[  132.301098] wlan0: authenticated
[  132.308490] wlan0: associate with a4:71:74:xx:xx:xx (try 1/3)
[  132.321067] wlan0: RX AssocResp from a4:71:74:xx:xx:xx (capab=0x431 status=0 aid=3)
[  132.373016] wlan0: associated
[  132.380301] random: wpa_supplicant: uninitialized urandom read (4096 bytes read)

# dhcpcd
all: IPv6 kernel autoconf disabled
all: IPv6 kernel autoconf disabled
DUID 00:01:00:01:c7:92:bd:27:1c:bf:ce:8a:ee:17
wlan0: IAID ce:8a:ee:17
wlan0: adding address fe80::662:94eb:4379:d8ca
[  139.879676] random: dhcpcd: uninitialized urandom read (120 bytes read)
if_addaddress6: Operation not supported
usb0: waiting for carrier
wlan0: rebinding lease of 192.168.100.154
wlan0: probing address 192.168.100.154/24
wlan0: leased 192.168.100.154 for 604800 seconds
wlan0: adding route to 192.168.100.0/24
wlan0: adding default route via 192.168.100.1
forked to background, child pid 214
```

### 接続状態の確認
`ip` コマンドを使用して `wlan0` に IP アドレスが割り当てられていることを確認します.
ネットワークの導通確認は任意の方法で行ってください. 一例 `ping -c 3 www.example.com` 等

```console
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: usb0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 82:6a:7e:8b:2f:83 brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq qlen 1000
    link/ether 1c:bf:ce:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.154/24 brd 192.168.100.255 scope global wlan0
       valid_lft forever preferred_lft forever
```

# Hello, World!
Arch User Repository に存在する [arm-linux-gnueabi-gcc](https://aur.archlinux.org/packages/arm-linux-gnueabi-gcc/) のクロスコンパイラで生成された実行ファイルは, Lichee Nano 環境においてはエラーが発生する.
また, u-boot 及び Linux カーネルビルド時に使用した Toolchain でも同様な現象が発生する.

```hello.c
#include <stdio.h>

int main(
    int argc,
    char *argv[]) {
  fprintf(stderr, "Hello, World!\n");

  return 0;
}
```

ホスト側でコンパイル.

```console
$ arm-linux-gnueabi-gcc -o hello src/main.c \
  -march=armv5te \
  -mfloat-abi=soft \
  -mabi=aapcs-linux \
  -marm \
  -mtls-dialect=gnu \
  -mtune=arm926ej-s
```

Lichee Nano 側で実行.

```console
$ ./hello
Segmentation fault
```

## 解決策
buildroot ビルド時に生成された `buildroot-2017.08/output/host/bin/arm-buildroot-eabi-gcc` を使用する. これが正攻法なのかわかりませんが, 今の所うまく行っています.

```console
arm-buildroot-linux-gnueabi-gcc \
  -o hello src/main.c \
  -march=armv5te \
  -mfloat-abi=soft \
  -mabi=aapcs-linux \
  -marm \
  -mtls-dialect=gnu \
  -mtune=arm926ej-s
```

```console
$ ./hello 
Hello, World!
$ /lib/ld-linux.so.3 --list `pwd`/hello
        libc.so.6 => /lib/libc.so.6 (0xb6dea000)
        /lib/ld-linux.so.3 (0xb6f2f000)
```

# 認識及びネットワークに接続できた Wi-Fi 子機

| メーカ | 名称 | チップセット |
| :--- | :--- | :--- |
| TP-Link | TL-WN823N v2/v3 | Realtek RTL8192EU |
| BUFFALO INC. | WLI-UC-GNM2 |Ralink RT3070 |
| Ralink Technology, Corp. | MT7601U Wireless Adapter | MediaTek MT7601U |