改动:内核 —— 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 就不会挂死。全程没有任何寄存器操作发生在活跃传输上。