☰
ARM64 Linux下PL2303驱动安装指南:从DKMS编译到ttyUSB节点排查
2026/10/14 11:23:06 网站建设 项目流程

简介:面向ARM64架构(如AGX Orin平台)Ubuntu22.04系统的PL2303 USB转串口驱动安装教程资源包,专门解决新系统下驱动兼容性差、编译环境配置繁琐等问题,适合嵌入式工程师与Linux运维人员参考。压缩包共111个文件,以30个C源码、28个头文件和27个Makefile为主体,同时提供可加载内核模块(ko)、符号表(symvers)、图片及命令脚本等辅助材料,整体仅671KB,内容紧凑且便于快速部署。资源聚焦PL2303G驱动v1.0.6,完整覆盖从环境准备、依赖安装、源码编译到内核模块加载与设备识别验证的流程;针对加载失败、权限不足、设备节点未生成等常见问题,也给出了基于dmesg日志的排查思路。配合minicom、screen等串口工具的配置说明,读者可在AGX Orin等ARM64设备上快速恢复PL2303串口通信能力。该教程已有1094人学习下载,对于在ARM64平台维护串口外设的开发者具有直接参考价值。

1. ARM64 架构下串口 PL2303 驱动:为什么“插上就有 ttyUSB”在 AGX Orin 上不成立

一根 USB 转串口线,插到 AGX Orin 的 Ubuntu 22.04 上,lsusb能看到067b:2303,但ls /dev/ttyUSB*就是什么都没有。这是我在 ARM64 平台上做串口调试时遇到的最典型翻车现场,而且十次里有七次不是硬件坏了。PL2303 驱动在 Linux 里不是一个“装上就有”的普通软件包,它由内核模块承载,在内核引导或者热插拔时被动态加载;而很多现成的驱动包、编译脚本都是面向 x86_64 架构的,到了 ARM64 上要么直接拒绝安装,要么装进去也跑不起来。AGX Orin 这类设备跑的是 Ubuntu 22.04,但内核通常是定制过的,硬件平台又不是常规 PC,所以驱动安装的路径完全是另一套。这篇笔记会带你走完从排查设备枚举、确认模块状态、准备内核头文件,到用 DKMS 编译安装 pl2303 驱动的全过程,并把识别不了芯片、端口漂移、权限不足这些常见坑一次说清。适合正在 ARM64 设备上做串口外设接入、机器人底盘通信或者工业设备调试的开发者。

2. PL2303 的驱动体系:先搞懂 usbserial、pl2303 和 ttyUSB 三者关系

2.1 Linux 里 PL2303 驱动不是“一个文件”,而是一条链路

很多教程把 PL2303 驱动简化成“下载一个 .ko 然后 modprobe”,但实际驱动加载是分层的。USB 转串口设备被主机识别后,先由 USB core 枚举,然后匹配到 USB 串口适配驱动层。在 Linux 内核里,usbserial是通用串口转换层,负责把 USB 端点和 tty 层对接;pl2303则是针对 PL2303 系列芯片的具体驱动,负责讲芯片的寄存器协议。设备最终注册成一个/dev/ttyUSB0节点,上层应用读写这个节点,和读写普通串口几乎没有区别。

内核自带的pl2303.ko一般位于/lib/modules/$(uname -r)/kernel/drivers/usb/serial/pl2303.ko,它依赖usbserial.ko。当你插入 PL2303 设备时,usb core 通过 VID/PID 匹配到pl2303驱动,驱动 probe 成功后再调用usb_serial_register注册 tty 设备。这个链路里任何一环断了,结果都是“lsusb 有设备,但 /dev/ttyUSB0 不存在”。

这里有个容易误解的地方:看到 NULL,谁都会怀疑“驱动没装”。但更常见的状况是驱动确实存在、也加载了,却在 probe 阶段主动拒绝设备。PL2303 芯片版本杂,老的 HXA、中间过渡型号和新款芯片的寄存器行为并不完全一致,内核驱动对某些克隆芯片或者过新版本会直接返回错误。所以排查时要同时看模块加载状态和内核日志,而不是只盯着lsmod的输出。

2.2 用三条命令快速判断设备状态

先把最基础的三条命令跑一遍,每一步输出的含义都值得看清楚。代码如下:

