系统休眠唤醒时候,GSM模组USB重新枚举问题

问题描述:rk3588平台,每次休眠唤醒,GSM模块会断掉然后重新枚举:

image

解决方案如下:

--- a/kernel-6.1/drivers/usb/serial/option.c
+++ b/kernel-6.1/drivers/usb/serial/option.c
@@ -2483,6 +2483,9 @@ static struct usb_serial_driver option_1port_device = {
        .suspend           = usb_wwan_suspend,
        .resume            = usb_wwan_resume,
 #endif
+#if 1
+     .reset_resume      = usb_wwan_resume,
+#endif
 };

借助AI解决了该问题,分析如下:

完整机制解释
这一行 .reset_resume = usb_wwan_resume 的缺失,触发了三层连锁反应。
第一层:udriver->reset_resume 不会被注册
usb-serial.c 第 1504~1507 行:
/* we only set the reset_resume field if the serial_driver has one */
for (sd = serial_drivers; *sd; ++sd) {
    if ((*sd)->reset_resume) {                      /* ← option 的 reset_resume */
        udriver->reset_resume = usb_serial_reset_resume;  /* ← USB 设备驱动的 reset_resume */
        break;
    }
}

这里 (*sd) 是 option_1port_device(即 usb_serial_driver 结构体),udriver 是最终注册到 USB core 的 usb_driver 结构体。
A13:option_1port_device.reset_resume = usb_wwan_resume ≠ NULL → udriver->reset_resume = usb_serial_reset_resume
A14:option_1port_device.reset_resume = 不存在(NULL)→ udriver->reset_resume 保持 NULL
第二层:resume 时走的函数不一样
系统唤醒后,USB core 在 usb_resume_interface() 中判断:
/* 简化后的逻辑 */
if (reset_resume) {
    if (driver->reset_resume)          /* 检查 udriver->reset_resume */
        ret = driver->reset_resume(intf);    /* → usb_serial_reset_resume() */
    else if (driver->resume)
        ret = driver->resume(intf);          /* → usb_serial_resume() */
}

A13:走 reset_resume 分支 → 调用 usb_serial_reset_resume()
A14:走 resume 分支 → 调用 usb_serial_resume()
第三层(致命区别):URB 的 poison 状态没有清除
usb_reset_and_verify_device() 在做 USB 设备原地复位时,会调 usb_disable_interface() 来禁用接口,这个过程中 URB 被标记为 poisoned(中毒状态)。复位完成后,URB 必须先解除中毒才能重新提交。
现在看两个函数的具体区别:
usb_serial_reset_resume()(A13 走的路径):
static int usb_serial_reset_resume(struct usb_interface *intf)
{
    ...
    usb_serial_unpoison_port_urbs(serial);    /* ← 解除 URB 中毒! */
    
    if (serial->type->reset_resume)           /* = usb_wwan_resume */
        rv = serial->type->reset_resume(serial);
    else
        rv = serial->type->resume(serial);
    ...
}

usb_serial_resume()(A14 走的路径):
int usb_serial_resume(struct usb_interface *intf)
{
    ...
    /* ← 这里没有 usb_serial_unpoison_port_urbs()! */
    
    if (serial->type->resume)                 /* = usb_wwan_resume */
        rv = serial->type->resume(serial);
    ...
}

usb_serial_resume() 缺了 usb_serial_unpoison_port_urbs() 这一行。
最终效果对比

A13:  driver->reset_resume → usb_serial_reset_resume()
       → unpoison_port_urbs()           ← URB中毒解除
       → usb_wwan_resume()              ← URB重新提交成功 ✓
       → ttyUSB正常工作

A14:  driver->reset_resume = NULL → 走 driver->resume → usb_serial_resume()
       → (没有unpoison)                  ← URB仍处于中毒状态
       → usb_wwan_resume()              ← usb_submit_urb() 返回 -EPIPE ✗
       → ttyUSB死亡 → 应用层收发失败 → RIL检测到异常


usb_submit_urb() 在 URB 被 poisoned 时返回 -EPIPE 或 -ESHUTDOWN,URB 无法提交,串口端口实际上无法工作。这种情况下,上层 RIL 尝试读写 ttyUSB 失败,最终只能由系统触发断开重连。
所以这几行代码的完整依赖链是:

option.c                            usb-serial.c                      USB core
─────────────────────────────────────────────────────────────────────────────
option_1port_device                 usb_serial_register_driver()      usb_resume_interface()
   │                                     │                                │
   │ .reset_resume =                    udriver->reset_resume =          if (reset_resume && 
   │   usb_wwan_resume   ────[A13]──→   usb_serial_reset_resume   ───→   driver->reset_resume())
   │                                     │                                │
   │                                     │ usb_serial_reset_resume()      │
   │                                     │ ├ unpoison_port_urbs() ← 关键 │
   │                                     │ └ usb_wwan_resume()           │
   │                                     │    └ submit_urb() → OK ✓      │
   │                                     │                                │
   │ .reset_resume = NULL  ────[A14]──→  udriver->reset_resume = NULL    else if (driver->resume())
   │                                                                      │
   │                                                          driver->resume()
   │                                                          usb_serial_resume()
   │                                                           ├ (NO unpoison!) ← 致命
   │                                                           └ usb_wwan_resume()
   │                                                              └ submit_urb() → -EPIPE ✗

 

A13: 有 .reset_resume          A14: 无 .reset_resume
→ udriver->reset_resume 注册    → udriver->reset_resume = NULL
→ usb_serial_reset_resume()     → 走 driver->resume()
→ unpoison_port_urbs() ✓        → usb_serial_resume()
→ usb_wwan_resume() OK          → 缺 unpoison,URB 中毒未解除
→ ttyUSB 正常恢复                → usb_submit_urb() 返回 -EPIPE
                                  → 串口死掉,RIL 异常→触发重枚举

 

Linux系统还需要多改一个地方:

 kernel defconfig 里没配 CONFIG_USB_DEFAULT_PERSIST=y,导致所有 USB 设备在 root hub 掉电后没有 persist 保护,走 disconnect + 完整重枚举路径。

该配置Android系统默认是打开的。

 

一句话总结:.reset_resume = usb_wwan_resume 缺了,usb_serial_reset_resume() 就不会被注册到 USB core,USB core 改走 usb_serial_resume(),而后者没有调用 unpoison_port_urbs() 清除 URB 中毒状态,导致 usb_wwan_resume() 提交 URB 失败,串口没法用,最终设备只能断开重枚举。

posted @ 2026-07-30 20:42  M-kobe  阅读(11)  评论(0)    收藏  举报