嵌入式Linux下无需重启切换g_printer与g_serial USB驱动的技术问询
嵌入式Linux USB Gadget模式切换问题
背景
我正在基于嵌入式Linux设备开发C语言项目,通过同一USB线缆使用两款USB Gadget驱动:
g_printer(Linux USB Printer Gadget Driver)g_serial(Linux Gadget Serial Driver)
模式切换流程
USB虚拟串口模式切换步骤
- 检查设备文件
/dev/g_printer0是否已打开,若是则关闭对应文件描述符。 - 执行
system("lsmod | grep g_printer > /dev/null");检查g_printer是否加载,若是则执行system("rmmod g_printer");卸载。 - 执行
system("lsmod | grep usb_f_printer > /dev/null");检查usb_f_printer是否加载,若是则执行system("rmmod usb_f_printer");卸载。 - 执行
system("modprobe g_serial");加载g_serial。 - 打开设备文件
/dev/ttyGS0。
USB打印机模式切换步骤
流程类似,先卸载g_serial相关模块,再加载g_printer,对应关闭/打开的设备文件为/dev/ttyGS0和/dev/g_printer0。
问题现象
从USB打印机模式切换到USB虚拟串口模式时,步骤3崩溃,日志报错:
rmmod: can't unload module 'usb_f_printer': Resource temporarily unavailable
系统启动后会自动加载g_printer,此时/dev/g_printer0未被打开,按上述步骤切换到串口模式可正常执行,无报错。目前临时方案是将模式选择保存到配置文件,重启系统后加载对应模式,但体验不佳。
限制条件:
- 这是设备上运行的唯一进程,无法杀死该进程
- 不能通过
exec启动其他进程处理USB通信,避免资源浪费
提问
- 是否存在无需重启Linux系统即可让用户切换这两种模式的方法?
- 是否意味着一旦进程打开
/dev/g_printer0设备文件,即便已关闭文件描述符,该驱动也无法卸载,只能重启系统或杀死进程?
解答
问题1:无需重启的切换方法
有几种可行的方案:
检查并释放驱动所有引用
- 除了关闭自身进程打开的
/dev/g_printer0文件描述符,可通过cat /sys/module/usb_f_printer/refcnt查看模块当前引用计数,若计数大于0,说明存在未释放的资源。 - 先关闭UDC设备:执行
echo 0 > /sys/class/udc/<udc-device>/state(替换<udc-device>为实际设备名,比如dwc2.0.auto),再卸载模块,之后重新启用UDC并加载新驱动。 - 替换
system调用为更可靠的内核接口操作:比如直接调用unload_module系统调用(需root权限),或使用libudev管理设备和模块,比解析lsmod输出更准确。
- 除了关闭自身进程打开的
使用USB复合Gadget框架
无需单独加载单一功能驱动,而是配置一个包含串口和打印机功能的复合Gadget,通过动态启用/禁用功能节点切换模式:- 初始化时创建USB Gadget配置,同时添加
acm(串口)和printer功能。 - 切换到串口模式:执行
echo 0 > /sys/kernel/config/usb_gadget/<gadget-name>/functions/printer.0/enable禁用打印机功能,再启用串口功能。 - 切换到打印机模式则反向操作。这种方式无需卸载模块,从根源避免引用计数问题。
- 初始化时创建USB Gadget配置,同时添加
修复驱动引用计数bug
若定制内核的usb_f_printer模块存在bug(比如关闭设备文件后未正确递减引用计数),可修改驱动代码,确保release函数中正确清理所有内核态资源并更新引用计数,重新编译模块后替换原文件。
问题2:关闭文件描述符后仍无法卸载的原因
不是必须重启或杀死进程,常见原因有:
- 驱动未正确释放内核资源:用户态文件描述符关闭后,驱动的
release函数可能未清理内核态资源(如USB端点、设备上下文、申请的内存等),导致模块引用计数仍大于0。 - UDC设备未解绑:USB设备控制器(UDC)仍绑定到打印机Gadget,会占用
usb_f_printer模块,需先解绑UDC再尝试卸载。 - 存在其他模块依赖或内核引用:比如其他内核模块依赖
usb_f_printer,或内核线程持有未关闭的文件句柄。可通过lsmod查看模块依赖关系,排查是否有其他模块在使用它。
若确认所有用户态和内核态资源都已释放,但引用计数仍不为0,大概率是驱动bug,此时才需要考虑重启或杀死进程,但优先尝试问题1中的方案规避。
内容的提问来源于stack exchange,提问作者laplacedoge
相关产品推荐
相关产品推荐

