SoloLinker-A编译SDK运行Ubuntu系统

前言说明

  1. 我的硬件设备是:SoloLinker-A V1版本 RV1106G3 256M内存 8G储存EMMC

  2. 编译系统用的是:MicroSoft WSL2 Ubuntu 22.04

编译镜像

  1. 准备源码
1
2
3
# SDk文件 sololinker-sdk-20231219.7z 来自于官方群文件,切记不要在Windows下直接解压,会导致丢失权限编译报错
sudo apt install p7zip-full gcc-multilib g++-multilib gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
7z x /mnt/d/Rockchip/RV1106/sololinker-sdk-20231219.7z
  1. 开始编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cd sololinker/
# ./develop_init_for_ubuntu20.04.sh # Ubuntu20.04 运行该命令安装编译所需软件
./develop_init_for_ubuntu22.04.sh # Ubuntu22.04 运行该命令安装编译所需软件

./build.sh lunch # 选择编译的方案,我这选的是 0. BoardConfig_IPC/BoardConfig-EMMC-NONE-Hinlink-sololinker-A-Ubuntu.mk
./build.sh clean # 如出错误先清理
./build.sh # 开始编译


# sololinker/project/cfg/BoardConfig_IPC/BoardConfig*.mk
# 该项用于设置保留的CMA内存,对于AI的图像处理与视频处理此选项很重要
# 但对于不使用视频/图像处理的应用,不需要保留那么多内存
# 本来RV1106内存就不大,可以尽可能多得给Linux用户程序
export RK_BOOTARGS_CMA_SIZE="32M"

# Ubuntu默认的系统分区布局如下
export RK_PARTITION_CMD_IN_ENV="32K(env),512K@32K(idblock),256K(uboot),32M(boot),7G(rootfs)"

# 默认配置的根文件系统为ext4
export RK_PARTITION_FS_TYPE_CFG=rootfs@IGNORE@ext4


# 编译好的固件在 /home/leux/sololinker/IMAGE/EMMC_RV1106G-HINLINK-SOLOLINKER-A.DTS_UBUNTU_20240329.2332_RELEASE_TEST
# 通过SocToolKit工具刷入上面路径下IMAGES文件夹中的镜像即可
# 要刷机时切记不要短接针脚4和6,因为短接虽然可启用WiFi但会导致无法进入Maskrom模式

修改内核

  1. 以添加桥接网卡支持为例,有两种方式可实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 拷贝默认内核配置并进入内核配置界面
cd sysdrv/source/kernel/
cp ./arch/arm/configs/rv1106_linux_sololinker_defconfig .config
make ARCH=arm menuconfig

# 首次进入菜单后先保存,然后选中你需要添加的选项保存退出。
# 比较.config和.config.old,多出来的选项即为你需要的

# Networking support -> Networing options -> 802.1d Ethernet Bridging
# 如打开桥接支持就多出来如下配置选项:
CONFIG_STP=y
CONFIG_BRIDGE=y
CONFIG_BRIDGE_IGMP_SNOOPING=y
CONFIG_LLC=y


# 第一种:直接修改默认的内核配置文件。即将上面多出来的配置选项直接添加到默认的内核配置文件
sololinker/sysdrv/source/kernel/arch/arm/configs/rv1106_linux_sololinker_defconfig


# 第二种:注释掉 sololinker/project/build.sh 第549行 RK_KERNEL_DEFCONFIG 来使用生成的.config
make kernel -C ${SDK_SYSDRV_DIR} \
# KERNEL_CFG=${RK_KERNEL_DEFCONFIG} \
KERNEL_DTS=${RK_KERNEL_DTS} \
KERNEL_CFG_FRAGMENT=${RK_KERNEL_DEFCONFIG_FRAGMENT} -j $(nproc)
  1. 以传统方式单独编译内核
1
2
3
4
5
6
cd /home/leux/sololinker/sysdrv/source/kernel/
export PATH=$PATH:/home/leux/sololinker/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin

make ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf- rv1106_linux_sololinker_defconfig
make ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf- BOOT_ITS=boot.its rv1106g-hinlink-sololinker-a.img -j$(nproc)

修复无线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 当前SDK编译出来的wifi驱动会加载错误的WiFi固件,可以看到需要的是aic8800D80而不是aic8800DC
[ 199.824241] aic_load_firmware :firmware path = /oem/usr/ko/aic8800DC/fw_adid_8800d80.bin
[ 199.824313] aic_load_firmware: fw_adid_8800d80.bin file failed to open

# https://github.com/armbian/firmware/tree/master/aic8800/USB/aic8800D80
# 可以根据如下修复,先下载上面网址路径中的固件文件保存到文件夹/home/leux/aic8800D80/

# 拷贝下载的固件文件夹到SDK中
cp -r /home/leux/aic8800D80 /home/leux/sololinker/sysdrv/drv_ko/wifi/aic8800_usb/

