1 环境准备

1.1 编译环境准备

  • 1)配置编译环境的几大要素:

    • (1)OS 平台选择基于微软的 WSL(Windows Subsystem for Linux)的 Ubuntu 22.04(见其它文章);
    • (2)读卡器+内存卡识别采用 usbipd(见其它文章);
    • (3)交叉编译工具链采用 arm-linux-gnueabihf-gcc;
  • 2)WSL 和 usbipd 见其它文章,这里仅记录交叉编译工具链的配置:

    # 安装其它工具库
    apt-get install build-essential libncurses5-dev u-boot-tools qemu-user-static \
                  debootstrap git binfmt-support libusb-1.0-0-dev pkg-config
    
    # (1)卸载原来的工具链
    apt-get remove gcc-arm-linux-gnueabi*
    
    # (2)下载工具链(可任选一个下载)
    wget https://releases.linaro.org/components/toolchain/binaries/4.9-2017.01/arm-linux-gnueabihf/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
    wget https://releases.linaro.org/components/toolchain/binaries/5.5-2017.10/arm-linux-gnueabihf/gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf.tar.xz
    wget https://releases.linaro.org/components/toolchain/binaries/6.5-2018.12/arm-linux-gnueabihf/gcc-linaro-6.5.0-2018.12-x86_64_arm-linux-gnueabihf.tar.xz
    wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
    # (3)解压
    mkdir /usr/local/arm
    tar -xvf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz -C /usr/local/arm
    tar -xvf gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf.tar.xz -C /usr/local/arm
    tar -xvf gcc-linaro-6.5.0-2018.12-x86_64_arm-linux-gnueabihf.tar.xz -C /usr/local/arm
    tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /usr/local/arm
    # (4)配置环境变量,在 /root/.bashrc 文件中添加:
    #export PATH=$PATH:gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
    #export PATH=$PATH:gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf/bin
    #export PATH=$PATH:gcc-linaro-6.5.0-2018.12-x86_64_arm-linux-gnueabihf/bin
    export PATH=$PATH:gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
    # (5)使工具链的环境变量生效
    cd && source ~/.bashrc
    
    # (6)测试
    arm-linux-gnueabihf-gcc -v
    Using built-in specs.
    COLLECT_GCC=arm-linux-gnueabihf-gcc
    ......
    gcc version 7.5.0 (Linaro GCC 7.5-2019.12)
    
  • 上面分别列出了 gcc 的 4.9、5.5、6.5、7.5 版本,根据自己要编译的源码选择对应的版本,
    比如 linux-sunxi 基于 linux-3.4 所以要选择 gcc 4.9 版本,而这里我们要编译 linux 的
    主线版本,所以选择 gcc 的最新版本 7.5
  • 通过修改 .bashrc 切换 gcc 版本时可能会遇到不生效的情况。原因是 $PATH 的内容会被继
    承,我们可以通过将 /usr/local/arm 下的旧 gcc 换个名字,然后重启下系统解决。

  • 3)配置结果:

    [root@Chris] [2024-09-13 14:44:07] # [~] :
    -> cat /etc/os-release
    PRETTY_NAME="Ubuntu 22.04.4 LTS"
    NAME="Ubuntu"
    VERSION_ID="22.04"
    VERSION="22.04.4 LTS (Jammy Jellyfish)"
    ......
    
    [root@Chris] [2024-09-13 14:44:16] # [~] :
    -> lsusb
    Bus 002 Device 002: ID 067b:2731 Prolific Technology, Inc. USB SD Card Reader
    
    [root@Chris] [2024-09-13 14:44:19] # [~] :
    -> lsblk
    NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
    ......
    sdd      8:48   1  29.1G  0 disk
    ├─sdd1   8:49   1    64M  0 part
    └─sdd2   8:50   1  29.1G  0 part
    
    [root@Chris] [2024-09-13 14:44:20] # [~] :
    -> arm-linux-gnueabihf-gcc -v
    Using built-in specs.
    COLLECT_GCC=arm-linux-gnueabihf-gcc
    ......
    gcc version 7.5.0 (Linaro GCC 7.5-2019.12)

1.2 主线源码准备

源码可以从其官方网站下载压缩包,但是我更推荐通过 git 仓库下载。原因有两个:
一个是方便切换源码版本;另外就是方便随时查看修改内、还原错误修改。