# 查看 USB 总线是否枚举到设备,记下 VID:PID lsusb # 查看内核打印的 USB 和串口相关日志,重点看最后 50 行 dmesg | tail -n 50 # 查看 pl2303 和 usbserial 模块是否已经加载 lsmod | grep -E "pl2303|usbserial"

lsusb的输出里如果没有067b:2303,说明 USB 枚举这关就没过,先不要急着弄驱动,优先换线、换 USB 口、重新插拔。如果能看到设备,继续看dmesg。正常加载会有类似pl2303 converter now attached to ttyUSB0的日志;如果出现device not supported或者failed to set vendor info,那就是驱动认出芯片但拒绝绑定。lsmod这步用来确认模块是否已经驻留在内核里,如果输出为空,说明模块压根没有被加载,需要手动modprobe pl2303或者排查模块加载路径。

三条命令跑完后,我一般会把结论套到下面这张表里,直接定位故障层面:

lsusb 结果dmesg 关键日志/dev/ttyUSB0故障层面
无设备无任何 USB 枚举信息无USB 链路层,换线换口
有设备枚举正常,无 pl2303 日志无驱动未加载或未匹配
有设备not supported / rejected无驱动与芯片版本不兼容
有设备attached to ttyUSB0有驱动正常,查上层配置

2.3 x86_64 的驱动包为什么在 ARM64 上不可靠

网上能搜到的 PL2303 驱动安装包,很大一部分是面向 x86_64 架构的.deb或者.rpm。这类包里的.ko文件是编译好的二进制,架构不匹配时modprobe会直接报invalid module format。ARM64 和 x86_64 的指令集完全不同,内核模块又没有跨架构运行的说法,所以“下载安装包双击安装”这条路在 AGX Orin 上基本走不通。

那能不能直接用内核自带的驱动?可以,但有限制。如果你用的 Ubuntu 22.04 内核版本比较新,自带的pl2303驱动对常见型号支持还算完整;而 AGX Orin 这类 ARM64 平台出厂内核可能修改过配置,可能没有把pl2303编进去,也可能编进去了但版本较老。最稳妥的做法是自己拿源码编译一份模块,并通过 DKMS 管理,让它在内核升级后还能自动重建。这个思路在嵌入式平台上是常规操作,也是后面几章要重点展开的内容。

3. 编译前置:给 AGX Orin 准备好内核头文件和 DKMS 环境

3.1 先确认当前内核版本和头文件是否匹配

编译内核模块最怕两件事:一是没有头文件,二是头文件和当前内核版本对不上。前者直接导致编译无法开始;后者更隐蔽,编译可能成功,但安装后modprobe依然报错。所以第一步是确认当前运行内核和头文件路径。

# 查看当前内核版本 uname -r # 查看对应的内核头文件目录是否存在 ls -ld /usr/src/linux-headers-$(uname -r) # 查看内核编译选项里是否已经包含 PL2303 相关配置 zcat /proc/config.gz 2>/dev/null | grep -i pl2303

uname -r输出的是正在运行的内核版本,比如5.10.104-tegra,带tegra后缀就说明是平台定制内核。/usr/src/linux-headers-xxx目录存在,说明头文件已经装好;如果不存在,需要补装。最后一条命令是为了确认内核配置里CONFIG_USB_SERIAL_PL2303是m还是n,m表示作为模块编译,n表示完全没有启用。如果zcat没有输出,说明/proc/config.gz不存在,可以跳过这步,用后面的modinfo判断。

3.2 安装编译工具链和 dkms

接下来安装编译必需的工具。build-essential 提供 gcc 和 make,dkms 负责管理内核模块的源码和自动重建,linux-headers 则是对应版本的编译依赖。命令如下:

# 更新软件源索引,然后安装编译工具和 dkms sudo apt update sudo apt install -y build-essential dkms # 尝试安装与当前内核版本一致的头文件 sudo apt install -y linux-headers-$(uname -r) # 如果 apt 提示没有可用软件包,就必须手动确认内核源码包的获取方式

需要注意,平台定制内核的头文件不一定能从 Ubuntu 软件源里装到。遇到Unable to locate package linux-headers-xxx的情况,说明这个内核不在 APT 仓库里,你需要去平台厂商提供的 Linux 内核源码包中找对应版本,解压后手动放到/usr/src下。判断头文件是否工作,可以检查目录里有没有Makefile:

# 检查内核头文件目录结构是否完整 ls /usr/src/linux-headers-$(uname -r)/Makefile

