改动:内核 —— drivers/usb/gadget/udc/core.c
usb_gadget_remove_driver() 中:
把 udc->driver->unbind() 提前到 usb_gadget_disconnect() 之前(原顺序互换):
- 原版(先停控制器,再解绑 —— 主机活跃传输时挂死)
usb_gadget_disconnect(udc->gadget); // → pullup(0) → soft_disconnect → RUN_STOP=0
usb_gadget_disable_async_callbacks(udc);
if (udc->gadget->irq) synchronize_irq(udc->gadget->irq);
udc->driver->unbind(udc->gadget); // → composite_unbind → fsg_unbind
usb_gadget_udc_stop(udc);
- 修复(先解绑让传输停止,再停控制器)
udc->driver->unbind(udc->gadget); // → fsg_unbind:等 fsg 线程完成当前命令、空闲端点
usb_gadget_disconnect(udc->gadget); // → RUN_STOP=0(控制器已完全空闲 → 安全)
usb_gadget_disable_async_callbacks(udc);
if (udc->gadget->irq) synchronize_irq(udc->gadget->irq);
usb_gadget_udc_stop(udc);
原理:
-
configfs 解绑走 configfs_composite_unbind → purge_configs_funcs → fsg_unbind,
-
而 fsg_unbind 里已有 wait_event(common->fsg_wait, common->fsg != fsg)——它发 CONFIG_CHANGE 异常并等待 fsg 线程。
-
fsg 线程在主循环的命令边界处理异常(do_set_interface(NULL)):先自然完成当前 SCSI 命令(传输正常结束)→ 在无活跃传输时禁用端点 → 置 fsg=NULL 唤醒。等这一步完成,控制器已完全空闲,随后的 RUN_STOP=0 就不会挂死。全程没有任何寄存器操作发生在活跃传输上。