背景
前一段时间定位一个上位机通过USB-TMC连接下位机(基于RK3588平台)时界面发生卡顿的问题,发现USB-TMC驱动代码是放在内核源码树里跟内核一起编译的,觉着这样既不便于更换TMC 驱动版本(每次修改代码都要重编内核),也不便于将TMC驱动代码单独放到SVN管理(RK的build系统有16GB之巨,全部提交到SVN服务器吃不消),于是考虑将其挪到源码树外。
想参照自己之前在zynq平台上开发的TMC驱动代码,结果发现也是放在内核源码树内的,尴尬,无奈从头摸索,几经周折,特别是踩了RK3588的systemd服务adbd的坑之后,总算实现目标了。
实现方法
创建独立工程
在SVN为产品开辟的目录下新建TMC目录并检出,并添加以下Makefile
obj-m += ftmc.o
SDK = ~/RK3588_LINUX_NNEWN_NH102_SDK_Release
CROSS_PREFIX = $(SDK)/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-rockchip1031-linux-gnu-
KDIR = $(SDK)/kernel
PWD ?= $(shell pwd)all:make -C $(KDIR) M=$(PWD) modules modules ARCH=arm64  CROSS_COMPILE=$(CROSS_PREFIX)
clean:rm -rf modules.order *.o *.mod Module.symvers *.mod.c *.ko *.cmd .*.cmd 
注:ftmc.o是源码ftmc.c编译出来的目标文件
在adbd服务启动脚本里加载ko
目前USB function驱动基本都切换到configfs开发方式了,因此RK3588为所有function驱动提供了一个adbd服务,用于在Linux启动阶段自动在configfs里创建一些目录和文件,从而触发驱动的alloc_inst、alloc_func、bind等方法。
以前所有function驱动都编译到内核,因此该脚本只操作configfs即可启用function,但因为tmc驱动变成了独立的ko,因此要先insmod一个ko,具体做法是在/etc/init.d/adbd.sh添加
    if [ $TMC_EN = on ];thenif [ ! -e "${USB_FUNCTIONS_DIR}/tmc.gs0" ] ;theninsmod /path/to/ftmc.ko  # 就添加这一行mkdir -p ${USB_FUNCTIONS_DIR}/tmc.gs0ln -s ${USB_FUNCTIONS_DIR}/tmc.gs0 ${USB_CONFIGS_DIR}/tmc.gs0fifi 
禁用adbd服务的ExecStop语句
RK3588的adbd服务是这样写的:
 
这个写法是有问题的,会导致ExecStop语句紧跟着ExecStart执行,而adbd.sh的stop分支是会删除gadget的:
stop)echo "none" > ${USB_CONFIGFS_DIR}/UDC  # 这行命令会删除整个gadget,包括依附其上的各种function,比如TMCif [ $ADB_EN = on ];thenstart-stop-daemon --stop --oknodo --pidfile /var/run/adbd.pid --retry 5fi;; 
因此不能这么写,我尝试过将服务的Type改成oneshot、simple等,也试过添加RemainAfterExit=yes、KillMode=none等语句,均无法达到RK3588想要的效果——系统启动时执行且只执行ExecStart,系统关闭时执行且只执行ExecStop——或许systemd可以增加一个新的Unit类型😉
不管怎么样,现在的解决办法是禁用adbd服务的ExecStop语句,反正嵌入式系统关机时就算设备没卸载也影响不大。
总结
configfs是个好东西,值得研究一番