如果这个Makefile不存在,说明头文件不完整,直接做模块编译大概率会在Kbuild not found报错处停下来。这里有个细节:有些平台把源码放到/usr/src之后还需要执行make modules_prepare,但这一步需要完整的内核源码树,而不是压缩过的 headers-only 包。大多数情况下,模块编译用带Makefile的 headers 包就够了。

3.3 检查平台上是否已有出厂预置的 pl2303 模块

在动手编译前,先确认系统里到底有没有现成的pl2303.ko。很多 AGX Orin 的板级镜像其实默认就带了驱动,你遇到的只是权限或者设备兼容问题,不需要重复编译。

# 用 modinfo 查看模块信息,能查到就说明模块存在且可用 modinfo pl2303 2>/dev/null | head -n 10 # 直接按路径查找模块文件 find /lib/modules/$(uname -r) -name "pl2303*" -type f 2>/dev/null

如果modinfo pl2303能打印出 filename、license、depends 等信息,说明模块已经在模块目录里了,直接跳到第 5 章排查为什么没有 ttyUSB 节点。如果find命令没有输出,那才需要走第 4 章的 DKMS 编译流程。还有一个细节值得注意:即使模块文件存在,modprobe pl2303时也可能因为固件、依赖模块缺失而失败,所以后续加载时先看dmesg再下结论,不要凭感觉反复插拔设备。

4. 用 DKMS 在 ARM64 上编译安装 PL2303 驱动:完整命令与参数

4.1 准备驱动源码和 dkms.conf

拿到 PL2303 驱动源码压缩包后,先解压,再把目录放到/usr/src/下,并以“驱动名-版本号”命名。这一步是 dkms add 能找到源码的前提。同时需要在该目录里准备一份dkms.conf,告诉 dkms 模块名、版本、编译方式和安装位置。

# 假设源码包已经下载到当前目录,文件名为 pl2303-x.x.x.tar.gz # 版本号 x.x.x 按你实际拿到的源码版本替换 tar -xzf pl2303-x.x.x.tar.gz sudo mv pl2303-x.x.x /usr/src/ # 进入驱动源码目录,编写 dkms.conf cd /usr/src/pl2303-x.x.x sudo tee dkms.conf <<'EOF' PACKAGE_NAME="pl2303" PACKAGE_VERSION="x.x.x" BUILT_MODULE_NAME[0]="pl2303" DEST_MODULE_LOCATION[0]="/kernel/drivers/usb/serial/" MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules" CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean" AUTOINSTALL="yes" EOF

PACKAGE_NAME和PACKAGE_VERSION是 dkms 管理模块的唯一标识,dkms build和dkms install用这两个字段定位源码目录。BUILT_MODULE_NAME指定编译产物名称,DEST_MODULE_LOCATION是模块安装到内核模块树里的相对路径,因为 usb serial 驱动约定放在kernel/drivers/usb/serial/下。MAKE这一行是编译命令模板,${kernel_source_dir}会被 dkms 替换成当前内核源码目录,${dkms_tree}则指向 dkms 的工作目录。AUTOINSTALL="yes"表示允许 dkms 在内核更新时自动重建模块,建议开启,避免日后升级内核后驱动失效。

4.2 执行 dkms add、build、install

源码和配置准备好后,按顺序执行三步。add 只是把源码注册进 dkms 的数据库,build 才真正调用编译,install 则把编好的.ko复制到当前内核的模块目录。

# 把 /usr/src/pl2303-x.x.x 注册进 dkms sudo dkms add /usr/src/pl2303-x.x.x # 编译模块,-m 指定模块名,-v 指定版本号 sudo dkms build -m pl2303 -v x.x.x # 安装模块到当前内核模块目录 sudo dkms install -m pl2303 -v x.x.x # 查看 dkms 管理的模块状态,出现 "installed" 才说明安装成功 dkms status

执行dkms build时,如果看到类似Kernel preparation unnecessary的提示是正常的;如果出现make: Entering directory后半途报错,通常是头文件不完整。重点看报错信息里有没有linux/compiler.h、linux/module.h这类找不到头文件的提示,如果有,回到第 3 章把头文件重新装一遍。dkms install失败率很低,但偶尔会因为模块目录权限或签名校验报错,前者用sudo即可,后者意味着你的平台启用了一些模块签名机制,需要跳过签名检查或重新签名,这是另一个话题,常见于定制内核。