# 在下面路径Makefile文件中第61行后面添加如下一句将固件目录aic8800D80最终复制到/lib/firmware/下
vi sololinker/sysdrv/drv_ko/wifi/aic8800_usb/Makefile
cp $(shell pwd)/aic8800DC $(M_OUT_DIR) -r
+ cp $(shell pwd)/aic8800D80 $(SYSDRV_DIR_OUT_ROOTFS)/lib/firmware/ -r


# Ubuntu连接WIFI示例,
nmcli device wifi connect "SSID" password "1234567890" # 连接WIFI
nmcli con del wifiname # 删除WIFI


# buildroot系统的话先短接4,6号针脚,然后根据如下使USB网卡AIC8800硬件启用
echo 4 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio4/direction
echo 1 > /sys/class/gpio/gpio4/value
echo host > /sys/devices/platform/ff3e0000.usb2-phy/otg_mode

# 加载AIC8800无线网卡驱动,经过上面修改后直接使用默认的固件地址即可
insmod /lib/modules/5.10.160/aic_load_fw.ko
insmod /lib/modules/5.10.160/aic8800_fdrv.ko
insmod /lib/modules/5.10.160/aic_btusb.ko

# 启用无线网卡
ifconfig wlan0 up

# 将名为:SSID 密码:1234567890 的无线接入点写入无线配置文件中
wpa_passphrase "SSID" "1234567890" > /etc/wpa_supplicant/wpa_supplicant.conf

# 读取无线配置文件并后台运行
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf

# 连接成功后可能还没有分配IP,使用如下命令从上级获取IP地址
udhcpc -i wlan0

其他说明

  1. 使用 WSL1 会导致构失败,请使用 WSL2 或正常的Linux
1
2
3
4
5
6
7
8
9
****** Fix rootfs file owner ******
mount: /tmp/tmp.xZOMfxycVo: mount failed: Operation not permitted.
/home/leux/sololinker/output/out/sysdrv_out/pc/mkfs_ext4.sh: line 7: msg_error: command not found
/home/leux/sololinker/output/out/sysdrv_out/pc/mkfs_ext4.sh: line 8: msg_error: command not found
/home/leux/sololinker/output/out/sysdrv_out/pc/mkfs_ext4.sh: line 9: msg_info: command not found
[build.sh:error] Running build_mkimg failed!
[build.sh:error] exit code 1 from line 1756:
[build.sh:info] $RK_PROJECT_TOOLS_MKFS_EXT4 $src $dst $part_size
leux@B650:~/sololinker$
  1. 编译 lvgl_app 时会遇到交叉编译器错误的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
-- Configuring incomplete, errors occurred!
See also "/home/leux/sololinker/project/app/lvgl_app/output/CMakeFiles/CMakeOutput.log".
See also "/home/leux/sololinker/project/app/lvgl_app/output/CMakeFiles/CMakeError.log".
make[1]: *** [Makefile:35: all] Error 1
make[1]: Leaving directory '/home/leux/sololinker/project/app/lvgl_app'
make: *** [Makefile:23: all] Error 255
make: Leaving directory '/home/leux/sololinker/project/app'
[build.sh:error] Running build_app failed!
[build.sh:error] exit code 2 from line 453:
[build.sh:info] make -C ${SDK_APP_DIR}

--------------------------------------------------------------------------------------
# 关于上面出现的lvgl_app编译出错问题,交叉编译器的定义在这两个文件中
sololinker/project/app/lvgl_app/cmake/toolchainfile-rv1106.cmake
sololinker/project/app/lvgl_app/cmake/toolchainfile-rv1106-glibc.cmake

# sololinker/project/app/lvgl_app/Makefile:第20到24行可以看到
ifeq ($(RK_ROOTFS_TYPE), ubuntu)
TOOLCHAIN_FILE := ../cmake/toolchainfile-rv1106-glibc.cmake
else
TOOLCHAIN_FILE := ../cmake/toolchainfile-rv1106.cmake
endif

# 当RK_ROOTFS_TYPE为ubuntu时使用如下编译器
SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

# 安装上面缺少的编译器:sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
--------------------------------------------------------------------------------------


--------------------------------------------------------------------------------------
# sololinker/project/app/Makefile 中可知,当 RK_ROOTFS_TYPE 为 ubuntu 时编译 lvgl_demo
# 那我们如果不需要注释掉即可:vi sololinker/project/app/Makefile 可注释掉 lvgl_app 的编译
all: $(pkg-build)
ifeq ($(RK_ROOTFS_TYPE), ubuntu)
@echo "RootFS type is Ubuntu. building lvgl_demo only."
#make -C lvgl_app -j$(nproc) ||exit -1
else
@echo "RootFS type is not Ubuntu. Proceeding with the build."
$(foreach target,$(app_src),make -C $(target)||exit -1;)
endif
$(call MAROC_COPY_PKG_TO_APP_OUTPUT, $(RK_PROJECT_PATH_APP), $(RK_APP_OUTPUT))
--------------------------------------------------------------------------------------
  1. 从SDK传来的脚本我不需要该如何去除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 因为没有接网线老是跳 `udhcpc: sending discover` 都是因为S21appinit惹的祸
