简介:npreal2 是 Moxa RealTTY 串口设备在 Linux 平台上的官方驱动源码,本资源面向需要在 Linux 服务器或工控环境中部署多串口卡、调试 RealTTY 设备的运维与嵌入式开发者。仓库按内核版本划分多个分支,4.x 内核对应 kernel_4.xx,5.x 内核对应 kernel_5.xx,补丁均取自 npreal2 驱动的 AUR 仓库,可解决新内核下编译失败、proc 接口变更等兼容性问题。压缩包共 35 个文件,约 132KB,以 C 源码与头文件为主,辅以 Makefile、patch 补丁、mod 模块描述及 mxinst、mxmknod 等安装脚本,另含 README、SUPPORT 等说明文档,覆盖驱动编译、设备节点创建与配置全流程。目前已有 310 人学习下载,适合希望快速完成驱动移植、对照补丁排查编译报错并理解 RealTTY 驱动结构的读者参考。
1. 串口设备接上 Linux 就跑不起来?先搞清 npreal2 到底管什么
很多做工业控制的开发者第一次把 Moxa 多串口卡插进 Linux 工控机,dmesg里能看到 PCI 设备,/dev/ttyS*却怎么都不够用,或者干脆一个都不冒出来。这时候翻驱动目录,大概率会撞上npreal2这个名字。它是 Moxa RealTTY 系列多串口卡的 Linux 内核驱动模块,负责把板卡上的物理串口映射成标准 tty 设备节点,让上层应用像操作普通/dev/tty*一样读写 RS-232/422/485 数据。没有它,系统只能识别到 PCI 设备本身,串口通道一个都不可用。这份资源就是该驱动的源码包,适合需要在 Linux 下驱动 Moxa 智能多串口卡、又不想依赖发行版自带老旧驱动的嵌入式与工控从业者。下面从编译、加载、设备映射到排错,按实际落地顺序拆一遍。
2. 编译前先对齐内核头文件与驱动版本:npreal2 的构建链路
2.1 驱动源码的目录结构与关键文件
拿到源码包后先别急着make,花两分钟看清目录结构能省掉后面一堆编译报错。典型布局是顶层一个Makefile,下面分driver/、include/、tools/几个子目录。driver/里是核心的.c和.h,npreal2.c通常是主模块入口,负责字符设备注册和 tty 操作集挂载;npreal2.h放 ioctl 命令码和板卡寄存器偏移定义;tools/下一般有msdiag之类的诊断程序,用来在驱动加载后查询板卡状态和端口映射。Makefile里最关键的是KERNELDIR变量,它指向当前运行内核的构建目录,编译时所有内核头文件都从这里取。
先确认三件事:uname -r拿到内核版本,ls /lib/modules/$(uname -r)/build确认构建目录存在,gcc --version确认编译器可用。如果/lib/modules/$(uname -r)/build是个断链,说明内核头文件没装或版本不匹配,后面make必然报linux/module.h: No such file or directory。常见做法是先装对应内核的linux-headers包,再继续。
# 确认内核版本与构建目录 uname -r ls -l /lib/modules/$(uname -r)/build # 如果 build 目录缺失,安装对应内核头文件(以 Debian 系为例) sudo apt-get install linux-headers-$(uname -r)上面命令的逻辑很直接:uname -r输出当前运行内核的精确版本号,ls -l检查该版本对应的构建目录是否存在且指向有效路径。参数上唯一要改的是包管理器命令,Red Hat 系换成yum install kernel-devel-$(uname -r)。注意内核头文件版本必须和uname -r完全一致,差一个小版本号都会导致模块 vermagic 不匹配,加载时报invalid module format。
2.2 编译参数与模块输出
进入源码根目录后,make默认会调用内核的 kbuild 体系编译出npreal2.ko。如果板卡型号有差异,Makefile里可能有BOARD_TYPE之类的宏定义需要按实际硬件改。编译过程会生成.o、.ko以及.mod.c等中间文件,最终产物是npreal2.ko,用modinfo可以查看它的 vermagic 和依赖。
# 编译驱动模块 cd npreal2_src make # 查看编译产物信息 modinfo npreal2.komake不带参数时走默认目标,通常就是编译模块。modinfo输出的vermagic字段必须和uname -r一致,depends字段显示它依赖哪些其他模块。如果vermagic后面带了SMP或PREEMPT标记,而你的内核配置不同,加载会失败。这一步的坑在于:有些源码包的Makefile默认KERNELDIR写死成某个路径,需要手动改成/lib/modules/$(uname -r)/build,否则编译时找的是编译者机器上的内核,产物根本没法在你机器上加载。
2.3 加载模块与设备节点生成
编译通过后,insmod或modprobe加载模块。insmod需要指定.ko完整路径,modprobe则依赖模块安装到/lib/modules/$(uname -r)/extra/并执行depmod。加载成功后dmesg里会打印板卡检测信息和分配的 tty 设备号范围。
# 加载模块 sudo insmod npreal2.ko # 查看内核日志确认板卡识别情况 dmesg | tail -30 # 查看生成的 tty 设备节点 ls -l /dev/ttyr*insmod直接加载当前目录的模块文件,适合调试阶段。dmesg | tail -30看最近的内核消息,正常会显示检测到几块板卡、每块几个端口、映射到哪些ttyr设备。ls /dev/ttyr*确认设备节点是否自动创建。如果dmesg显示板卡识别成功但/dev下没有节点,通常是 udev 规则没触发或驱动版本不生成节点,需要手动mknod或检查 udev 规则文件。这一步的常见翻车点是:模块加载顺序不对,先加载了发行版自带的旧版驱动,新版insmod时报Device or resource busy,得先rmmod旧模块再加载。
3. 设备映射与数据读写:从 ttyr 节点到应用层
3.1 ttyr 设备命名规则与端口对应关系
npreal2 驱动加载后,设备节点通常以ttyr开头,后面跟数字,比如/dev/ttyr00、/dev/ttyr01。命名规则一般是板卡序号加端口序号,第一块板卡的第一个端口是ttyr00,第二个端口ttyr01,以此类推。多块板卡时第二块从ttyr16或ttyr32开始,具体偏移取决于驱动版本和板卡端口数。要确认对应关系,最可靠的是看dmesg输出,它会明确打印每个物理端口映射到哪个设备节点。
# 查看驱动打印的端口映射详情 dmesg | grep -i npreal # 用诊断工具查询板卡与端口状态 sudo ./tools/msdiag -admesg | grep -i npreal过滤出驱动相关日志,里面会逐条列出端口号和设备节点的对应。msdiag -a是驱动自带诊断工具,-a参数表示显示所有板卡和端口信息,输出比dmesg更结构化。如果msdiag报找不到设备,说明驱动没加载成功或板卡没被识别。这一步的坑在于:不同批次的板卡固件版本可能导致端口编号顺序和丝印不一致,不能只看板卡上的标签,必须以dmesg和msdiag输出为准。
3.2 用 stty 配置串口参数
设备节点有了,接下来要配置波特率、数据位、停止位、校验位。Linux 下标准工具是stty,直接对/dev/ttyr00操作。工业场景常用 9600 或 115200 波特率,8 数据位,1 停止位,无校验。
# 配置 ttyr00 为 115200 8N1,关闭流控 sudo stty -F /dev/ttyr00 115200 cs8 -cstopb -parenb -crtscts # 查看当前配置 sudo stty -F /dev/ttyr00 -astty -F指定设备文件,115200是波特率,cs8表示 8 数据位,-cstopb表示 1 位停止位,-parenb表示无校验,-crtscts关闭硬件流控。stty -a回读当前配置确认生效。参数怎么改:波特率直接替换数字,7 数据位用cs7,偶校验用parenb -parodd,奇校验用parenb parodd。注意stty配置在设备关闭后可能丢失,应用打开设备时需要重新配置,或者用open时的 termios 结构体在代码里设好。
3.3 应用层读写与 RS-485 方向控制
标准读写用read()/write()即可,但 RS-485 半双工场景需要控制收发方向。npreal2 驱动通常通过 ioctl 提供方向切换接口,具体命令码在npreal2.h里定义。常见做法是发送前切到发送模式,发送完切回接收模式。
#include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include "npreal2.h" int fd = open("/dev/ttyr00", O_RDWR | O_NOCTTY); if (fd < 0) { /* 错误处理 */ } /* 切换到 RS-485 发送模式,具体 ioctl 命令码以头文件为准 */ ioctl(fd, NPREAL_SET_RS485_TX); write(fd, buf, len); /* 等待发送完成,切回接收模式 */ tcdrain(fd); ioctl(fd, NPREAL_SET_RS485_RX); close(fd);open用O_RDWR | O_NOCTTY打开设备,O_NOCTTY防止该设备成为控制终端。ioctl命令码NPREAL_SET_RS485_TX和NPREAL_SET_RS485_RX是示例名,实际以源码npreal2.h里的定义为准。tcdrain确保发送缓冲区数据全部发出后再切方向,否则最后几个字节可能被截断。这一步的血泪经验是:方向切换后不加延时或tcdrain,接收端收到的数据末尾经常少一两个字节,排查半天以为是波特率问题,其实是方向切早了。
4. 避坑与排查:npreal2 加载失败的六种典型情况
4.1 模块加载报 invalid module format
现象:insmod npreal2.ko返回invalid module format,dmesg显示 vermagic 不匹配。原因:编译时用的内核头文件版本和当前运行内核不一致,或者内核配置的 SMP/PREEMPT 选项不同。解决:用uname -r确认运行内核,确保/lib/modules/$(uname -r)/build指向正确版本,重新make clean && make。如果还是不行,modinfo npreal2.ko | grep vermagic对比uname -r,差异一目了然。
4.2 设备节点不生成
现象:模块加载成功,dmesg显示板卡识别,但/dev/ttyr*不存在。原因:udev 规则未安装或未触发,或者驱动版本不自动创建设备节点。解决:先ls /dev/ttyr*确认,没有的话手动mknod /dev/ttyr00 c 主设备号 0,主设备号从dmesg或/proc/devices里查。更稳妥的做法是把源码包tools/下的 udev 规则文件复制到/etc/udev/rules.d/再udevadm trigger。
4.3 打开设备报 Permission denied
现象:应用open("/dev/ttyr00")返回Permission denied。原因:设备节点默认权限是root:root 600,普通用户无权访问。解决:用chmod 666 /dev/ttyr00临时放开,或在 udev 规则里加MODE="0666"持久化。生产环境更推荐把用户加入dialout组,然后chown root:dialout /dev/ttyr00,权限设660。
4.4 数据收发乱码或丢包
现象:能打开设备,但收到的数据乱码或间歇性丢字节。原因:波特率不匹配、流控配置冲突、RS-485 方向切换时机不对。解决:先用stty -a确认两端参数完全一致,再检查是否一端开了硬件流控另一端没开。RS-485 场景重点查方向切换代码,确保tcdrain后再切接收。如果用的是中断模式,还要确认驱动是否支持该板卡的中断共享。
4.5 多块板卡端口号冲突
现象:插两块板卡,第二块的端口节点和第一块重叠或缺失。原因:驱动加载时板卡枚举顺序不确定,或者Makefile里最大板卡数宏定义太小。解决:查npreal2.h里MAX_BOARDS之类的宏,确认值大于实际板卡数。加载时可以用insmod npreal2.ko board_type=xxx指定板卡类型,避免自动探测出错。端口号冲突时以dmesg打印的映射为准,不要依赖物理插槽顺序。
4.6 卸载模块报 Device or resource busy
现象:rmmod npreal2返回Device or resource busy。原因:还有进程持有/dev/ttyr*设备节点未关闭。解决:lsof /dev/ttyr*找出占用进程,先停掉再卸载。如果找不到进程,可能是内核某个子系统还引用着模块,lsmod | grep npreal看引用计数,计数不为零就说明有依赖。实在不行重启,但重启前先确认没有关键业务在跑。
5. 进阶:用 udev 规则固化设备名与权限
设备节点每次加载顺序可能变,ttyr00这次对应端口 1,下次可能对应端口 2,应用层写死设备名就会翻车。稳妥做法是用 udev 规则根据板卡物理路径或序列号绑定固定名称。先拿到设备的KERNELS和ATTRS信息,再写规则。
# 查看 ttyr00 的 udev 属性 udevadm info -a -n /dev/ttyr00 | grep -E "KERNELS|ATTRS|SUBSYSTEM"输出里找到板卡对应的KERNELS值(通常是 PCI 地址)和ATTRS{vendor}等属性,然后写规则文件。
# /etc/udev/rules.d/99-npreal.rules # 根据 PCI 地址绑定固定名称和权限 KERNELS=="0000:03:00.0", SUBSYSTEM=="tty", ATTRS{vendor}=="0x1393", MODE="0666", SYMLINK+="ttyPort01"KERNELS匹配内核设备路径,SUBSYSTEM=="tty"限定 tty 子系统,ATTRS{vendor}是板卡厂商 ID,MODE设权限,SYMLINK创建固定软链接。这样应用层统一用/dev/ttyPort01,不管底层ttyr编号怎么变。写完规则后udevadm control --reload-rules && udevadm trigger生效。验证方法:拔插板卡或重新加载驱动,ls -l /dev/ttyPort01始终指向正确设备。
从那以后我每次部署 npreal2 都强制走一遍:先uname -r对内核头文件,再modinfo验 vermagic,加载后dmesg确认端口映射,最后 udev 规则固化设备名。这套流程走完,现场调试基本不会在驱动层卡住。希望帮到你。
本文还有配套的精品资源,点击获取