dkms status是最后的验证关口。我习惯在install之前先跑一次sudo dkms build -m pl2303 -v x.x.x --no-depmod,快速确认编译能通过,再执行 install。这样可以减少无效的模块安装次数。编译过程中如果你改了源码,必须重新跑 build,只改dkms.conf的话执行sudo dkms update -m pl2303 -v x.x.x更稳妥,它会刷新配置并重建状态。

4.3 加载模块并确认 ttyUSB0 出现

安装完成后,先卸载可能存在的旧模块,再加载新版模块。千万不要直接插设备,先看模块能否正常挂载。

# 卸载旧模块,避免版本冲突 sudo modprobe -r pl2303 2>/dev/null # 加载新模块 sudo modprobe pl2303 # 检查模块加载日志 dmesg | tail -n 20 # 查看是否生成串口节点 ls -l /dev/ttyUSB*

modprobe pl2303会自动处理usbserial依赖,前提是依赖模块在模块目录里。如果报Module pl2303 not found,先执行sudo depmod -a更新模块依赖关系。加载成功后,插入 PL2303 设备,dmesg末尾应该能看到pl2303 converter now attached to ttyUSB0。如果依然没有 ttyUSB0,不要重复加载模块,直接看第 5 章的兼容性排查。

为了开机自动加载,建议在/etc/modules-load.d/下新建一个配置,内容只有一行pl2303。另外,串口设备通常归dialout组管理,当前用户需要加入这个组才能免 sudo 访问,这一步容易忘,放在这里提醒:

# 把当前用户加入 dialout 组,重新登录后生效 sudo usermod -aG dialout "$USER" # 写入模块开机加载配置 echo "pl2303" | sudo tee /etc/modules-load.d/pl2303.conf

权限问题上,还可以用 udev 规则把设备节点软链成固定名字,比如/dev/pl2303,这在多串口设备混插时非常实用,后面避坑章节会专门讲端口漂移。

5. 驱动装完还是没有 ttyUSB0?常见问题与避坑清单

5.1 设备枚举正常但 dmesg 出现 not supported

现象:lsusb能看到067b:2303,但dmesg里出现类似device not supported的提示,/dev/ttyUSB0始终不出现。
原因:你手上的线用了兼容版或者克隆版 PL2303 芯片,内核驱动在 probe 阶段发现芯片回复的寄存器特征与正规型号不符,主动拒绝。这个问题在 ARM64 和 x86_64 上都会出现,但在嵌入式平台更容易被误判成“驱动版本不对”。
解决:先确认芯片来源,有条件就换一根用原装芯片的线,这是最省事的方法。如果暂时换不了,可以尝试加载usbserial通用驱动,让它不经过pl2303的校验机制,强制绑定:

# 卸载原驱动,改用通用 usbserial 绑定指定 VID/PID sudo modprobe -r pl2303 sudo modprobe usbserial vendor=0x067b product=0x2303

注意,通用绑定方式失去了 PL2303 驱动对芯片的特殊初始化,部分参数可能不稳定,但至少能创建ttyUSB0,用来临时调通业务问题不大。还有一种情况是dmesg里没有not supported,而是failed to set baudrate之类,那通常是芯片供电不足,换独立供电的 USB HUB 试试。

5.2 编译时报 Kbuild 相关错误

现象:执行sudo dkms build后输出含有Kbuild not found、linux/compiler.h not found或No rule to make target。
原因:内核头文件目录虽然存在,但没有完整的内核构建目录结构,通常是因为头文件不是从对应镜像源安装的,或者版本与uname -r不一致。
解决:重新确认/usr/src/linux-headers-$(uname -r)里有没有Makefile和include/generated目录。缺失的话,从平台内核源码包手动安装头文件,然后用sudo dkms remove -m pl2303 -v x.x.x --all清掉旧记录,重新 add 和 build。编译环境的问题不要靠反复改代码解决,先保证make -C /lib/modules/$(uname -r)/build能进入目录再说。

5.3 模块安装成功但 modprobe 找不到 .ko

现象:dkms status显示installed,但sudo modprobe pl2303报Module not found。
原因:dkms install把.ko放到了模块目录,但内核模块依赖索引还是旧的,或者.ko权限不对。还有一种情况是安装到了错误内核版本的目录下,尤其当你更新过内核后重启进新内核,旧模块自然不在当前内核里。
解决:先执行sudo depmod -a重新生成依赖索引,再试一次modprobe。仍然失败就找模块文件实际位置:

