CH340系列Linux驱动编译与内核适配实战
1. CH340驱动在Linux环境下的特殊挑战第一次在Ubuntu 22.04上折腾CH340驱动时我遇到了一个让人哭笑不得的情况——官方提供的驱动源码居然编译不过这让我意识到随着Linux内核版本的快速迭代很多老牌硬件设备的驱动都需要手动适配。CH340作为国产USB转串口芯片中的性价比之王在嵌入式开发和硬件调试领域应用广泛但它的Linux驱动维护却明显跟不上内核更新的步伐。我后来发现这个问题主要源于Linux内核从5.0版本开始引入的一系列API变更。比如原先使用的wait_queue_t类型被重新定义串口子系统中的write_room等回调函数的返回值类型也发生了变化。更麻烦的是不同发行版的内核补丁策略各不相同这就导致在Ubuntu、Debian、CentOS等系统上遇到的编译错误可能完全不同。有次我在一台Fedora 36的机器上还遇到了usb_serial_port结构体成员访问权限的问题。2. 驱动源码获取与预处理2.1 官方源码的正确打开方式南京沁恒官网提供的CH340驱动包CH341SER_LINUX.ZIP通常包含以下几个关键文件ch34x.c核心驱动源码Makefile编译脚本readme.txt基础说明但要注意这个驱动包的最新版本还是2018年发布的直接用在现代Linux系统上肯定会出问题。我建议下载后先做三件事解压到~/driver_ch340这样的纯英文路径备份原始ch34x.c文件查看当前内核版本uname -r这里有个小技巧如果官网下载速度慢可以尝试在终端用wget直接获取wget https://www.wch.cn/downloads/CH341SER_LINUX_ZIP.html -O ch340_driver.zip2.2 内核头文件的秘密编译驱动前必须确保安装了对应版本的内核头文件。在Ubuntu系系统上可以这样操作sudo apt update sudo apt install linux-headers-$(uname -r) build-essential有一次我在树莓派上编译时发现默认源里的头文件版本和实际运行的内核不一致这时候就需要手动指定sudo apt install linux-headers-5.15.0-1035-raspi3. 源码修改的实战技巧3.1 必须做的五项修改根据我的踩坑经验现代内核5.15环境下必须修改以下内容头文件补充在ch34x.c开头添加#include linux/sched/signal.h等待队列修复注释掉老式声明// wait_queue_t wait;返回值类型修正这两个函数要改为unsigned intstatic unsigned int ch34x_write_room(struct tty_struct *tty) static unsigned int ch34x_chars_in_buffer(struct tty_struct *tty)兼容性处理对于5.17内核需要处理tty_port结构体的变化#if LINUX_VERSION_CODE KERNEL_VERSION(5,17,0) tty_port_tty_set(port-port, tty); #else port-port.tty tty; #endifDMA相关警告在6.0内核需要添加#define DMA_BIT_MASK(n) (((n) 64) ? ~0ULL : ((1ULL(n))-1))3.2 常见编译错误解决方案遇到implicit declaration错误时通常是因为内核移除了某些API。比如最近遇到的一个典型错误error: implicit declaration of function signal_pending解决方法是在文件开头添加#include linux/sched/signal.h如果看到关于wait_queue_t的错误说明你的内核版本较新需要将相关代码改为wait_queue_entry_t wait;4. 编译与安装的完整流程4.1 编译的玄学问题执行make时可能会遇到各种神奇问题。这里分享几个实用参数make -j$(nproc) KERNELDIR/lib/modules/$(uname -r)/build如果遇到BTF相关警告可以暂时禁用make CONFIG_DEBUG_INFO_BTFn我在一台NVIDIA Jetson设备上编译时发现需要额外指定架构make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu-4.2 安装的正确姿势常规安装命令大家都知道sudo make install sudo depmod -a但有几个细节需要注意驱动文件应该放在/lib/modules/$(uname -r)/kernel/drivers/usb/serial/安装后要执行modprobe加载模块检查dmesg看是否有加载错误我习惯用这个组合命令sudo cp ch34x.ko /lib/modules/$(uname -r)/kernel/drivers/usb/serial/ \ sudo depmod -a \ sudo modprobe ch34x \ dmesg | tail -205. 验证与调试技巧5.1 设备识别检查插入CH340设备后应该依次检查lsusb | grep 1a86 dmesg | tail ls /dev/ttyUSB*如果看到类似这样的输出就成功了Bus 003 Device 004: ID 1a86:7523 QinHeng Electronics CH340 serial converter5.2 串口回环测试我推荐用picocom进行基础测试sudo apt install picocom picocom -b 115200 /dev/ttyUSB0更专业的测试可以用Python脚本import serial ser serial.Serial(/dev/ttyUSB0, 115200, timeout1) ser.write(bHello CH340\n) print(ser.readline())6. 内核升级后的应对策略每次系统内核升级后CH340驱动都需要重新编译。我开发了一个自动化脚本来自动处理#!/bin/bash cd ~/driver_ch340 make clean make sudo make install sudo modprobe -r ch34x sudo modprobe ch34x把这个脚本放在/etc/kernel/postinst.d/目录下就能在每次内核更新后自动执行。7. 深度定制与性能优化7.1 修改PID/VID如果需要支持自定义PID的设备可以修改ch34x.c中的这些行#define CH340_VID 0x1A86 #define CH340_PID 0x75237.2 提高传输性能在驱动源码中可以调整这些参数#define CH340_TX_BUFFER_SIZE 256 #define CH340_RX_BUFFER_SIZE 512但要注意太大的缓冲区可能导致延迟增加。7.3 解决RS485模式问题如果需要RS485支持要修改GPIO控制部分// 在ch34x_set_control函数中添加 if (control CH340_CTRL_RS485) gpio_set_value(gpio_pin, 1); else gpio_set_value(gpio_pin, 0);8. 疑难杂症解决方案遇到过最棘手的问题是驱动加载后设备不断断开重连。最终发现是电源管理导致的解决方法是在modprobe配置中添加options ch34x enable_pm0另一个常见问题是权限问题可以通过udev规则解决echo SUBSYSTEMtty, ATTRS{idVendor}1a86, MODE0666 | sudo tee /etc/udev/rules.d/99-ch340.rules对于Arch Linux用户AUR上有个维护更及时的驱动包yay -S ch340-dkms-git