/etc/init.d/S21appinit # 它启动了脚本RkLunch.sh
/oem/usr/bin/RkLunch.sh # 第47行ifconfig eth0 up && udhcpc -i eth0
/oem/usr/bin/RkLunch-stop.sh # 结束脚本,也可直接:killall udhcpc

# 如下SDK传来的脚本我用不上,可通过后面的方式去除
/etc/init.d/S21appinit # 默认的buildroot采用了该方式联网,但现在我们不需要了
/etc/init.d/S20linkmount # 挂载储存资源

# 上述脚本在 sololinker/project/build.sh 中被定义赋值
export RK_PROJECT_FILE_ROOTFS_SCRIPT=$SDK_ROOT_DIR/output/out/S20linkmount
export RK_PROJECT_FILE_OEM_SCRIPT=$SDK_ROOT_DIR/output/out/S21appinit
# 如不需要可以注释掉 sololinker/project/build.sh 第1091 1123如下两行试试
1091 cp -f $RK_PROJECT_FILE_OEM_SCRIPT $RK_PROJECT_PACKAGE_ROOTFS_DIR/etc/init.d
1123 cp -f $RK_PROJECT_FILE_ROOTFS_SCRIPT $RK_PROJECT_PACKAGE_ROOTFS_DIR/etc/init.d
  1. 去除我用不上的 /oem 目录