# 查找 pl2303.ko 实际放置位置 find /lib/modules/$(uname -r) -name "pl2303.ko"

确认文件存在后再查权限,.ko需要可读即可,不需要执行权限。如果文件确实不在当前内核目录,dkms status里又显示 installed,说明版本号或者内核版本匹配出了问题,移除后重新 install。

5.4 能打开 ttyUSB0 但提示权限不足

现象:ls /dev/ttyUSB0能看到设备,但运行串口工具时报Permission denied,或者用echo测试时无输出。
原因:当前用户不在dialout组,没有对串口设备的访问权限。这个问题在桌面版 Ubuntu 上常见,在 AGX Orin 这类嵌入式系统上也经常被忽略。
解决:执行sudo usermod -aG dialout "$USER",然后重新登录或重启,确认组生效后再试。如果你不想影响现有用户组,也可以临时用sudo运行串口工具,先验证硬件链路是否正常。另外某些系统里串口设备所属组是tty,可以用ls -l /dev/ttyUSB0看实际的组名,再决定加入哪个组。

5.5 多个 USB 转串口插上后端口漂移

现象:这次插上 PL2303 是ttyUSB0,重启后变成了ttyUSB2,程序里写死的设备路径失效。
原因:Linux 按 USB 枚举顺序分配 ttyUSB 编号,跟物理接入顺序强相关,换口、重启、插入其他 USB 设备都会改变编号。
解决:用 udev 规则固定设备节点。先拿到设备的序列号或者稳定的系统路径,再写规则:

# 查看设备路径和序列号 udevadm info -a -n /dev/ttyUSB0 | grep -E "ATTR{serial}|ATTRS{serial}"

拿到唯一的序列号后,创建/etc/udev/rules.d/99-pl2303.rules,内容按实际 serial 替换,比如SUBSYSTEM=="tty", ATTRS{serial}=="A104O3X2", SYMLINK+="ttyPL2303"。重载 udev 规则后,插上设备就会出现固定软链/dev/ttyPL2303,程序里用它来替代 ttyUSB 编号。要注意克隆芯片的序列号字段可能为空或者完全相同,这种情况只能按 USB 口的拓扑路径写规则,稳定性稍差。

6. 装完驱动之后:用回环测试验证串口是否真的能收发

驱动装上、/dev/ttyUSB0也出现了,但先别急着接外部设备,我强烈建议做一次回环测试:把串口模块的 TX 和 RX 短接,然后从系统里写数据,看能不能自己读回来。这一步能把“驱动问题”和“接线问题”快速隔离,也是我做串口调试的第一条习惯。

# loopback_test.py # 回环测试:TX 与 RX 短接后,写入一个字节,应读到同样的字节 import serial ser = serial.Serial( port="/dev/ttyUSB0", baudrate=115200, timeout=2, write_timeout=2 ) # 写入 5 个已知字节 ser.write(b"pl2303") # 从同一串口读回 data = ser.read(5) print("received:", data) # 校验读写一致则测试通过 if data == b"pl2303": print("loopback test PASS") else: print("loopback test FAIL") ser.close()

运行方式python3 loopback_test.py。能完整打印pl2303就说明驱动、设备节点、串口硬件全链路正常。如果读回来的数据是空,先确认短接是否可靠,别用杜邦线虚接;如果读到数据但内容对不上,检查波特率和校验位设置。

回环测试通过之后,我一般还会顺手验证一下两个参数:一是不同波特率下重跑一次,二是用stty -F /dev/ttyUSB0 9600切换速率后再试,这样可以暴露驱动对波特率配置的支持差异。部分兼容芯片在 921600 以上高速率下表现很不稳定,做高速通信前一定先测过,别等现场才踩坑。

我的建议是,在 AGX Orin 这类 ARM64 平台上报串口问题,不要直接把“没有 /dev/ttyUSB0”等价于“没有驱动”。我第一次调这类问题时就犯了同样的错,反复编译了五六遍驱动,最后才发现是克隆芯片被内核拒认,换了一根原装线立刻就好了。从那以后,我每拿到一块新板子,都会先花十分钟把枚举、模块、权限、回环测一遍,再去看业务代码。这套流程看起来简单,但能省下后面大半天的排查时间,希望能帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询