1.2.1 linux 源码

  • 1)linux 源码:3 个仓库任选其一

    # linux 源码仓库在 gitlab、github 上由 Linus Torvalds 维护
    git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    git clone https://github.com/torvalds/linux.git
    
    # 由于 github 连接不稳定等原因,我们可以通过其它方式获取源码 —— 由 Gitee 官方
    # 提供的 Linux 源码的镜像仓库,每天同步一次。
    git clone https://gitee.com/mirrors/Linux.git
    
    
    # 由于这次编译主要是为了支持 Tenda 的 aic8800 无线网卡,其支持的内核版本为 V3.10-V6.2,
    # 目前(2024.9.13)最新的 v6.1 版本为 v6.1-rc8,所以这里选择 v6.1-rc8 版本。
    
    # 查看内核版本分支
    git branch -a       # 查看所有分支
    git fetch --tags    # 获取最新标签
    git tag | grep v6   # 查看并筛选标签名
    # 根据标签创建分支
    git checkout tags/v6.1-rc8
    
    # 已经做了修改,又需要在内核源码分支间切换时,可以使用 git stash,如:
    # git stash [save "message"]    # 将未提交的更改保存到暂存区,并指定名称为 message(可省略)
    # git stash pop                 # 恢复暂存区的更改

1.2.2 U-Boot 源码

  • 1)u-boot 源码:2 个仓库任选其一

    # 同理,u-boot 源码也提供两个仓库地址,任选一个即可
    # github 仓库
    git clone https://github.com/u-boot/u-boot.git
    # gitee 镜像仓库
    git clone https://gitee.com/mirrors/u-boot.git

1.2.3 启动文件 boot.scr

  • 1)新建 boot.cmd 文件,并输入以下内容:

    setenv stdout serial,hdmi
    setenv stderr serial,hdmi
    fatload mmc 0 0x46000000 uImage
    fatload mmc 0 0x49000000 sun7i-a20-cubieboard2.dtb
    setenv bootargs console=ttyS0,115200 rw [earlyprintk] root=/dev/mmcblk0p2 rootwait panic=10 ${extra}
    bootm 0x46000000 - 0x49000000
  • 2)在上述内容的设置 bootargs 时,没有添加 rw,会导致 “Read-only file system” 的错误。
  • 3)编译出 boot.scr:

    mkimage -C none -A arm -T script -d boot.cmd boot.scr

1.2.4 根文件系统 rootfs

这里我们使用 debootstrap 制作从镜像网站下载的根文件系统包。
  • 1)生成 rootfs:

    ## man debootstrap :
     # --no-check-gpg :Disables checking gpg signatures of retrieved Release files.
     #
     # 从 man 的示例 “debootstrap stretch ./stretch-chroot http://deb.debian.org/debian” 可知:
     # 这里允许自定义下载源,官方的地址已经不支持 wheezy 版本,这里通过指定阿里源来指定 debian 版本为 fullseye 或 bookworm
     # 
    debootstrap --foreign --no-check-gpg --arch armhf bookworm . http://mirrors.aliyun.com/debian/
    debootstrap --foreign --no-check-gpg --arch armhf bullseye . http://mirrors.aliyun.com/debian/
    
    # 如果要制作 ubuntu 根文件系统,则需要指定 ubuntu 的源
    debootstrap --foreign --no-check-gpg --arch armhf noble . https://mirrors.aliyun.com/ubuntu-ports/
    debootstrap --foreign --no-check-gpg --arch armhf jammy . https://mirrors.aliyun.com/ubuntu-ports/
    
    cp /usr/bin/qemu-arm-static usr/bin/
    LC_ALL=C LANGUAGE=C LANG=C chroot . /debootstrap/debootstrap --second-stage
至此,所有的环境我们准备就绪,使用 tree 命令查看一下:

2 内核编译

2.1 添加 tenda aic8800 驱动(可选)

linux-sunxi 基于 linux-3.4 内核修改,而 Tenda U2 V5.0(基于 aic8800 芯片)支持的 linux 内核版本为 V3.10-V6.2。
尝试在升级内核后的 Cubieboard2 上安装 .deb 驱动包,但报错 linux-headers 问题以及 Invalid module format 问题,因此选择直接编译到内核中。
# 复制内核源码(来自其驱动的 deb 包解压)
cp ~/aic8800/drivers/aic8800/ -r drivers/net/wireless/

# 打开 drivers/net/wireless/Makefile 文件,添加:
obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800/

# 打开 drivers/net/wireless/Kconfig 文件,添加:
source "drivers/net/wireless/aic8800/Kconfig"
  • 查看修改结果:

2.2 编译 linux 内核

  • 1)以 sunxi_defconfig 为基础,生成 .config 配置文件:

    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sunxi_defconfig
    
    ......
    #
    # configuration written to .config
    #
    在 arch/arm/configs/ 目录下有许多开发板的预定义配置文件,这里我们选择 Allwinner Cubieboard2 的 sunxi_defconfig
  • 2)编译菜单项配置(这是一个需要不断调试的枯燥工作):

    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
    输入上述命令后,会弹出编译菜单项的窗口,根据自己的需求修改。我这里需要无线网卡 AIC8800、USB 大容量设备(用来接 USB 转 SSD)等。
    *,表示该驱动将作为内核的一部分被编译进内核映像中,键入 “Y” 生效;M,则表示该驱动将作为一个独立的模块编译,并且可以在需要时通过 insmod 或 modprobe 加载,键入 “M” 生效;键入 “N” 表示不选中。

image.png

  • 3)开始编译(i7-9750 12 核心花费 5 分钟):

    make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage dtbs
    ......
    OBJCOPY arch/arm/boot/zImage
    Kernel: arch/arm/boot/zImage is ready
    
    # 把 zImage 转换为 uImage 
    cd ~/mainline/linux
    mkimage -A arm -O linux -T kernel -C none -a 0x46000000 -e 0x46000000 -n "Linux kernel uImage" -d arch/arm/boot/zImage ../uImage
  • 4)复制 dtc(编译与反编译设备树文件)到 /usr/bin 目录备用:

    cp ./scripts/dtc/dtc /usr/bin

3 U-Boot 编译

  • 1)查看 Cubieboard2 配置文件:

  • 2)编译生成 u-boot-sunxi-with-spl.bin 文件:

    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- Cubieboard2_defconfig
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
    
    ......
    OBJCOPY spl/u-boot-spl-nodtb.bin
    COPY    spl/u-boot-spl.bin
    SYM     spl/u-boot-spl.sym
    MKIMAGE spl/sunxi-spl.bin
    MKIMAGE u-boot.img
    COPY    u-boot.dtb
    MKIMAGE u-boot-dtb.img
    BINMAN  .binman_stamp
    OFCHK   .config

4 分区与烧录

4.1 sdcard 分区

  • 1)读卡器插入主机后,查看:

    [root@Chris] [2024-09-13 18:00:33] # [~] :
    -> lsblk
    NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
    .....
    sdd      8:48   1  29.1G  0 disk
    └─sdd1   8:49   1  29.1G  0 part
  • 2)创建 DOS 分区表:

  • 3)创建 2 个分区:

  • 4)格式化:

    mkfs.vfat ${card}1
    mkfs.ext4 ${card}2

4.2 烧录 SPL

  • 1)SPL(Second Program Loader,可以认为是 uboot 的 bootloader,用来加载完整 uboot 到 SDRAM 中)烧录:

    dd if=u-boot-sunxi-with-spl.bin of=$card bs=1024 seek=8
    sync

4.3 烧录第 1 分区

  • 2)烧录镜像、dtb、启动文件 boot.scr 到 sdcard 的第一分区中:

    cd ~/mainline/
    mkdir /mnt/h
    
    # 挂载分区 1
    mount ${card}1                /mnt/h
    cp linux/arch/arm/boot/dts/sun7i-a20-cubieboard2.dtb /mnt/h
    cp linux/arch/arm/boot/zImage /mnt/h
    cp boot.scr                   /mnt/h
    
    # 卸载分区 1
    sync && sudo umount /mnt/h

    image.png

4.3 烧录第 2 分区

第二分区主要烧录根文件系统 rootfs。
  • 1)配置根文件系统 rootfs:

    cd ~/rootfs/chroot-armhf-bullseye/
    chroot . passwd
    
    echo "Cubieboard2"            > etc/hostname && cat etc/hostname
    echo "127.0.0.1 Cubieboard2" >> etc/hosts && cat etc/hosts
    
    # 安装内核驱动模块
    cd ~/mainline/linux
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=~/rootfs/chroot-armhf-bullseye/ modules modules_install
    
    cd ~/rootfs/chroot-armhf-bullseye/
    
    # 初始化 inittab 文件
    echo T0:2345:respawn:/sbin/getty -L ttyS0 115200 vt100 >> etc/inittab && cat etc/inittab
    
    # 挂载 SSD
    chroot . mkdir /data
    echo UUID=8612ea26-f6a4-4b2e-b9bc-775a662b0dea /data ext2 defaults,noatime,nofail 0 0 >> etc/fstab
    
    # 初始化 sources.list 文件
    # vi etc/apt/sources.list
    
    chroot . apt-get update
    chroot . apt-get upgrade
    chroot . apt-get install openssh-server locales wireless-tools wpasupplicant vim lrzsz net-tools ntpdate
    echo "*/60 * * * * /usr/sbin/ntpdate ntp.sjtu.edu.cn" >> /var/spool/cron/crontabs/root
    
    echo "export LC_ALL=\"zh_CN.UTF-8\""
    echo "en_US.UTF-8 UTF-8" > etc/locale.gen
    echo "zh_CN.UTF-8 UTF-8" >> etc/locale.gen
    chroot . locale-gen
    
    
    
    # 备份 rootfs。
    cd ../ && tar -czvf chroot-armhf-bullseye-`date +%Y%m%d_%H%M%S`.tar.gz chroot-armhf-bullseye/
  • 2)烧录到第 2 分区:

    # 挂载分区 2
    mount ${card}2 /mnt/h
    # 烧录 rootfs
    mv ~/rootfs/chroot-armhf-bullseye/* /mnt/h/
    # 卸载分区 2
    sync && umount /mnt/h

附录:基于 AIC8800 的 Tenda U2 V5 USB WIFI

  • 参考:

  • 1)添加驱动程序:以基于 aic8800 的 USB WIFI 为例

    • (1)将内核文件复制到 drivers/net/wireless 目录下
    • (2)修改 drivers/net/wireless/Kconfig 文件,添加下面一行内容:

      source "drivers/net/wireless/aic8800/Kconfig"
    • (3)修改 drivers/net/wireless/Makefile 文件,添加下面一行内容:

      obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800/
    • (4)通过 make menuconfig 修改配置文件时,进行如下选择:

      Device Drivers  --->
      [*] Network device support  --->
          [*]   Wireless LAN  --->
              [*]   AIC wireless Support
              <M>     AIC8800 wlan Support
              <M>     AIC8800 bluetooth Support (UART)
  • 2)Linux 内核编译参考:

    # 安装工具链
    apt-get install build-essential bc bison flex libncurses5-dev libssl-dev \
        gcc-arm-linux-gnueabihf arm-linux-gnueabihf-g++
    
    # 下载内核
    git clone -b v5.4 https://github.com/torvalds/linux.git
    cd linux
    
    # 生成默认的内核配置文件
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig  
    # 启动一个基于文本的交互式菜单,允许用户手动配置内核
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig 
    
    # 主要用于在现有配置基础上进行增量更新,以适应新的内核版本或更新的默认配置
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- olddefconfig
    # 编译内核镜像
    make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage modules
    # 仅编译内核驱动
    make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=drivers/net/wireless/aic8800 modules
    
    # 清理
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
    
  • 3)Tenda U2 V5 最小内核版本支持 3.10,在 3.18.140 版本编译成功
  • 4)问题处理(编译 linux-3.11 版本时出现):ERROR: Kernel configuration is invalid.

    现象:
    ERROR: Kernel configuration is invalid.
         include/generated/autoconf.h or include/config/auto.conf are missing.
         Run 'make oldconfig && make prepare' on kernel src to fix it.
    
    处理:
    # 清理编译失败的文件
    make mrproper 
    
    # 生成默认的内核配置文件
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig  
    # 启动一个基于文本的交互式菜单,允许用户手动配置内核
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig 
    # 主要用于在现有配置基础上进行增量更新,以适应新的内核版本或更新的默认配置
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- olddefconfig
    
    # 准备内核源码树,生成一些必要的头文件和其他辅助文件。
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- prepare
    # 生成内核编译过程中需要用到的脚本文件
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- scripts 
    ...
  • 5)问题处理:

    WARNING: Symbol version dump /root/linux-3.10.1/Module.symvers
           is missing; modules will have no dependencies and modversions.
    
    先编译内核镜像,再编译内核驱动即可
    make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage modules
    make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=drivers/net/wireless/aic8800 modules
  • 6)问题处理:

    # make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sunxi_defconfig
    
    drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig:4: syntax error
    drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig:3: unknown statement "---help---"
    drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig:4:warning: ignoring unsupported character '.'
    drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig:4: unknown statement "This"
    make[1]: *** [scripts/kconfig/Makefile:94: sunxi_defconfig] Error 1
    make: *** [Makefile:697: sunxi_defconfig] Error 2
    
    通过 make mrproper 命令清理旧的编译缓存文件。
    

送南阳马生序
7 声望3 粉丝

余之业有不精、德有不成,非天质之卑,则心不若他之专耳,岂他人之过哉!