1
2
3
4
5
6
7
8
9
# 将 sololinker/project/build.sh 文件第1900行后面两行注释可解决根文件系统里面存在/oem目录
if [ "$RK_BUILD_APP_TO_OEM_PARTITION" = "y" ];then
rm -rf $RK_PROJECT_PACKAGE_ROOTFS_DIR/oem/*
build_mkimg $GLOBAL_OEM_NAME $RK_PROJECT_PACKAGE_OEM_DIR
else
#mkdir -p $RK_PROJECT_PACKAGE_ROOTFS_DIR/oem
#__COPY_FILES $RK_PROJECT_PACKAGE_OEM_DIR $RK_PROJECT_PACKAGE_ROOTFS_DIR/oem
rm -rf $RK_PROJECT_PACKAGE_OEM_DIR
fi

RNDIS支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 开机自动启用ABD和RNDIS同时支持,先添加内核驱动
# > Device Drivers > USB support > USB Gadget Support > RNDIS
CONFIG_USB_U_ETHER=y
CONFIG_USB_F_RNDIS=y
CONFIG_USB_CONFIGFS_RNDIS=y

# 下载幸狐的脚本放到SDK对应路径,切记该脚本要有执行权限
wget https://github.com/LuckfoxTECH/luckfox-pico/raw/main/sysdrv/tools/board/android-tools/S50usbdevice \
-O sololinker/sysdrv/tools/board/android-tools/S50usbdevice

# 选择Ubuntu默认是不编译ADB的,添加配置到相应文件让其编译ADB服务:export RK_ENABLE_ADBD=y
# sololinker/project/cfg/BoardConfig_IPC/BoardConfig-EMMC-NONE-Hinlink-sololinker-A-Ubuntu.mk

# 如果没有也可直接拷贝上述S50usbdevice到根系统:/etc/init.d/S50usbdevice 但是Ubuntu下开机并不会执行这个
# 然后直接去 sololinker/sysdrv/tools/board/rootfs_ubuntu.tar.gz 中提取 /usr/bin/adbd 放到Alpine根文件系统对应位置

虚拟终端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 这个可以把TTY的内容从FrameBuffer输出,方便没有串口时查看一些信息

# 先添加内核驱动来支持虚拟终端
CONFIG_VT_HW_CONSOLE_BINDING=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y
CONFIG_FONT_SUPPORT=y
# CONFIG_FONTS is not set
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y

# 然后开机后启动它来显示,/dev/tty0 和 /dev/tty1 可用,添加这句到 /etc/inittab
# respawn:表示进程在无论任何时候终止,都会自动重启,即此进程永远挂着
tty0::respawn:/sbin/getty -L tty0 115200 vt100

# 如果需要启动后自动登录则如下
tty0::respawn:/sbin/agetty --autologin root tty0 vt100

# /dev/tty[0-N]只是一个【虚拟控制台】,/dev/tty0是默认的虚拟控制台


# 那该如何让它上电就把内核启动都显示在屏幕上呢?像电脑上那样操作
# /dev/console设备表示的是系统控制台,主要用于接收系统message的,系统消息一般不会被发送到tty上,而是发送给console设备上。
# 当然我们可以配置console为一个tty,这样系统消息就会被发送到一个tty终端上,通过cmdline指定console=tty0,此时/dev/console相当于是/dev/tty0的一个别名。
# /dev/console被称为【系统控制台】,引导和内核消息在引导过程中显示在这个控制台上。

# 修改DTS中的 console=ttyFIQ0 为 console=tty0 即可。注意:修改后将无法通过串口ttyFIQ0登录!!!
sololinker/sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-hinlink-sololinker-a.dts

chosen {
bootargs = "earlycon=uart8250,mmio32,0xff4c0000 console=ttyFIQ0 root=/dev/mmcblk0p5 rootfstype=ext4 rootwait snd_soc_core.prealloc_buffer_size_kbytes=16 coherent_pool=0 net.ifnames=0 biosdevname=0";
};

V2版本WiFi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# 首先我像V1版硬件那样设置引脚6为 1 但不生效,看情况是uart1占用了
Alpine:~# echo 1 > /sys/class/gpio/gpio6/value
Alpine:~# cat /sys/class/gpio/gpio6/value
0
Alpine:~# cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (gpio0-0): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 1 (gpio0-1): vcc3v3-sd gpio0:1 function sdmmc group sdmmc-pwren
pin 2 (gpio0-2): (MUX UNCLAIMED) gpio0:2
pin 3 (gpio0-3): (MUX UNCLAIMED) gpio0:3
pin 4 (gpio0-4): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 5 (gpio0-5): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 6 (gpio0-6): ff4b0000.serial gpio0:6 function uart1 group uart1m0-ctsn
pin 7 (gpio0-7): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 8 (gpio0-8): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 9 (gpio0-9): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 10 (gpio0-10): (MUX UNCLAIMED) (GPIO UNCLAIMED)


# 如上显示可用看到GPIO0_6默认是串口模式,修改dts文件把uart1这一段全删除了
&uart1 {
status = "okay";
pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>;
};


# 改设备树删除uart1来释放它后,gpio6正常工作了
Alpine:~# cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (gpio0-0): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 1 (gpio0-1): vcc3v3-sd gpio0:1 function sdmmc group sdmmc-pwren
pin 2 (gpio0-2): (MUX UNCLAIMED) gpio0:2
pin 3 (gpio0-3): (MUX UNCLAIMED) gpio0:3
pin 4 (gpio0-4): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 5 (gpio0-5): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 6 (gpio0-6): (MUX UNCLAIMED) gpio0:6
pin 7 (gpio0-7): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 8 (gpio0-8): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 9 (gpio0-9): (MUX UNCLAIMED) (GPIO UNCLAIMED)


# V1版本启用WiFi硬件(还需短接4,6针脚)
echo 4 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio4/direction
echo 1 > /sys/class/gpio/gpio4/value
echo host > /sys/devices/platform/ff3e0000.usb2-phy/otg_mode

# V2版本启用WiFi硬件(无需短接针脚了)
echo 6 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio6/direction
echo 1 > /sys/class/gpio/gpio6/value
echo host > /sys/devices/platform/ff3e0000.usb2-phy/otg_mode

# 加载无线网卡AIC8800的驱动
insmod /lib/modules/5.10.160/aic_load_fw.ko
insmod /lib/modules/5.10.160/aic8800_fdrv.ko
insmod /lib/modules/5.10.160/aic_btusb.ko

# 连接到配置文件中的无线
ifconfig wlan0 up
wpa_passphrase "SSID" "1234567890" > /etc/wpa_supplicant/wpa_supplicant.conf
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
udhcpc -i wlan0

---------------------------------------------------------------------
# Ubuntu系统WiFi的两个控制脚本为如下(V2版本除了去掉uart1还需要改4为6):
# 开机自启脚本:/etc/systemd/system/wifi-power-control.service

[Unit]
Description=WiFi Power Control Service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/wifi-power-control.sh start
ExecStop=/usr/sbin/wifi-power-control.sh stop

[Install]
WantedBy=multi-user.target


# 启用WiFi脚本:/usr/sbin/wifi-power-control.sh
#!/bin/bash

GPIO_PIN=4
GPIO_PATH="/sys/class/gpio/gpio$GPIO_PIN"

case $1 in
start)
echo $GPIO_PIN > /sys/class/gpio/export
echo out > $GPIO_PATH/direction
echo 1 > $GPIO_PATH/value
echo host > /sys/devices/platform/ff3e0000.usb2-phy/otg_mode
;;
stop)
echo 0 > $GPIO_PATH/value
echo $GPIO_PIN > /sys/class/gpio/unexport
echo otg > /sys/devices/platform/ff3e0000.usb2-phy/otg_mode
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac