Rockchip RK3399 - 移植uboot 2017.09 & linux 4.19(友善之家脚本方式)
----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4
开发板 和 SOM-RK3399
核心板+定制底板
eMMC
:16GB
LPDDR3
:4GB
显示屏 :15.6
英寸HDMI
接口显示屏
u-boot
:2017.09
linux
:4.19
----------------------------------------------------------------------------------------------------------------------------
本节将会介绍官方固件方式uboot 2017.09
以及linux 4.19
内核的编译过程,教程来自友善之家官方手册。
一、下载工具和固件
1.1 下载工具
root@zhengyang:/work/sambashare/rk3399/friendly# git clone https://github.com/friendlyarm/sd-fuse_rk3399.git -b kernel-4.19
root@zhengyang:/work/sambashare/rk3399/friendly# git clone https://521github.com/friendlyarm/sd-fuse_rk3399.git -b kernel-4.19
root@zhengyang:/work/sambashare/rk3399/friendly# cd sd-fuse_rk3399/
如果第一个下载比较慢,可以尝试使用第二个命令,切换镜像源。
1.2 下载系统镜像
系统镜像,这里我们以debian-bullseye-desktop-arm64
为例,下载地址:https://download.friendlyelec.com/NanoPC-T4
。
将debian-bullseye-desktop-arm64-images.tgz
(位于"\03_分区镜像文件"目录下,以实际下载的文件为准)拷贝到/work/sambashare/rk3399/friendly/sd-fuse_rk3399
目录下;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ll debian*
-rwxrw-rw- 1 root root 1561144972 Sep 23 18:54 debian-bullseye-desktop-arm64-images.tgz*
-rwxrw-rw- 1 root root 75 Sep 23 18:49 debian-bullseye-desktop-arm64-images.tgz.hash.md5*
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# tar -xvzf debian-bullseye-desktop-arm64-images.tgz
解压得到debian-bullseye-desktop-arm64
文件夹;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ll debian-bullseye-desktop-arm64
-rw-r--r-- 1 root root 8072140 Mar 14 2023 boot.img
-rw-r--r-- 1 root root 912 Apr 14 15:54 dtbo.img
-rw-r--r-- 1 root root 203036 Oct 13 2020 idbloader.img
-rw-r--r-- 1 root root 64 Sep 15 00:14 info.conf
-rw-r--r-- 1 root root 28983316 Sep 5 16:32 kernel.img
-rw-r--r-- 1 root root 391502 Oct 13 2020 MiniLoaderAll.bin
-rw-r--r-- 1 root root 49152 Oct 13 2020 misc.img
-rw-r--r-- 1 root root 461 Sep 15 00:14 parameter.txt
-rw-r--r-- 1 root root 4250112 Sep 5 16:32 resource.img
-rw-r--r-- 1 root root 4006843284 Sep 15 00:14 rootfs.img
-rw-r--r-- 1 root root 4194304 Oct 13 2020 trust.img
-rw-r--r-- 1 root root 4194304 Aug 18 2022 uboot.img
-rw-r--r-- 1 root root 159868 Sep 15 00:14 userdata.img
1.3 安装交叉编译工具
参考《Rockchip RK3399
- 引导流程和准备工作》小节《四、安装交叉编译工具链》。
二、编译内核
2.1 下载内核源码
下载内源代码:
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# git clone https://github.com/friendlyarm/kernel-rockchip --depth 1 -b nanopi4-v4.19.y kernel-rk3399
保存到当前路径kernel-rk3399
文件夹中。
2.2 修改build-kernel.sh
脚本
在编译内核之前我们需要修改build-kernel.sh
脚本;
根据自己安装的交叉编译环境,这里我需要替换如下代码为:
CROSS_COMPILE=aarch64-linux-gnu- 修改为 CROSS_COMPILE=arm-linux-
并且将如下代码移除:
export PATH=/opt/FriendlyARM/toolchain/11.3-aarch64/bin/:$PATH
if [ ! -d /opt/FriendlyARM/toolchain/11.3-aarch64 ]; then
echo "please install aarch64-gcc-11.3 first, using these commands: "
echo " git clone https://github.com/friendlyarm/prebuilts.git -b master --depth 1"
echo " cd prebuilts/gcc-x64"
echo " sudo tar xvf toolchain-11.3-aarch64.tar.xz -C /"
exit 1
fi
我安装的交叉编译环境位于/usr/local/arm/12.2.1
,并且我已经将其配置为全局环境变量了。
此外我们还需要修改./tools/update_kernel_bin_to_img.sh
,配置;
CROSS_COMPILE=arm-linux-
2.3 编译内核
执行编译内核命令,编译完成后会自动更新debian-bullseye-desktop-arm64
目录下的相关映象文件,包括文件系统中的内核模块 (rootfs.img
会被解包并重新打包,即更新/lib/modules
下的驱动模块);
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# KERNEL_SRC=$PWD/kernel-rk3399 ./build-kernel.sh debian-bullseye-desktop-arm64
using official logo.
using official kernel logo.
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
YACC scripts/kconfig/zconf.tab.c
LEX scripts/kconfig/zconf.lex.c
HOSTCC scripts/kconfig/zconf.tab.o
HOSTLD scripts/kconfig/conf
#
# configuration written to .config
#
WRAP arch/arm64/include/generated/uapi/asm/errno.h
WRAP arch/arm64/include/generated/uapi/asm/ioctl.h
......
其中:
KERNEL_SRC
配置为内核源码所在路径;$1
配置为目标OS
系统debian-bullseye-desktop-arm64
;
编译完成后会在./out
路径下生成若干文件:
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ll out/
drwxr-xr-x 8 root root 4096 Sep 23 20:54 cryptodev-linux/
-rw-r--r-- 1 root root 20 Sep 23 20:59 debian-bullseye-desktop-arm64_rootfs-img.info
-rw-r--r-- 1 root root 167 Sep 23 18:21 .gitignore
drwxr-xr-x 3 root root 4096 Sep 23 20:36 output_rk3399_kmodules/
drwxr-xr-x 23 root root 4096 Sep 23 20:58 rootfs_new/
drwxr-xr-x 9 root root 4096 Sep 23 20:55 rtl8812au/
drwxr-xr-x 10 root root 4096 Sep 23 20:54 rtl8821CU/
drwxr-xr-x 9 root root 4096 Sep 23 20:55 rtl8822bu/
其中:
cryptodev-linux
、rtl8812au
、rtl8821CU
、rtl8822bu
:cryptodev
以及usb wifi
驱动源码;output_rk3399_kmodules
:为内核驱动模块;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ls out/output_rk3399_kmodules/lib/modules/4.19.193/
cryptodev.ko modules.alias.bin modules.builtin.bin modules.devname modules.symbols rtl8821CU.ko
kernel modules.builtin modules.dep modules.order modules.symbols.bin rtl8822bu.ko
modules.alias modules.builtin.alias.bin modules.dep.bin modules.softdep rtl8812au.ko
rootfs_new
:新的根文件系统的源码;
此外debian-bullseye-desktop-arm64
目录下的内核镜像和根文件系统被更新了;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ll debian-bullseye-desktop-arm64
-rw-r--r-- 1 root root 8072140 Mar 14 2023 boot.img
-rw-r--r-- 1 root root 912 Apr 14 15:54 dtbo.img
-rw-r--r-- 1 root root 203036 Oct 13 2020 idbloader.img
-rw-r--r-- 1 root root 64 Sep 15 00:14 info.conf
-rw-r--r-- 1 root root 31207444 Sep 23 20:55 kernel.img # 更新了
-rw-r--r-- 1 root root 391502 Oct 13 2020 MiniLoaderAll.bin
-rw-r--r-- 1 root root 49152 Oct 13 2020 misc.img
-rw-r--r-- 1 root root 461 Sep 23 20:59 parameter.txt # 更新了
-rw-r--r-- 1 root root 4024320 Sep 23 20:55 resource.img # 更新了
-rw-r--r-- 1 root root 4001059708 Sep 23 20:59 rootfs.img # 更新了
-rw-r--r-- 1 root root 4194304 Oct 13 2020 trust.img
-rw-r--r-- 1 root root 4194304 Aug 18 2022 uboot.img
-rw-r--r-- 1 root root 159868 Sep 15 00:14 userdata.img
2.4 build-kernel.sh
分析
如果感兴趣可以分析一下./build-kernel.sh
的主要工作流程。
2.4.1 配置内核
首先配置内核:
make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} ${KCFG}
其中:
CROSS_COMPILE
被配置成arm-linux-
;ARCH
被配置成arm64
;KCFG
被配置成nanopi4_linux_defconfig
;
2.4.2 编译内核
make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} ${KALL} -j$(nproc)
其中:
KALL
被配置成nanopi4-images
;编译规则定义在arch/arm64/Makefile
文件;$(nproc)
:用于获取系统中可用的处理器核心数;
在arch/arm64/Makefile
文件;
# 编译生成kernel.img
kernel.img: Image.lz4
$(Q)scripts/mkkrnlimg $(objtree)/arch/arm64/boot/Image $(objtree)/kernel.img >/dev/null
@echo ' Image: kernel.img is ready'
DTBS := rk33*-nanopi*-rev*.dtb
# 调用scripts/mkimg编译生成resource.img(由设备树、图片资源文件组成,不包含内核)
nanopi4-images: dtbs kernel.img $(LOGO) $(LOGO_KERNEL)
$(Q)$(srctree)/scripts/mkimg --dtb $(DTBS) --keep-dtb-name
因此该步骤执行完成后,会在./kernel-rk3399
目录下生成kernel.img
、resource.img
文件 ;
root@zhengyang:/work/sambashare/rk3588/friendly/sd-rk3399# ll ./kernel-rk3399/kernel.img
-rw-r--r-- 1 root root 31207444 Sep 23 20:55 ./kernel-rk3399/kernel.img
root@zhengyang:/work/sambashare/rk3588/friendly/sd-rk3399# ll ./kernel-rk3399/resource.img
-rw-r--r-- 1 root root 4024320 Sep 23 20:55 ./kernel-rk3399/resource.img
2.4.3 编译驱动模块
执行make modules
命令编译驱动模块:
rm -rf ${KMODULES_OUTDIR}
mkdir -p ${KMODULES_OUTDIR}
make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} INSTALL_MOD_PATH=${KMODULES_OUTDIR} modules -j$(nproc)
if [ $? -ne 0 ]; then
echo "failed to build kernel modules."
exit 1
fi
其中:
- 内核模块路径被配置为
./out/output_rk3399_kmodules
:
TOPPATH=$PWD
OUT=$TOPPATH/out
if [ ! -d $OUT ]; then
echo "path not found: $OUT"
exit 1
fi
KMODULES_OUTDIR="${OUT}/output_${SOC}_kmodules" # out/output_rk3399_kmodules
2.4.4 安装驱动模块
执行make modules_install
命令安装驱动模块:
make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} INSTALL_MOD_PATH=${KMODULES_OUTDIR} modules_install
if [ $? -ne 0 ]; then
echo "failed to build kernel modules."
exit 1
fi
# 用于构建并输出内核版本号
KERNEL_VER=`make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} kernelrelease`
rm -rf ${KMODULES_OUTDIR}/lib/modules/${KERNEL_VER}/kernel/drivers/gpu/arm/mali400/
# 如果模块依赖文件modules.dep"不存在,则生成内核模块的依赖关系
[ ! -f "${KMODULES_OUTDIR}/lib/modules/${KERNEL_VER}/modules.dep" ] && depmod -b ${KMODULES_OUTDIR} -E Module.symvers -F System.map -w ${KERNEL_VER}
# 去除驱动中的符号信息
(cd ${KMODULES_OUTDIR} && find . -name \*.ko | xargs ${CROSS_COMPILE}strip --strip-unneeded)
(1)接着编译cryptodev.ko
驱动,并拷贝到内核模块路径下;
# build cryptodev-linux
(cd ${OUT} && {
if [ ! -d cryptodev-linux ]; then
git clone https://github.com/cryptodev-linux/cryptodev-linux.git -b master cryptodev-linux
fi
(cd cryptodev-linux && {
make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} KERNEL_DIR=${KERNEL_SRC}
cp cryptodev.ko ${KMODULES_OUTDIR}/lib/modules/${KERNEL_VER} -afv
})
})
cryptodev-linux
是一个linux
内核模块,它提供了一个加密硬件的接口,可在用户空间中使用该接口来执行加密和解密操作。
(2) 接着编译usb wifi driver
;
if [ ${BUILD_THIRD_PARTY_DRIVER} -eq 1 ]; then
for (( i=0; i<${#KERNEL_3RD_DRIVERS[@]}; i++ ));
do
build_external_module ${KERNEL_3RD_DRIVERS[$i]} ${KERNEL_3RD_DRIVER_BRANCHES[$i]} ${KERNEL_3RD_DRIVER_NAME[$i]}
done
fi
2.4.5 更新内核模块依赖
使用depmod
命令更新内核模块依赖:
(cd ${KMODULES_OUTDIR}/lib/modules/${KERNEL_VER}/ && {
rm -rf ./build ./source
echo "depmod ${KMODULES_OUTDIR} ${KERNEL_VER} ..."
depmod -a -b ${KMODULES_OUTDIR} ${KERNEL_VER}
})
其中:
-a
选项表示更新所有已经加载或已知的内核模块的依赖关系;-b
选项用于指定内核模块所在的目录;
2.4.6 重新打包rootfs.img
执行./tools/update_kernel_bin_to_img.sh
脚本解压并重新打包rootfs.img
;
./tools/update_kernel_bin_to_img.sh ${OUT} ${KERNEL_SRC} ${TARGET_OS} ${TOPPATH}/prebuilt
其中:
OUT
配置为./out
;KERNEL_SRC
配置为./kernel-rk3399
;TARGET_OS
配置为debian-bullseye-desktop-arm64
;TOPPATH
配置为./
;
其主要工作就是:
- 挂载根文件系统
./debian-bullseye-desktop-arm64/rootfs.img
到某个路径; - 删除原有的驱动模块,即
mount_point/lib/modules/*
文件 - 使用
cp -af
将新编译的驱动模块拷贝到mount_point/lib/modules
; - 如果存在固件(比如
wifi
固件文件为brcmfmac4356-sdio.bin
),使用cp -af
将新编译的固件库拷贝到mount_point/lib/firmware
; - 重新制作根文件系统镜像文件;
update_kernel_bin_to_img.sh
脚本源码如下:
点击查看代码
#!/bin/bash
set -eu
[ -f ${PWD}/mk-emmc-image.sh ] || {
echo "Error: please run at the script's home dir"
exit 1
}
# Automatically re-run script under sudo if not root
if [ $(id -u) -ne 0 ]; then
echo "Re-running script under sudo..."
sudo --preserve-env "$0" "$@"
exit
fi
TOP=$PWD
export MKE2FS_CONFIG="${TOP}/tools/mke2fs.conf"
if [ ! -f ${MKE2FS_CONFIG} ]; then
echo "error: ${MKE2FS_CONFIG} not found."
exit 1
fi
true ${MKFS:="${TOP}/tools/mke2fs"}
true ${SOC:=rk3399}
ARCH=arm64
KCFG=nanopi4_linux_defconfig
KIMG=kernel.img
KDTB=resource.img
KALL=nanopi4-images
CROSS_COMPILE=arm-linux-
# ${OUT} ${KERNEL_SRC} ${TOPPATH}/${TARGET_OS} ${TOPPATH}/prebuilt
if [ $# -ne 4 ]; then
echo "bug: missing arg, $0 needs four args"
exit
fi
OUT=$1
KERNEL_BUILD_DIR=$2
TARGET_OS=$3
PREBUILT=$4
KMODULES_OUTDIR="${OUT}/output_${SOC}_kmodules"
(cd ${KERNEL_BUILD_DIR} && {
cp ${KIMG} ${KDTB} ${TOP}/${TARGET_OS}/
})
# copy kernel modules to rootfs.img
if [ -f ${TARGET_OS}/rootfs.img ]; then
echo "copying kernel module and firmware to rootfs ..."
# Extract rootfs from img
simg2img ${TARGET_OS}/rootfs.img ${TARGET_OS}/r.img
mkdir -p ${OUT}/rootfs_mnt
mkdir -p ${OUT}/rootfs_new
mount -t ext4 -o loop ${TARGET_OS}/r.img ${OUT}/rootfs_mnt
if [ $? -ne 0 ]; then
echo "failed to mount ${TARGET_OS}/r.img."
exit 1
fi
rm -rf ${OUT}/rootfs_new/*
cp -af ${OUT}/rootfs_mnt/* ${OUT}/rootfs_new/
umount ${OUT}/rootfs_mnt
rm -f ${TARGET_OS}/r.img
# Processing rootfs_new
# Here s5pxx18 is different from h3/h5
[ -d ${KMODULES_OUTDIR}/lib/firmware ] && cp -af ${KMODULES_OUTDIR}/lib/firmware/* ${OUT}/rootfs_new/lib/firmware/
rm -rf ${OUT}/rootfs_new/lib/modules/*
cp -af ${KMODULES_OUTDIR}/lib/modules/* ${OUT}/rootfs_new/lib/modules/
MKFS_OPTS="-E android_sparse -t ext4 -L rootfs -M /root -b 4096"
case ${TARGET_OS} in
friendlywrt* | buildroot*)
# set default uid/gid to 0
MKFS_OPTS="-0 ${MKFS_OPTS}"
;;
*)
;;
esac
# Make rootfs.img
ROOTFS_DIR=${OUT}/rootfs_new
case ${TARGET_OS} in
friendlywrt*)
echo "prepare kernel modules for friendlywrt ..."
${TOP}/tools/prepare_friendlywrt_kernelmodules.sh ${ROOTFS_DIR}
;;
*)
;;
esac
# clean device files
(cd ${ROOTFS_DIR}/dev && find . ! -type d -exec rm {} \;)
# calc image size
IMG_SIZE=$(((`du -s -B64M ${ROOTFS_DIR} | cut -f1` + 3) * 1024 * 1024 * 64))
IMG_BLK=$((${IMG_SIZE} / 4096))
INODE_SIZE=$((`find ${ROOTFS_DIR} | wc -l` + 128))
# make fs
[ -f ${TARGET_OS}/rootfs.img ] && rm -f ${TARGET_OS}/rootfs.img
${MKFS} -N ${INODE_SIZE} ${MKFS_OPTS} -d ${ROOTFS_DIR} ${TARGET_OS}/rootfs.img ${IMG_BLK}
if [ ${TARGET_OS} != "eflasher" ]; then
echo "IMG_SIZE=${IMG_SIZE}" > ${OUT}/${TARGET_OS}_rootfs-img.info
${TOP}/tools/generate-partmap-txt.sh ${IMG_SIZE} ${TARGET_OS}
fi
else
echo "not found ${TARGET_OS}/rootfs.img"
exit 1
fi
2.5 编译内核头文件
linux-headers
(内核头文件)包含各种头文件,可以让设备具有本地编译驱动的能力。
编译内核头文件运行如下命令:
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# MK_HEADERS_DEB=1 BUILD_THIRD_PARTY_DRIVER=0 KERNEL_SRC=$PWD/kernel-rk3399 ./build-kernel.sh debian-bullseye-desktop-arm64
这里设置了MK_HEADERS_DEB=1
表示编译内核头文件;
这里我们分析一下内核头文件的编译过程,其实现代码如下:
if [ ${MK_HEADERS_DEB} -eq 1 ]; then
# 设置内核头文件dep包路径为 ./out/linux-headers-4.19.193.deb
KERNEL_HEADERS_DEB=${OUT}/linux-headers-${KERNEL_VER}.deb
rm -f ${KERNEL_HEADERS_DEB}
# 1. 重点 构建debian包
make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} bindeb-pkg
if [ $? -ne 0 ]; then
echo "failed to build kernel header."
exit 1
fi
# 跳转到 ./kernel-rk3399/debian/hdrtmp目录下
(cd ${KERNEL_SRC}/debian/hdrtmp && {
# 删除usr/src/linux-headers*/scripts/子目录下以 .o 结尾的文件和以 .*.cmd结尾的隐藏文件
find usr/src/linux-headers*/scripts/ \
-name "*.o" -o -name ".*.cmd" | xargs rm -rf
# 6. 设置头文件脚本目录./files/linux-headers-4.19.y-bin_arm64/scripts
HEADERS_SCRIPT_DIR=${TOPPATH}/files/linux-headers-4.19.y-bin_arm64/scripts
if [ -d ${HEADERS_SCRIPT_DIR} ]; then
# 拷贝脚本文件到 usr/src/linux-headers-4.19.193/scripts/
cp -avf ${HEADERS_SCRIPT_DIR}/* ./usr/src/linux-headers-*${KERNEL_VER}*/scripts/
if [ $? -ne 0 ]; then
echo "failed to copy bin file to /usr/src/linux-headers-${KERNEL_VER}."
exit 1
fi
else
echo "not found files/linux-headers-x.y.z-bin_arm64, why?"
exit 1
fi
find . -type f ! -path './DEBIAN/*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
})
# 3. 使用dpkg工具将指定的目录打包成一个debian软件包
dpkg -b ${KERNEL_SRC}/debian/hdrtmp ${KERNEL_HEADERS_DEB}
if [ $? -ne 0 ]; then
echo "failed to re-make deb package."
exit 1
fi
# clean up 移除./路径下的xxx.deb文件
(cd $TOPPATH && {
rm -f linux-*${KERNEL_VER}*_arm64.buildinfo
rm -f linux-*${KERNEL_VER}*_arm64.changes
rm -f linux-headers-*${KERNEL_VER}*_arm64.deb
rm -f linux-image-*${KERNEL_VER}*_arm64.deb
rm -f linux-libc-dev_*${KERNEL_VER}*_arm64.deb
rm -f linux-firmware-image-*${KERNEL_VER}*_arm64.deb
})
fi
接下来我们分析一下这段代码,主要涉及三大步骤。
2.5.1 make bindeb-pkg
这里其中有一条比较重要的命令:
make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} bindeb-pkgmkae bindeb-pkg
make bindeb-pkg
是一个用于构建debian
包的命令,它通常用于编译linux
内核并生成对应的debian
软件包。
当执行make bindeb-pkg
命令时,它会读取当前目录下的linux
内核源代码,并根据配置文件进行内核编译。
编译过程将包括编译内核、生成模块、创建initramfs
等步骤。最后,它将生成一组二进制文件和相关的debian
控制文件,用于创建 debian
包。
对于linux 4.19
执行完成会在内核源码debian
目录生成以下文件;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/kernel-rk3399# ll debian/
-rw-r--r-- 1 root root 6 Sep 23 21:09 arch
-rw-r--r-- 1 root root 139 Sep 23 21:09 changelog
-rw-r--r-- 1 root root 1168 Sep 23 21:09 control
-rw-r--r-- 1 root root 692 Sep 23 21:09 copyright
drwxr-xr-x 4 root root 4096 Sep 23 21:13 dbgtmp/
-rw-r--r-- 1 root root 288 Sep 23 21:13 files
drwxr-xr-x 5 root root 4096 Sep 23 21:12 hdrtmp/
drwxr-xr-x 4 root root 4096 Sep 23 21:13 headertmp/
-rwxr-xr-x 1 root root 287 Sep 23 21:09 rules*
drwxr-xr-x 7 root root 4096 Sep 23 21:12 tmp/
如果是linux 6.3
执行完成会在内核源码debian
目录生成以下文件;
root@zhengyang:/work/sambashare/rk3399/linux-6.3# ll debian/
-rw-r--r-- 1 root root 6 Sep 23 20:58 arch
-rw-r--r-- 1 root root 136 Sep 23 20:58 changelog
-rw-r--r-- 1 root root 1239 Sep 23 20:58 control
-rw-r--r-- 1 root root 691 Sep 23 20:58 copyright
-rw-r--r-- 1 root root 264 Sep 23 21:05 files
drwxr-xr-x 5 root root 4096 Sep 23 21:05 linux-headers/
drwxr-xr-x 7 root root 4096 Sep 23 21:04 linux-image/
drwxr-xr-x 4 root root 4096 Sep 23 21:05 linux-image-dbg/
drwxr-xr-x 4 root root 4096 Sep 23 21:05 linux-libc-dev/
-rwxr-xr-x 1 root root 486 Sep 23 20:58 rules*
同时会在内核上一级目录下生成如下deb
包:
linux-image-<version>.deb
:内核镜像文件,用于安装和引导新的内核;linux-headers-<version>.deb
:内核头文件,用于开发其他软件或编译内核模块;linux-libc-dev_<version>.deb
:用于构建用户空间软件的头文件和静态库;
这些生成的debian
包可以在 debian
或基于debian
的系统上安装和使用。
2.5.2 工具拷贝
拷贝./files/linux-headers-4.19.y-bin_arm64/scripts/
下的编译相关的工具到./kernel-rk3399/debian/hdrtmp/usr/src/linux-headers-4.19.193/scripts
下;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ll files/linux-headers-4.19.y-
drwxr-xr-x 8 root root 4096 Sep 23 18:21 ./
drwxr-xr-x 3 root root 4096 Sep 23 18:21 ../
-rwxr-xr-x 1 root root 32688 Sep 23 18:21 asn1_compiler*
drwxr-xr-x 2 root root 4096 Sep 23 18:21 basic/
-rwxr-xr-x 1 root root 9368 Sep 23 18:21 bin2c*
-rwxr-xr-x 1 root root 14168 Sep 23 18:21 conmakehash*
drwxr-xr-x 2 root root 4096 Sep 23 18:21 dtc/
-rwxr-xr-x 1 root root 15096 Sep 23 18:21 extract-cert*
drwxr-xr-x 2 root root 4096 Sep 23 18:21 genksyms/
-rwxr-xr-x 1 root root 23840 Sep 23 18:21 kallsyms*
drwxr-xr-x 2 root root 4096 Sep 23 18:21 kconfig/
-rwxr-xr-x 1 root root 13880 Sep 23 18:21 mkkrnlimg*
drwxr-xr-x 2 root root 4096 Sep 23 18:21 mod/
-rwxr-xr-x 1 root root 29976 Sep 23 18:21 recordmcount*
-rwxr-xr-x 1 root root 36664 Sep 23 18:21 resource_tool*
drwxr-xr-x 4 root root 4096 Sep 23 18:21 selinux/
-rwxr-xr-x 1 root root 19008 Sep 23 18:21 sortextable*
-rwxr-xr-x 1 root root 38792 Sep 23 18:21 unifdef*
2.5.3 dpkg-deb
使用dpkg
工具将指定的目录打包成一个debian
软件包;
dpkg -b ${KERNEL_SRC}/debian/hdrtmp ${KERNEL_HEADERS_DEB}
这里将./kernel-rk3399/debian/hdrtmp
打包成debian
软件包,软件包名称为./out/linux-headers-4.19.193.deb
;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ll out/
drwxr-xr-x 8 root root 4096 Sep 23 21:09 cryptodev-linux/
-rw-r--r-- 1 root root 20 Sep 23 21:18 debian-bullseye-desktop-arm64_rootfs-img.info
-rw-r--r-- 1 root root 167 Sep 23 18:21 .gitignore
-rw-r--r-- 1 root root 11046020 Sep 23 21:14 linux-headers-4.19.193.deb # 生成的dep软件包
drwxr-xr-x 3 root root 4096 Sep 23 20:36 output_rk3399_kmodules/
drwxr-xr-x 23 root root 4096 Sep 23 21:16 rootfs_new/
drwxr-xr-x 9 root root 4096 Sep 23 20:55 rtl8812au/
drwxr-xr-x 10 root root 4096 Sep 23 20:54 rtl8821CU/
drwxr-xr-x 9 root root 4096 Sep 23 20:55 rtl8822bu/
三、编译uboot
3.1 下载uboot
下载uboot
源代码:
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# git clone https://github.com/friendlyarm/uboot-rockchip --depth 1 -b nanopi4-v2017.09
3.2 编译uboot
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# UBOOT_SRC=$PWD/uboot-rockchip ./build-uboot.sh debian-bullseye-desktop-arm64
编译完成后会自动更新debian-bullseye-desktop-arm64
目录下的相关映象文件:
四、制作SD
卡固件
固件文件一般有两种:
- 单个统一固件:统一固件是由分区表、
bootloader
、uboot
、kernel
、system
等所有文件打包合并成的单个文件。一般官方正式发布的固件都是采用统一固件格式,升级统一固件将会更新主板上所有分区的数据和分区表,并且擦除主板上所有数据; - 多个分区镜像:即各个功能独立的文件,如分区表、
bootloader
、kernel
等,在开发阶段生成。独立分区镜像可以只更新指定的分区,而保持其它分区数据不被破坏,在开发过程中会很方便调试;
通过统一固件解包/打包工具,可以把统一固件解包为多个分区镜像,也可以将多个分区镜像合并为一个统一固件。
4.1 生成统一固件
将debian-bullseye-desktop-arm64
目录下的映象文件重新打包成SD
卡固件:
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ./mk-sd-image.sh debian-bullseye-desktop-arm64
Creating RAW image: out/rk3399-sd-debian-bullseye-desktop-4.19-arm64-20230923.img (7800 MB)
---------------------------------
记录了0+0 的读入
记录了0+0 的写出
0字节已复制,0.000125523 s,0.0 kB/s
----------------------------------------------------------------
[out/rk3399-sd-debian-bullseye-desktop-4.19-arm64-20230923.img] capacity = 7438MB, 7799999488 bytes
current out/rk3399-sd-debian-bullseye-desktop-4.19-arm64-20230923.img partition:
----------------------------------------------------------------
parsing ./debian-bullseye-desktop-arm64/parameter.txt:
create new GPT 9:
----------------------------------------------------------------
copy from: ./debian-bullseye-desktop-arm64 to out/rk3399-sd-debian-bullseye-desktop-4.19-arm64-20230923.img
[RAW. 0]: 198 KB | ./debian-bullseye-desktop-arm64/idbloader.img > 100% : done.
[RAW. 1]: 4096 KB | ./debian-bullseye-desktop-arm64/uboot.img > 100% : done.
[RAW. 2]: 4096 KB | ./debian-bullseye-desktop-arm64/trust.img > 100% : done.
[RAW. 3]: 48 KB | ./debian-bullseye-desktop-arm64/misc.img > 100% : done.
[RAW. 4]: 1 KB | ./debian-bullseye-desktop-arm64/dtbo.img > 100% : done.
[RAW. 5]: 3930 KB | ./debian-bullseye-desktop-arm64/resource.img > 100% : done.
[RAW. 6]: 30476 KB | ./debian-bullseye-desktop-arm64/kernel.img > 100% : done.
[RAW. 7]: 7882 KB | ./debian-bullseye-desktop-arm64/boot.img > 100% : done.
[RAW. 8]: 3907284 KB | ./debian-bullseye-desktop-arm64/rootfs.img > 100% : done.
[RAW. 9]: 156 KB | ./debian-bullseye-desktop-arm64/userdata.img > 100% : done.
----------------------------------------------------------------
---------------------------------
RAW image successfully created (21:42:28).
-rw-r--r-- 1 root root 7799999488 Sep 23 21:42 out/rk3399-sd-debian-bullseye-desktop-4.19-arm64-20230923.img
Tip: You can compress it to save disk space.
该sh
脚本内部调用了Rockchip
官方提供的打包工具生成的统一固件,由于打包工具并不开源,所以无法研究。
不过我们大致可以猜测出应该就是做了一个镜像文件,然后按照parameter.txt
进行划分分区,并将各个分区镜像依次烧录进去。
命令完成后,生成的统一固件位于out
目录,可以用dd
命令制作SD
启动卡。
4.2 制作SD
启动卡
在我们将SD
卡插入PC
上,在虚拟机ubuntu
中运行demsg
查看新接入的设备;
[32908.310364] loop7: detected capacity change from 0 to 8257536
[32909.079216] EXT4-fs (loop7): mounted filesystem with ordered data mode. Opts: (null). Quota mode: none.
[33991.894980] loop7: detected capacity change from 0 to 8126464
[33991.948702] EXT4-fs (loop7): mounted filesystem with ordered data mode. Opts: (null). Quota mode: none.
[35745.808031] usb 1-1: new high-speed USB device number 2 using ehci-pci
[35746.078673] usb 1-1: New USB device found, idVendor=14cd, idProduct=1212, bcdDevice= 1.00
[35746.078714] usb 1-1: New USB device strings: Mfr=1, Product=3, SerialNumber=2
[35746.078716] usb 1-1: Product: Mass Storage Device
[35746.078717] usb 1-1: Manufacturer: Generic
[35746.078718] usb 1-1: SerialNumber: 121220160204
[35747.340887] usb-storage 1-1:1.0: USB Mass Storage device detected
[35747.341582] scsi host33: usb-storage 1-1:1.0
[35747.342608] usbcore: registered new interface driver usb-storage
[35747.403944] usbcore: registered new interface driver uas
[35748.377640] scsi 33:0:0:0: Direct-Access Mass Storage Device 1.00 PQ: 0 ANSI: 0 CCS
[35748.378504] sd 33:0:0:0: Attached scsi generic sg3 type 0
[35748.522897] sd 33:0:0:0: [sdc] 31211520 512-byte logical blocks: (16.0 GB/14.9 GiB)
[35748.526150] sd 33:0:0:0: [sdc] Write Protect is off
[35748.526152] sd 33:0:0:0: [sdc] Mode Sense: 03 00 00 00
[35748.528185] sd 33:0:0:0: [sdc] No Caching mode page found
[35748.528254] sd 33:0:0:0: [sdc] Assuming drive cache: write through
[35748.551595] sdc: sdc1 sdc2
[35748.571109] sd 33:0:0:0: [sdc] Attached SCSI removable disk
[35802.517787] rfkill: input handler enabled
可以看到SD
卡对应的设备节点为/dev/sdc
,对应两个分区sdc1
、 sdc2
;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ls /dev/sdc*
/dev/sdc /dev/sdc1 /dev/sdc2
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# df -hT
文件系统 类型 容量 已用 可用 已用% 挂载点
udev devtmpfs 3.9G 0 3.9G 0% /dev
tmpfs tmpfs 791M 3.6M 787M 1% /run
/dev/sda5 ext4 98G 69G 24G 75% /
tmpfs tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs tmpfs 5.0M 4.0K 5.0M 1% /run/lock
tmpfs tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/sda1 vfat 511M 4.0K 511M 1% /boot/efi
/dev/loop15 squashfs 497M 497M 0 100% /snap/gnome-42-2204/132
tmpfs tmpfs 791M 0 791M 0% /run/user/0
tmpfs tmpfs 791M 36K 791M 1% /run/user/1000
/dev/sdc2 ext4 11G 311M 9.8G 4% /media/zhengyang/userdata
/dev/sdc1 ext4 4.5G 4.4G 35M 100% /media/zhengyang/rootfs
开始制作SD
启动卡:
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# dd if=out/rk3399-sd-debian-bullseye-desktop-4.19-arm64-20230923.img of=/dev/sdc bs=1M
五、Linux下烧录固件
在Linux
下烧录固件到eMMC
有多种方法:
- 其一是将
RK3399
进入到MASKROM
升级模式或者LOADER
升级模式,然后使用Linux_Upgrade_Tool
工具进行烧录,这个是Rockchip
官方提供的linux
环境下的烧录工具。 - 通过
dd
命令将image
文件烧写至image
对应的分区; - 通过搭建
TFTP
服务器,通过tftp
命令进行下载,这个我们后面的文章会涉及;
5.1 Linux_Upgrade_Tool_1.34
工具烧录
5.1.1 安装Linux_Upgrade_Tool_1.34
下载 Linux_Upgrade_Tool_1.34.zip
, 并按以下方法安装到系统中,方便调用:
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# unzip Linux_Upgrade_Tool_1.34.zip.zip
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# cd Linux_Upgrade_Tool
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo cp upgrade_tool /usr/local/bin
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo chown root:root /usr/local/bin/upgrade_tool
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo chmod a+x /usr/local/bin/upgrade_tool
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo apt-get install lib32stdc++6
5.1.2 烧写统一固件
将开发板连接上电源,并且通过HDMI
接口连接到显示设备,连接Type-C
数据线到PC
;
按住BOOT
键再长按Power
键开机(保持按下BOOT
键5秒以上),将强制进入MASKROM
模式,
烧写统一固件update.img
;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool uf update.img
由于我没有统一固件,这里就不演示了。
5.1.3 烧写分区镜像
我们在开发过程中,经常会对内核、根文件系统、uboot
进行修改,在这种情况下我们只需要替换我们修改的镜像文件即可。
按住BOOT
键再长按Power
键开机(保持按下BOOT
键5秒以上),将强制进入MASKROM
模式,烧写分区镜像;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool ul MiniLoaderAll.bin
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -p parameter.txt
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -uboot uboot.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -trust trust.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -misc misc.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -dtbo dtbo.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -resource resource.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -k kernel.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -boot boot.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -rootfs rootfs.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool di -userdata userdata.img
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool RD
对于MASKROM
模式,其中前两步和最后一步是必须的,中间的步骤根据实际情况进行调整。
由于SoC
进入到MASKROM
模式后,目标板子会运行Rockusb
驱动程序。在MASKROM
模式下,烧写镜像需要使用到DDR
,因此需要下载固件进行DDR
的初始化,所以升级之前第一步要做的就是执行sudo upgrade_tool ul MiniLoaderAll.bin
。
如果想查看分区情况,执行如下命令,该命令会读取设备上的分区表信息,支持parameter
和gpt
;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64# sudo upgrade_tool pl
Program Data in /root/.config/upgrade_tool
Partition Info(gpt):
NO LBA Size Name
01 0x00004000 0x00002000 uboot
02 0x00006000 0x00002000 trust
03 0x00008000 0x00002000 misc
04 0x0000a000 0x00002000 dtbo
05 0x0000c000 0x00008000 resource
06 0x00014000 0x00014000 kernel
07 0x00028000 0x00018000 boot
08 0x00040000 0x007c0000 rootfs
09 0x00800000 0x0151efdf userdata
5.2 dd
命令烧录
由于RK3399
的OS
默认均采用GPT
分区, 可以用dd
命令将image
文件烧写至image
对应的分区,SD
卡与eMMC
的设备节点如下:
SD/TF Card
设备节点为/dev/mmcblk0
;eMMC
设备节点为/dev/mmcblk2
;
下面步骤的前提是eMMC
已经烧录过固件,比如debian-bullseye-desktop-arm64
,具体参考上一篇文章:《Rockchip RK3399 - NanoPC-T4
开发板介绍》有关Windows
下通过RKDevTool.exe
工具烧录的内容。
下面将演示如何将内核更新到eMMC
,开发板上电进入操作系统终端命令行,使用parted
命令查看分区布局。
比如:
uboot
分区的序号为1,对应的设备节点为/dev/mmcblk2p1
;resource
分区的序号为5,对应的设备节点为/dev/mmcblk2p5
;kernel
分区的序号为6,对应的设备节点为/dev/mmcblk2p6
;rootfs
分区的序号为8,对应的设备节点为/dev/mmcblk2p8
;
dd
命令如下:
dd if=uboot.img of=/dev/mmcblk2p1 bs=1M
dd if=resource.img of=/dev/mmcblk2p5 bs=1M
dd if=kernel.img of=/dev/mmcblk2p6 bs=1M
dd if=rootfs.img of=/dev/mmcblk2p8 bs=1M
五、调试
6.1 设置密码
这里我们采用SD
卡启动的方式,将SD
卡插入开发板,重新上电系统从SD
卡启动(如果无法启动,可以尝试将eMMC
烧录的镜像清空,然后给开发板上电启动)。启动内核后设置root
用户密码, 默认没有设置root
密码;
pi@NanoPC-T4:~$ sudo passwd root
pi@NanoPC-T4:~$ su root
这里我将密码设置为123456
。
6.2 安装内核头文件
将我们编译的头文件拷贝到/opt
目录下;
root@NanoPC-T4:/home/pi# cd /opt
root@NanoPC-T4:/opt# scp root@192.168.0.200://work/sambashare/rk3399/friendly/sd-fuse_rk3399/out/linux-headers-4.19.193.deb /opt/
root@NanoPC-T4:/opt# ls -l
-rw-r--r-- 1 root root 11046020 Sep 23 14:37 linux-headers-4.19.193.deb
root@NanoPC-T4:/opt# sudo dpkg -i /opt/linux-headers-*.deb
安装完成后,我们可以看一下/usr/src
目录下是否创建了linux-headers-$(uname-r)
文件;
root@NanoPC-T4:/opt# ls -l /usr/src/
total 4
drwxr-xr-x 24 root root 4096 Sep 23 14:38 linux-headers-4.19.193
root@NanoPC-T4:/opt# ls -l /usr/src/linux-headers-4.19.193/
total 1040
drwxr-xr-x 10 root root 4096 Sep 23 14:38 Documentation
-rw-r--r-- 1 root root 563 Sep 23 11:16 Kconfig
-rw-r--r-- 1 root root 65114 Sep 23 11:16 Makefile
-rw-r--r-- 1 root root 893991 Sep 23 12:35 Module.symvers
drwxr-xr-x 26 root root 4096 Sep 23 14:38 arch
drwxr-xr-x 3 root root 4096 Sep 23 14:39 block
drwxr-xr-x 2 root root 4096 Sep 23 14:39 certs
drwxr-xr-x 4 root root 4096 Sep 23 14:39 crypto
drwxr-xr-x 143 root root 4096 Sep 23 14:39 drivers
drwxr-xr-x 2 root root 4096 Sep 23 14:39 firmware
drwxr-xr-x 78 root root 4096 Sep 23 14:39 fs
drwxr-xr-x 30 root root 4096 Sep 23 14:39 include
drwxr-xr-x 2 root root 4096 Sep 23 14:39 init
drwxr-xr-x 2 root root 4096 Sep 23 14:39 ipc
drwxr-xr-x 17 root root 4096 Sep 23 14:39 kernel
drwxr-xr-x 15 root root 4096 Sep 23 14:39 lib
drwxr-xr-x 3 root root 4096 Sep 23 14:39 mm
drwxr-xr-x 70 root root 4096 Sep 23 14:39 net
drwxr-xr-x 26 root root 4096 Sep 23 14:39 samples
drwxr-xr-x 14 root root 12288 Sep 23 14:39 scripts
drwxr-xr-x 11 root root 4096 Sep 23 14:39 security
drwxr-xr-x 26 root root 4096 Sep 23 14:39 sound
drwxr-xr-x 26 root root 4096 Sep 23 14:39 tools
drwxr-xr-x 2 root root 4096 Sep 23 14:39 usr
drwxr-xr-x 4 root root 4096 Sep 23 14:39 virt
在开发版测试编译内核模块并安装:
root@NanoPC-T4:/opt# sudo apt update
root@NanoPC-T4:/opt# sudo apt install git gcc make bc
root@NanoPC-T4:/opt# git clone https://github.com/RinCat/RTL88x2BU-Linux-Driver.git
root@NanoPC-T4:/opt# cd RTL88x2BU-Linux-Driver
root@NanoPC-T4:/opt/RTL88x2BU-Linux-Driver# make -j$(nproc)
root@NanoPC-T4:/opt/RTL88x2BU-Linux-Driver# sudo make install
root@NanoPC-T4:/opt/RTL88x2BU-Linux-Driver# sudo modprobe 88x2bu
安装完驱动,dmesg
可以看到内核输入如下日志;
[ 1269.137528] 88x2bu: loading out-of-tree module taints kernel.
[ 1269.301734] RTW: module init start
[ 1269.301821] RTW: rtl88x2bu v5.13.1-20-gbd7c7eb9d.20210702_COEX20210316-18317b7b
[ 1269.301830] RTW: rtl88x2bu BT-Coex version = COEX20210316-18317b7b
[ 1269.302279] usbcore: registered new interface driver rtl88x2bu
[ 1269.302295] RTW: module init ret=0
6.3 模拟触摸屏
在进行模拟之前,我们需要将/bin/usbdevice
文件移动到/opt/hid
目录下,这个是官方提供的用于模拟各种usb
设备的脚本,系统会自动执行,默认还是将开发板模拟成adb
终端(通过/etc/profile.d/usbdevice.sh
配置的)。
这里我们不使用官方提供的脚本,主要是这个脚本支持模拟的设备种类比较多,并且代码比较复杂,有兴趣可以自己研究。
root@NanoPC-T4:/# mkdir /opt/hid
root@NanoPC-T4:/# cd /opt/hid
root@NanoPC-T4:/opt/hid# mv /bin/usbdevice /opt/hid
接下来我们会进行模拟触摸屏设备,如果想模拟鼠标,键盘可以参考文章:《四、 模拟USB
设备》。
6.3.1 配置USB Gadget
在linux
内核根目录下执行make menuconfig
配置以下选项:
Device Drivers --->
[*] USB support --->
<*> USB Gadget Support --->
[*] Uevent notification of Gadget state
[ ] Generic serial bulk in/out
[*] Abstract Control Model (CDC ACM)
[ ] Object Exchange Model (CDC OBEX)
[ ] Network Control Model (CDC NCM)
[ ] Ethernet Control Model (CDC ECM)
[ ] Ethernet Control Model (CDC ECM) subset
[*] RNDIS
[ ] Ethernet Emulation Model (EEM)
[ ] Phonet protocol
[*] Mass storage
[ ] Loopback and sourcesink function (for testing)
[*] Function filesystem (FunctionFS)
[*] Accessory gadget
[*] Audio Source gadget
[ ] Audio Class 1.0
[ ] Audio Class 1.0 (legacy implementation)
[ ] Audio Class 2.0
[*] MIDI function
[*] HID function
[*] USB Webcam function
[ ] Printer function
<*> USB Gadget precomposed configurations (USB Raw Gadget) --->
注意的是这里是将驱动都配置到内核了,并不是配置为模块。
注意:如果配置发生了变更,记得重新编译,烧录。
6.3.2 hid_touch
脚本
下面我们创建一个触摸屏设备,然后编写hid_touch.sh
脚本,文件存放在开发板debian
系统/opt/hid
路径下;
#!/bin/bash
gadget=g2
do_start(){
has_mount=$(mount -l | grep /sys/kernel/config)
if [[ -z $has_mount ]];then
mount -t configfs none /sys/kernel/config
fi
cd /sys/kernel/config/usb_gadget
# 当我们创建完这个文件夹之后,系统自动的在这个文件夹中创建usb相关的内容 ,这些内容需要由创建者自己填写
if [[ ! -d ${gadget} ]]; then
mkdir ${gadget}
fi
cd ${gadget}
#设置USB协议版本USB2.0
echo 0x0200 > bcdUSB
#定义产品的VendorID和ProductID
echo "0x0525" > idVendor
echo "0xa4ac" > idProduct
#实例化"英语"ID:
mkdir strings/0x409
#将开发商、产品和序列号字符串写入内核
echo "76543210" > strings/0x409/serialnumber
echo "mkelehk" > strings/0x409/manufacturer
echo "touch_screen" > strings/0x409/product
#创建一个USB配置实例
if [[ ! -d configs/c.1 ]]; then
mkdir configs/c.1
fi
#定义配置描述符使用的字符串
if [[ ! -d configs/c.1/strings/0x409 ]]; then
mkdir configs/c.1/strings/0x409
fi
echo "hid" > configs/c.1/strings/0x409/configuration
#创建接口
mkdir functions/hid.0
#接口0,模拟触摸屏
echo 0 > functions/hid.0/subclass
echo 0 > functions/hid.0/protocol
echo 5 > functions/hid.0/report_length #标识该hid设备每次发送的报表长度为5字节
echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x05\\x15\\x00\\x25\\x01\\x95\\x05\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x03\\x81\\x01\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x00\\x26\\xff\\x7f\\x35\\x00\\x46\\xff\\x7f\\x75\\x10\\x95\\x02\\x81\\x02\\xc0\\xc0 > functions/hid.0/report_desc
#捆绑接口到配置config.1
ln -s functions/hid.0 configs/c.1
#配置USB3.0 OTG0的工作模式为Device(设备)
echo peripheral > /sys/devices/platform/ff770000.syscon/ff770000.syscon:usb2-phy@e450/otg_mode
echo "sleep 3s"
sleep 3s
#将gadget驱动注册到UDC上,插上USB线到电脑上,电脑就会枚举USB设备。
echo fe800000.dwc3 > UDC # 可以通过 ls /sys/class/udc/ 命令USB设备控制器
}
do_stop() {
cd /sys/kernel/config/usb_gadget/${gadaget}
echo "" > UDC
}
case $1 in
start)
echo "Start hid gadget "
do_start
;;
stop)
echo "Stop hid gadget"
do_stop
;;
*)
echo "Usage: $0 (stop | start)"
;;
esac
对于触摸屏设备,我们配置:
subclass
:接口子类,配置为0;protocol
:接口协议,配置为0;report_length
:报告长度配置为5,触摸屏与鼠标的不同点是鼠标的上报值是相对坐标,触摸屏是绝对坐标,鼠标xy轴分别需要一个字节,而触摸屏一般为16bit即两个字节;
在开发板debian
系统运行命令模拟触摸屏:
root@NanoPC-T4:/opt/hid# ls /sys/kernel/config/usb_gadget/
root@NanoPC-T4:/opt/hid# bash hid_touch.sh start
Start hid gadget
sleep 3s
root@NanoPC-T4:/opt/hid# ls /sys/kernel/config/usb_gadget/
g2
root@NanoPC-T4:/opt/hid# ls /sys/kernel/config/usb_gadget/g2
UDC bDeviceProtocol bMaxPacketSize0 bcdUSB functions idVendor strings
bDeviceClass bDeviceSubClass bcdDevice configs idProduct os_desc
此时会在/dev
目录下生成了设备节点/dev/hidg0
;
root@NanoPC-T4:/opt/hid# ls -l /dev/hidg*
crw------- 1 root root 236, 0 Sep 24 14:30 /dev/hidg0
如果需卸载触摸屏,执行如下命令:
root@NanoPC-T4:/opt/hid# bash hid_touch.sh stop
6.3.3 编写应用程序
然后我们通过设备节点/dev/hidg0
、就可以模拟触摸屏实现与PC
的通信。
编写测试应用程序hid_touch_control.c
,文件存放在debian
开发板/opt/hid
路径下;
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
/**
* 触摸屏每次发送5字节报告
* report[0]的D0就是左键,D1就是右键,D2就是中键
* report[1]为X轴低字节
* report[2]为X轴高字节
* report[3]为Y轴低字节
* report[4]为Y轴高字节,
*/
/**
* @brief 将焦点移动到指定位置
*
* @param fd 文件描述符
* @param x x轴坐标
* @param y y轴坐标
*/
void move_To(int fd ,int x, int y)
{
char report[5]={0,0,0,0,0};
report[0] = 0x00;
report[1] = x & 0xFF;
report[2] = (x >> 8) & 0xFF;
report[3] = y & 0xFF;
report[4] = (y >> 8) & 0xFF;
// 写入失败
if (write(fd, report, 5) != 5) {
perror("move_To error");
return;
}
usleep(50000);
}
/**
* @brief 函数功能:画线段的函数。
*
*@param fd 文件描述符
* @param x x轴坐标
* @param y y轴坐标
*/
void line_to(int fd, int x, int y)
{
char report[5]={0,0,0,0,0};
report[0] = 0x01; //左键按下
report[1] = x & 0xFF;
report[2] = (x >> 8) & 0xFF;
report[3] = y & 0xFF;
report[4] = (y >> 8) & 0xFF;
if (write(fd, report, 5) != 5) {
perror("line_to error");
return;
}
usleep(50000);
}
int main(int argc, char const *argv[])
{
const char *filename = NULL;
int fd = 0;
if (argc != 3)
{
fprintf(stderr, "Usage: %s devname a/l\n",
argv[0]);
return 1;
}
filename = argv[1];
if ((fd = open(filename, O_RDWR, 0666)) == -1) {
perror(filename);
return 1;
}
if(argv[2][0] == 'a')
{
printf("Draw Tri-angle begin\n");
move_To(fd, 8888, 8888); //移动到(8888,8888)
line_to(fd, 8888, 8888); //开始画线
line_to(fd, 12000, 12000); //画线到(12000,12000)
move_To(fd, 12000, 12000); //松开鼠标左键
move_To(fd, 12000, 12000); //移动到(12000,12000)
line_to(fd, 12000, 12000); //开始画线
line_to(fd ,8888, 12000); //画线到(8888,12000)
move_To(fd, 8888, 12000); //松开鼠标左键
move_To(fd, 8888, 12000); //移动到(8888,12000)
line_to(fd, 8888, 12000); //开始画线
line_to(fd ,8888, 8888); //画线到(8888,8888)
move_To(fd, 8888, 8888); //松开鼠标左键
printf("Draw Tri-angle end\n");
}
if(argv[2][0] == 'l'){
printf("Draw line begin\n");
move_To(fd, 10000, 10000); // 移动到(10000,10000)
line_to(fd, 10000, 10000); // 开始画线
line_to(fd, 20000, 10000); // 画线到(20000,10000)
move_To(fd, 20000, 10000); // 松开鼠标左键
printf("Draw line end\n");
}
close(fd);
return 0;
}
直接在开发板编译应用程序:
root@NanoPC-T4:/opt/hid# sudo apt update
root@NanoPC-T4:/opt/hid# sudo apt install git gcc make bc
root@NanoPC-T4:/opt/hid# gcc -o hid_touch_control hid_touch_control.c
root@NanoPC-T4:/opt/hid# ls -l
-rwxr-xr-x 1 root root 2659 Sep 24 14:24 hid_touch.sh
-rwxr-xr-x 1 root root 13824 Sep 24 14:40 hid_touch_control
-rwxr-xr-x 1 root root 3007 Sep 24 07:28 hid_touch_control.c
-rwxrwxr-x 1 pi pi 13207 Sep 24 13:18 usbdevice
6.3.4 测试触摸屏
(1) Type-C
连接线将开发板USB3.0 Type-C
接口与开发板上的USB2.0 Host Type-A
接口(对应开发板上的USBH3
)连接起来;内核日志信息如下:
[ 1355.592509] usb 3-1: new high-speed USB device number 2 using xhci-hcd
[ 1355.741431] usb 3-1: New USB device found, idVendor=0525, idProduct=a4ac, bcdDevice= 4.19
[ 1355.741539] usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 1355.741573] usb 3-1: Product: touch_screen
[ 1355.741603] usb 3-1: Manufacturer: mkelehk
[ 1355.741633] usb 3-1: SerialNumber: 76543210
[ 1355.742597] android_work: sent uevent USB_STATE=CONNECTED
[ 1355.758927] configfs-gadget gadget: high-speed config #1: c
[ 1355.760989] android_work: sent uevent USB_STATE=CONFIGURED
[ 1355.766544] input: mkelehk touch_screen as /devices/platform/usb@fe900000/fe900000.dwc3/xhci-hcd.1.auto/usb3/3-1/3-1:1.0/0003:0525:A4AC.0007/input/input16
[ 1355.825419] hid-generic 0003:0525:A4AC.0007: input,hidraw4: USB HID v1.01 Mouse [mkelehk touch_screen] on usb-xhci-hcd.1.auto-1/input0
(2) Type-C
连接线将开发板USB3.0 Type-C
接口与PC
连接起来,PC
机打开画图软件,窗口调大最大,在开发板debian
桌面环境打开一个终端输入如下命令模拟触摸屏:
root@rk3399:/shell/hid# ./hid_touch_control /dev/hidg0 a
Draw Tri-angle begin
Draw Tri-angle end
root@rk3399:/shell/hid# ./hid_touch_control /dev/hidg0 l
Draw line begin
Draw line end
运行效果如下:
6.4 测试USB3.0 Type-C PHY1
如果你的开发板USB3.0 Type-C PHY1
、USB2.0 PHY1
物理接口被设计为USB3.0 OTG Type-A
的话,就可以尝试将USB3.0 OTG1
控制器工作模式也设置为otg
(同时支持主机/丛机)。比如(下面演示使用的并不是NanoPC-T4
开发板,而是使用的SOM-RK3399
核心板+定制底板):
在《Rockchip RK3399 - USB
基础》我们介绍过USB3.0 Type-C PHY1
、USB2.0 PHY1
物理接口被设置为USB3.0 Host Type-A
的原理图,与USB3.0 OTG Type-A
的主要区别:
- 使用了
TYPEC1_U2VBUSDET
引脚检测VBUS
信号; VBUS
没有供电;
由于VBUS
是断开的,所以该接口只能作为USB
设备使用。当插入USB
主机时,USB
主机会给USB
设备VUSB
供电,TYPEC1_U2VBUSDET
引脚检测到上升沿。
如果既想作为Device
又想作为Host
,EN
引脚可以连接到GPIO
口,通过GPIO
来控制VBUS
电源,比如:
6.4.1 修改usbdrd_dwc3_1
设备节点
修改arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi
设备节点usbdrd_dwc3_1
设备节点:
&usbdrd_dwc3_1 {
/* 配置dr_mode为peripheral */
dr_mode = "peripheral";
status = "okay";
};
测试发现这个不可以配置成otg
,配置成otg
,后面通过echo peripheral > /sys/devices/platform/ff770000.syscon/ff770000.syscon:usb2-phy@e460/otg_mode
更改工作模式后,将开发板模拟成USB
设备并不会生效,具体原因还不太清楚,等后面有时间分析驱动源码再说。
同时也不可以配置成host
,主要是因为作为USB
主机需要为USB
设备供电,但是由于我们这个电路设计VBUS
并没有连接电源。
6.4.2 修改usbdrd_dwc3_0
设备节点
修改arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi
设备节点usbdrd_dwc3_0
设备节点:
&usbdrd_dwc3_0 {
/* 配置dr_mode为otg */
dr_mode = "otg";
status = "okay";
extcon = <&fusb0>;
extcon = <&fusb0>;
extcon = <&fusb0>;
};
6.4.3 编译烧录
重新编译内核并烧录,查看USB3.0 OTG0
、USB3.0 OTG1
控制器的工作模式;
root@SOM-RK3399v2:~# cat /sys/devices/platform/ff770000.syscon/ff770000.syscon:usb2-phy@e450/otg_mode
otg
root@rk3399:~# cat /sys/devices/platform/ff770000.syscon/ff770000.syscon:usb2-phy@e460/otg_mode
peripheral
更改工作模式为Host
(主机),这种做法只会临时有效:
root@rk3399:~# echo host > /sys/devices/platform/ff770000.syscon/ff770000.syscon:usb2-phy@e450/otg_mode
USB3.0 Type-C PHY0
接口被配置为了主机,因此可以通过USB3.0 Type-C PHY0
、USB2.0 PHY0
物理接口连接USB
设备测试,这里就不演示了。
6.4.4 模拟USB
设备
USB3.0 OTG1
被配置为了从设备,因此可以通过``USB3.0 Type-C PHY1、
USB2.0 PHY1物理接口将开发板模拟成
USB设备(参考前面介绍的脚本同时模拟鼠标、键盘、触摸屏),
hid_keyboard_mouse.sh`脚本内容如下:
#!/bin/bash
gadget=g1
do_start(){
has_mount=$(mount -l | grep /sys/kernel/config)
if [[ -z $has_mount ]];then
mount -t configfs none /sys/kernel/config
fi
cd /sys/kernel/config/usb_gadget
# 当我们创建完这个文件夹之后,系统自动的在这个文件夹中创建usb相关的内容 ,这些内容需要由创建者自己填写
if [[ ! -d ${gadget} ]]; then
mkdir ${gadget}
else
exit 0
fi
cd ${gadget}
#设置USB协议版本USB2.0
echo 0x0200 > bcdUSB
#定义产品的VendorID和ProductID
echo "0x0525" > idVendor
echo "0xa4ac" > idProduct
#实例化"英语"ID:
mkdir strings/0x409
#将开发商、产品和序列号字符串写入内核
echo "76543210" > strings/0x409/serialnumber
echo "mkelehk" > strings/0x409/manufacturer
echo "keyboard_mouse" > strings/0x409/product
#创建一个USB配置实例
if [[ ! -d configs/c.1 ]]; then
mkdir configs/c.1
fi
#定义配置描述符使用的字符串
if [[ ! -d configs/c.1/strings/0x409 ]]; then
mkdir configs/c.1/strings/0x409
fi
echo "hid" > configs/c.1/strings/0x409/configuration
#创建接口
mkdir functions/hid.0 #键盘
mkdir functions/hid.1 #鼠标
mkdir functions/hid.2 #触摸屏
#接口0,模拟键盘
echo 1 > functions/hid.0/subclass #启动设备符
echo 1 > functions/hid.0/protocol #标识键盘设备
echo 8 > functions/hid.0/report_length #标识该hid设备每次发送的报表长度为8字节
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.0/report_desc
#接口1,模拟键盘鼠标
echo 1 > functions/hid.1/subclass #启动设备符
echo 2 > functions/hid.1/protocol #鼠标协议
echo 4 > functions/hid.1/report_length # 相对值是4
echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x09\\x38\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x03\\x81\\x06\\xc0\\xc0 > functions/hid.1/report_desc
#接口2,模拟触摸屏
echo 0 > functions/hid.2/subclass
echo 0 > functions/hid.2/protocol
echo 5 > functions/hid.2/report_length #标识该hid设备每次发送的报表长度为5字节
echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x05\\x15\\x00\\x25\\x01\\x95\\x05\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x03\\x81\\x01\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x00\\x26\\xff\\x7f\\x35\\x00\\x46\\xff\\x7f\\x75\\x10\\x95\\x02\\x81\\x02\\xc0\\xc0 > functions/hid.2/report_desc
#捆绑接口到配置config.1
ln -s functions/hid.0 configs/c.1
ln -s functions/hid.1 configs/c.1
ln -s functions/hid.2 configs/c.1
#配置USB3.0 OTG1的工作模式为Device(设备):
#echo peripheral > /sys/devices/platform/ff770000.syscon/ff770000.syscon:usb2-phy@e460/otg_mode
echo "sleep 3s"
sleep 3s
#将gadget驱动注册到UDC上,插上USB线到电脑上,电脑就会枚举USB设备。
echo fe900000.dwc3 > UDC
}
do_stop() {
cd /sys/kernel/config/usb_gadget/${gadaget}
echo "" > UDC
}
case $1 in
start)
echo "Start hid gadget "
do_start
;;
stop)
echo "Stop hid gadget"
do_stop
;;
*)
echo "Usage: $0 (stop | start)"
;;
esac
在/etc/profile.d/
目录下创建一个文件hid.sh
,文件内容如下:
#!/bin/sh
bash /opt/hid/hid_keyboard_mouse.sh start
修改文件权限:
root@SOM-RK3399v2:/etc/profile.d# chown -R pi:pi hid.sh
root@SOM-RK3399v2:/etc/profile.d# ls -l
-rw-r--r-- 1 pi pi 58 Sep 26 14:18 hid.sh
重新开机,开机会自动执行该目录下的所有shell
脚本。查看hid
设备;
root@SOM-RK3399v2:/home/pi# ls /dev/hidg* -nR
crw------- 1 0 0 236, 0 Sep 26 14:25 /dev/hidg0 # 键盘
crw------- 1 0 0 236, 1 Sep 26 14:25 /dev/hidg1 # 鼠标
crw------- 1 0 0 236, 2 Sep 26 14:25 /dev/hidg2 # 触摸屏
root@SOM-RK3399v2:/home/pi# ls /sys/kernel/config/usb_gadget/g1/
UDC bDeviceProtocol bMaxPacketSize0 bcdUSB functions idVendor strings
bDeviceClass bDeviceSubClass bcdDevice configs idProduct os_desc
6.4.5 测试
通过USB OTG
线将USB3.0 Type-C PHY0
、USB2.0 PHY0
物理接口连接到PC
,开发板内核输出如下信息:
[ 160.051545] dwc3 fe900000.dwc3: device reset
[ 160.115243] android_work: sent uevent USB_STATE=CONNECTED
[ 160.223255] dwc3 fe900000.dwc3: device reset
[ 160.224010] android_work: sent uevent USB_STATE=DISCONNECTED
[ 160.282067] android_work: sent uevent USB_STATE=CONNECTED
[ 160.286240] configfs-gadget gadget: high-speed config #1: c
[ 160.287203] android_work: sent uevent USB_STATE=CONFIGURED
此时PC
识别到一个USB
设备插入,windows
会提示用户;并且查看该USB
设备信息可以看到VID
、PID
就是我们之前设置的。
(1) PC
机将鼠标聚焦在一个文本输入框中,然后在开发板debian
桌面环境打开一个终端输入如下命令模拟键盘:
root@SOM-RK3399v2:/# /opt/hid/hid_gadget_control /dev/hidg0 keyboard
keyboard options:
--hold
--left-ctrl
--right-ctrl
--left-shift
--right-shift
--left-alt
--right-alt
--left-meta
--right-meta
keyboard values:
[a-z] or
--return --esc
--bckspc --tab
--spacebar --caps-lock
--f1 --f2
--f3 --f4
--f5 --f6
--f7 --f8
--f9 --f10
--f11 --f12
--insert --home
--pageup --del
--end --pagedown
--right --left
--down --kp-enter
--up --num-lock
a b c d e f # 一次最多输入6个字符,输入完回车,
此时PC
文本输入框会出现这几个字符。
需要注意的是:该应用程序目前只支持上面提示符给出的字符,比如a-z、esc、tab等,并不支持数字。
(2) 在开发板debian
桌面环境打开一个终端输入如下命令模拟鼠标:
root@SOM-RK3399v2:/# /opt/hid/hid_gadget_control /dev/hidg1 mouse
mouse options:
--hold
--b1
--b2
--b3
mouse values:
Two signed numbers
--quit to close
200 210 # 鼠标向左上移动
-200 210 # 鼠标向右上移动
200 -210 # 鼠标向左下移动
-200 -210 # 鼠标向右下移动
0 0 --b1 # 鼠标左键点击
0 0 --b2 # 鼠标右键点击
0 0 --b3 # 鼠标中键点击
注意:这个测试发现PC
鼠标没有反应,等有时间再研究一下。
(3) PC
机打开画图软件,窗口调大最大,在开发板debian
桌面环境打开一个终端输入如下命令模拟触摸屏:
root@SOM-RK3399v2:/# /opt/hid/hid_touch_control /dev/hidg2 a
root@SOM-RK3399v2:/# /opt/hid/hid_touch_control /dev/hidg2 l
运行效果如下:
6.5 安装应用程序
6.5.1 安装node_exporter
root@SOM-RK3399v2:/opt# wget https://521github.com/prometheus/node_exporter/releases/download/v1.1.2/node_exporter-1.1.2.linux-arm64.tar.gz
root@SOM-RK3399v2:/opt# tar -zxvf node_exporter-1.1.2.linux-arm64.tar.gz
root@SOM-RK3399v2:/opt# cd node_exporter-1.1.2.linux-arm64
root@SOM-RK3399v2:/opt/node_exporter-1.1.2.linux-arm64# nohup ./node_exporter > node_exporter.log 2>&1 &
端口号9100
, 访问http://192.168.0.102:9100
,这里我的开发版ip为192.168.0.102
。
6.5.2 安装ibpam-google-authenticator
root@SOM-RK3399v2:/opt# sudo apt install libpam-google-authenticator
6.5.3 安装nginx
安装nginx
并查看版本号:
root@SOM-RK3399v2:/opt# apt-get install nginx
root@SOM-RK3399v2:/opt# ps -ef | grep nginx
root 4508 1 0 15:10 ? 00:00:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
www-data 4511 4508 0 15:10 ? 00:00:00 nginx: worker process
www-data 4512 4508 0 15:10 ? 00:00:00 nginx: worker process
www-data 4513 4508 0 15:10 ? 00:00:00 nginx: worker process
www-data 4514 4508 0 15:10 ? 00:00:00 nginx: worker process
www-data 4515 4508 0 15:10 ? 00:00:00 nginx: worker process
www-data 4516 4508 0 15:10 ? 00:00:00 nginx: worker process
root 4560 1506 0 15:11 pts/0 00:00:00 grep nginx
root@SOM-RK3399v2:/opt# /usr/sbin/nginx -v
nginx version: nginx/1.18.0
安装nginx
后的文件位置;
/usr/sbin/nginx
:主程序;/etc/nginx
:存放配置文件;/usr/share/nginx
:存放静态文件;/var/log/nginx
:存放日志;
查看nginx
状态:
root@SOM-RK3399v2:/opt# systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2023-09-26 15:10:41 UTC; 56s ago
Docs: man:nginx(8)
Process: 4420 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 4421 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 4508 (nginx)
Tasks: 7 (limit: 4567)
Memory: 7.4M
CPU: 139ms
CGroup: /system.slice/nginx.service
├─4508 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
├─4511 nginx: worker process
├─4512 nginx: worker process
├─4513 nginx: worker process
├─4514 nginx: worker process
├─4515 nginx: worker process
└─4516 nginx: worker process
停止nginx
;
root@SOM-RK3399v2:/opt# systemctl stop nginx
启动nginx
:
root@SOM-RK3399v2:/opt# systemctl start nginx
如果要卸载nginx
,运行如下命令:
apt-get --purge autoremove nginx
6.5.4 安装libusb-1.0-0-dev
root@SOM-RK3399v2:/opt# sudo apt install libusb-dev libusb-1.0-0-dev
在使用libusb
库的时候需要指定:
-
头文件
libusb.h
安装到了/usr/include/libusb-1.0
路径; -
动态库
libusb-1.0.so
安装到了/usr/lib/aarch64-linux-gnu/
路径;
在/opt/libusb
目录下编写测试应用程序listdev.c
:
#include <stdio.h>
#include <sys/types.h>
#include <libusb.h>
static void print_devs(libusb_device **devs)
{
libusb_device *dev;
int i = 0;
while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
fprintf(stderr, "failed to get device descriptor");
return;
}
printf("%04x:%04x (bus %d, device %d)\n",
desc.idVendor, desc.idProduct,
libusb_get_bus_number(dev), libusb_get_device_address(dev));
}
}
int main(void)
{
libusb_device **devs;
int r;
ssize_t cnt;
r = libusb_init(NULL);
if (r < 0)
return r;
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0)
return (int) cnt;
print_devs(devs);
libusb_free_device_list(devs, 1);
libusb_exit(NULL);
return 0;
}
编译并运行:
root@SOM-RK3399v2:/opt/libusb$ gcc -o listdev listdev.c -I/usr/include/libusb-1.0 -lusb-1.0
root@SOM-RK3399v2:/opt/libusb$ ./listdev
1d6b:0003 (bus 4, device 1)
1d6b:0002 (bus 3, device 1)
1d6b:0001 (bus 2, device 1)
1a86:e5e3 (bus 1, device 4)
1a2c:4d7e (bus 1, device 3)
14cd:8601 (bus 1, device 2)
1d6b:0002 (bus 1, device 1)
046d:c52b (bus 6, device 2)
1d6b:0001 (bus 6, device 1)
1d6b:0002 (bus 5, device 1)
6.5.5 安装libjsoncpp-dev
root@SOM-RK3399v2:/opt# sudo apt-get install libjsoncpp-dev
6.5.6 安装nfs-common
(1) 安装NFS
内核服务器软件包:
root@SOM-RK3399v2:/opt# sudo apt install nfs-kernel-server portmap
接下来,配置NFS
导出。编辑/etc/exports
文件以定义要共享的目录和允许访问的客户端:
root@SOM-RK3399v2:/opt# vim /etc/exports
将以下内容添加到文件中,共享/opt
目录:
/opt *(rw,sync,no_subtree_check)
保存文件并退出编辑器。修改完毕之后,启动NFS
服务器;
root@SOM-RK3399v2:/opt# systemctl restart nfs-kernel-server
查看是否启动:
netstat -a | grep nfs
实际上并没有成功,出现如下错误:
Sep 26 16:22:36 SOM-RK3399v2 exportfs[1756]: exportfs: /opt does not support NFS export
如何解决目前还不是很清楚。
(2) 在客户端安装NFS
客户端并挂载NFS
共享:
root@zhengyang:/# sudo apt install nfs-common
创建一个挂载点,例如 /mnt/nfs_share
:
root@zhengyang:/# sudo mkdir -p /mnt/nfs_share
挂载NFS
共享到挂载点。将 <server_IP>
替换为NFS
服务器的IP地址:
root@zhengyang:/# mount -t nfs 192.168.0.102:/opt /mnt/nfs_share
检查挂载是否成功:
root@zhengyang:/# df -h
root@zhengyang:~# ls /mnt/nfs_share
卸载共享目录,运行以下命令:
root@zhengyang:~# umount /mnt/rootfs
6.5.7 安装raw-gadget
下载源码:
root@SOM-RK3399v2:/opt# git clone https://521github.com/xairy/raw-gadget.git
查看UDC
(USB
设备控制器)设备名称:
root@SOM-RK3399v2:/opt# ls /sys/class/udc/
fe900000.dwc3
查看UDC
驱动名字:
root@SOM-RK3399v2:/opt# cat /sys/class/udc/fe900000.dwc3/uevent
DRIVER=configfs-gadget
USB_UDC_NAME=dwc3-gadget
USB_UDC_DRIVER=g1
(1) 安装raw-gadget
编译源码:
root@SOM-RK3399v2:/opt# cd raw-gadget/raw_gadget/
root@SOM-RK3399v2:/opt/raw-gadget/raw_gadget# make
make -C /lib/modules/4.19.193/build M=/opt/raw-gadget/raw_gadget SUBDIRS=/opt/raw-gadget/raw_gadget modules
make[1]: Entering directory '/usr/src/linux-headers-4.19.193'
CC [M] /opt/raw-gadget/raw_gadget/raw_gadget.o
Building modules, stage 2.
MODPOST 1 modules
CC /opt/raw-gadget/raw_gadget/raw_gadget.mod.o
LD [M] /opt/raw-gadget/raw_gadget/raw_gadget.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.19.193'
安装驱动:
root@SOM-RK3399v2:/opt/raw-gadget/raw_gadget# ./insmod.sh
+ sudo modprobe udc_core
+ sudo insmod ./raw_gadget.ko
root@SOM-RK3399v2:/opt/raw-gadget/raw_gadget# lsmod
Module Size Used by
raw_gadget 28672 0
algif_hash 20480 1
algif_skcipher 16384 1
af_alg 24576 6 algif_hash,algif_skcipher
bnep 24576 2
hci_uart 61440 1
btbcm 16384 1 hci_uart
serdev 20480 1 hci_uart
hid_logitech_hidpp 36864 0
crct10dif_ce 16384 0
bcmdhd 1695744 0
joydev 28672 0
cfg80211 638976 1 bcmdhd
hid_logitech_dj 20480 0
l2tp_ppp 24576 0
l2tp_netlink 24576 1 l2tp_ppp
l2tp_core 28672 2 l2tp_ppp,l2tp_netlink
ip6_udp_tunnel 16384 1 l2tp_core
udp_tunnel 16384 1 l2tp_core
pppox 16384 1 l2tp_ppp
uio_pdrv_genirq 16384 0
uio 20480 1 uio_pdrv_genirq
binfmt_misc 20480 1
ledtrig_netdev 16384 0
nfsd 344064 1
ip_tables 28672 0
(2) 测试
编译源码:
root@SOM-RK3399v2:/opt/raw-gadget/raw_gadget# cd /opt/raw-gadget/examples/
root@SOM-RK3399v2:/opt/raw-gadget/examples# ls -l
total 56
-rw-r--r-- 1 root root 206 Sep 26 16:31 Makefile
-rw-r--r-- 1 root root 911 Sep 26 16:31 README.md
-rw-r--r-- 1 root root 23000 Sep 26 16:31 keyboard.c
-rw-r--r-- 1 root root 21525 Sep 26 16:31 printer.c
root@SOM-RK3399v2:/opt/raw-gadget/examples# make
gcc -o keyboard keyboard.c -O2 -Wall -g
gcc -o printer printer.c -O2 -Wall -g -lpthread
测试键盘:
root@SOM-RK3399v2:/opt/raw-gadget/examples# sudo ./keyboard fe900000.dwc3 fe900000.dwc3
ioctl(USB_RAW_IOCTL_RUN): Device or resource busy
这个是因为UDC
已经被我使用了,已经被用来模拟USB
设备了。
6.6 导出根文件系统
当用户已经在一台设备上完成工作环境的部署,需要将当前环境完整导出,以批量部署到其它同设备上,可以通过导出设备文件系统来备份当前的开发环境。
开发板debian
配置允许root
远程连接:
root@SOM-RK3399v2:/opt# vim /etc/ssh/sshd_config
将PermitRootLogin
设置为yes
,PasswordAuthentication
设置为yes
即可,:wq
保存退出即可。
重新启动SSH服务器:
root@SOM-RK3399v2:/opt# /etc/init.d/ssh restart
6.6.1 从设备导出根文件系统
ubuntu
虚拟机以及开发板安装rsync
服务:
root@zhengyang:/work/sambashare/rk3399/rootfs# apt install rsync # 虚拟机
root@SOM-RK3399v2:/opt# apt install rsync #开发板
在ubuntu
虚拟机创建一个操作目录,如 debian
root@zhengyang:/work/sambashare/rk3399/rootfs# mkdir debian
root@zhengyang:/work/sambashare/rk3399/rootfs# cd debian/
把开发板上的根文件系统克隆到debian
文件夹,此处假设开发板IP
为192.168.0.102
;
root@zhengyang:/work/sambashare/rk3399/rootfs/debian# rsync -avx root@192.168.0.102:/ ./
命令执行完后,./debian
里就保存着和开发板系统上一样的文件系统内容了;
总用量 84
lrwxrwxrwx 1 root root 7 Jun 29 17:13 bin -> usr/bin
drwxr-xr-x 4 root root 4096 Jun 29 17:13 boot
drwxr-xr-x 2 root root 4096 Sep 27 01:06 dev
drwxr-xr-x 116 root root 4096 Sep 27 00:23 etc
drwxr-xr-x 3 root root 4096 Jul 11 2022 home
lrwxrwxrwx 1 root root 7 Jun 29 17:13 lib -> usr/lib
drwx------ 2 root root 4096 Sep 15 00:13 lost+found
drwxr-xr-x 3 root root 4096 Sep 13 15:35 man
drwxr-xr-x 2 root root 4096 Jun 29 17:13 media
drwxr-xr-x 3 root root 4096 Sep 27 00:53 mnt
drwxr-xr-x 2 root root 4096 Jun 29 17:47 oem
drwxrwxrwx 8 root root 4096 Sep 27 00:40 opt
dr-xr-xr-x 2 root root 4096 Jan 1 1970 proc
drwxr-xr-x 4 root root 4096 Sep 27 01:13 root
drwxr-xr-x 2 root root 4096 Sep 27 01:17 run
lrwxrwxrwx 1 root root 8 Jun 29 17:13 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Jun 29 17:13 srv
dr-xr-xr-x 2 root root 4096 Sep 27 01:06 sys
drwxr-xr-x 5 root root 4096 Jun 30 10:41 system
drwxrwxrwt 12 root root 4096 Sep 27 01:16 tmp
drwxr-xr-x 2 root root 4096 Jun 29 17:47 userdata
drwxr-xr-x 11 root root 4096 Sep 27 01:06 usr
drwxr-xr-x 12 root root 4096 Sep 26 23:10 var
drwxr-xr-x 3 root root 4096 Jun 29 17:47 vendor
6.6.2 打包生成rootfs.img
文件
建立根文件系统挂载点debian_rootfs
:
root@zhengyang:/work/sambashare/rk3399/rootfs/debian# cd ../
root@zhengyang:/work/sambashare/rk3399/rootfs# mkdir debian_rootfs
创建空镜像文件debian_ext4_rootfs.img
,大小6144(设置为文件系统大小x2),具体需要的大小可以通过d 路径 -h
查看我们根文件系统大小调整;
root@zhengyang:/work/sambashare/rk3399/rootfs# dd if=/dev/zero of=debian_ext4_rootfs.img bs=1M count=6144
root@zhengyang:/work/sambashare/rk3399/rootfs# ll debian_ext4_rootfs.img
-rw-r--r-- 1 root root 6442450944 Sep 27 01:26 debian_ext4_rootfs.img
root@zhengyang:/work/sambashare/rk3399/rootfs# mkfs.ext4 debian_ext4_rootfs.img
将该镜像文件挂载到debian_rootfs
:
root@zhengyang:/work/sambashare/rk3399/rootfs# mount debian_ext4_rootfs.img debian_rootfs/
然后将debian
的文件复制到该空文件夹中:
root@zhengyang:/work/sambashare/rk3399/rootfs# cp ./debian/* ./debian_rootfs/ -af
使用df
命令可以查看是否已经挂载:
root@zhengyang:/work/sambashare/rk3399/rootfs# df debian_rootfs
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/loop15 6102624 4513424 1258244 79% /work/sambashare/rk3399/rootfs/debian_rootfs
将之前挂载的卸载掉:
root@zhengyang:/work/sambashare/rk3399/rootfs# umount debian_rootfs
root@zhengyang:/work/sambashare/rk3399/rootfs# ll debian_rootfs/
总用量 8
drwxr-xr-x 2 root root 4096 Sep 27 01:25 ./
drwxr-xr-x 8 root root 4096 Sep 27 01:26 ../
此时我们已经得到了ext4
根文件系统镜像debian_ext4_rootfs.img
:
root@zhengyang:/work/sambashare/rk3399/rootfs# ll debian_ext4_rootfs.img
-rw-r--r-- 1 root root 6442450944 Sep 27 01:29 debian_ext4_rootfs.img
删除文件夹debian_rootfs
:
root@zhengyang:/work/sambashare/rk3399/rootfs# rm -rf debian_rootfs
用e2fsck
修复及检测镜像文件系统,resize2fs
减小镜像文件的大小;
root@zhengyang:/work/sambashare/rk3399/rootfs# e2fsck -p -f debian_ext4_rootfs.img
root@zhengyang:/work/sambashare/rk3399/rootfs# resize2fs -M debian_ext4_rootfs.img
root@zhengyang:/work/sambashare/rk3399/rootfs# ll debian_ext4_rootfs.img
-rw-r--r-- 1 root root 4950212608 Sep 27 01:30 debian_ext4_rootfs.img
root@zhengyang:/work/sambashare/rk3399/rootfs# du -sh debian_ext4_rootfs.img
4.5G debian_ext4_rootfs.img
最终得到的ext4
根文件系统镜像debian_ext4_rootfs.img
大小为4.5G。
6.6.3 生成统一固件
通过上述的文件系统制作流程,我们得到了一个rootfs.img
镜像,该镜像可以直接通过rockchip
提供的工具进行烧录,如果需要制作一个完整的镜像包含uboot
、kernel
和rootfs
,需要将rootfs.img
放
/work/sambashare/rk3399/friendly/sd-fuse_rk3399/debian-bullseye-desktop-arm64/#
目录下,然后按照上面介绍的流程生成统一固件;
root@zhengyang:/work/sambashare/rk3399/friendly/sd-fuse_rk3399# ./mk-sd-image.sh debian-bullseye-desktop-arm64
我们自己制作的根文件系统大小可能超过了4G
,那么我们需要修改rootfs
和userdata
的大小和起始地址,例如,如果我们的根文件系统大小为4G
多,那么可以指定rootfs
大小为5G
,那么 parameter.txt
就应该如下所示:
FIRMWARE_VER:10.0
MACHINE_MODEL:RK3399
MACHINE_ID:007
MANUFACTURER: RK3399
MAGIC: 0x5041524B
ATAG: 0x00200800
MACHINE: 3399
CHECK_MASK: 0x80
PWR_HLD: 0,0,A,0,1
TYPE: GPT
CMDLINE: mtdparts=rk29xxnand:0x00002000@0x00004000(uboot),0x00002000@0x00006000(trust),0x00002000@0x00008000(misc),0x00002000@0x0000a000(dtbo),0x00008000@0x0000c000(resource),0x00014000@0x00014000(kernel),0x00018000@0x00028000(boot),0x00a00000@0x00040000(rootfs),-@0x00a40000(userdata:grow)
同步修改了rootfs
的大小和userdata
的起始地址,需要注意上面的地址是以扇区为单位的(每扇区大小512字节)。
参考文章
[1] friendlyarm
/sd-fuse_rk3399 Public
[2] 使用USB
线缆升级固件
[3] raw-gadget-USB Raw Gadget-Linux USB Gadget
子系统的底层接口
[4] xairy/raw-gadge
[5] USB Support
[6] 找到一篇关于Linux usb gadget
非常使用的说明和使用教程
[7] prometheus和grafana
以及node_exporter
安装配置
[8] Node_exporter+Prometheus+Grafana
快速实现Linux
系统性能数据提取、存储和可视化展示
[9] 如何在Ubuntu 20.04 LTS
上设置双重身份验证
[11 ] ubuntu install jsoncpp and demos,StyledWriter,FastWriter,Reader,parse
[12] libusb
实现读写USB
设备
[13] 如何在Ubuntu
上安装配置NFS