dakejahl opened a new pull request, #19936:
URL: https://github.com/apache/nuttx/pull/19936

   ## Summary
   
   The Synopsys DWC2-derived USB device drivers enable `USBSUSP` in `GINTMSK` 
but not `WKUP`, and every one of them ANDs `GINTSTS` with `GINTMSK` at the top 
of its ISR before dispatching. The `WKUP` branch and the `*_resumeinterrupt()` 
it calls are therefore dead code: `CLASS_SUSPEND` is delivered, `CLASS_RESUME` 
never is.
   
   For CDC/ACM the asymmetry is fatal rather than merely lossy. 
`cdcacm_suspend()` calls `uart_connected(&priv->serdev, false)`, and from then 
on `serial.c` refuses every board-side `open()` and `write()` with `-ENOTCONN`. 
The `cdcacm_resume()` that would call `uart_connected(true)` never runs, so the 
port stays unusable for the rest of the boot even though the device remains 
enumerated and the host has long since resumed it.
   
   Adding `WKUP` to the mask makes the existing handler reachable. The status 
bit is already acknowledged in the same read/ack cycle as the rest of the 
writable interrupts, so unmasking it cannot latch.
   
   The second commit adds `tools/usbdev_suspend_test.py`, the host-side script 
used to characterise this.
   
   ## Impact
   
   Affects any board whose USB device controller is one of these eight drivers 
and whose host suspends it. Linux hosts do that by default: with 
`power/control=auto` and the usual `autosuspend_delay_ms=2000`, closing the tty 
is enough. Software that opens, reads and closes the port — as opposed to 
holding it open — kills the link on the first close, which is why this reads as 
an intermittent wedge rather than a deterministic bug.
   
   No configuration, API or wire-format change. One extra interrupt source per 
suspend/resume, handled by code already present.
   
   Drivers touched:
   
   ```
   arch/arm/src/at32/at32_otgfsdev.c
   arch/arm/src/common/stm32/stm32_otgfsdev_m3m4_v1.c
   arch/arm/src/common/stm32/stm32_otghsdev_m3m4_v1.c
   arch/arm/src/efm32/efm32_usbdev.c
   arch/arm/src/stm32f7/stm32_otgdev.c
   arch/arm/src/stm32h7/stm32_otgdev.c
   arch/arm/src/stm32l4/stm32l4_otgfsdev.c
   arch/xtensa/src/esp32s3/esp32s3_otg_device.c
   ```
   
   ## Testing
   
   **Host:** Linux 7.0.0-28-generic x86_64, xhci_hcd, `usbcore.autosuspend=2`.
   **Board:** ARK FMU v6X (STM32H743), `CONFIG_STM32H7_OTGFS=y`, 
`CONFIG_CDCACM=y` (which `select`s `SERIAL_REMOVABLE`), NuttX 11.0.0 under PX4. 
Console on a separate STLINK-V3 VCP so it survives the CDC port dying.
   
   Only the STM32H7 path was exercised on hardware. The other seven drivers 
carry a line-for-line copy of the same `GINTMSK` initialisation and the same 
masked-off resume handler.
   
   Each cycle below forces a verified runtime suspend, resumes the device by 
opening the port, reads for 2 s, then asks the board over its console whether 
the CDC port is writable again.
   
   Before:
   
   ```
   $ ./tools/usbdev_suspend_test.py -d 
/dev/serial/by-id/usb-ARK_ARK_FMU_v6X.x_0-if00 \
         -n 5 --console /dev/ttyACM0 --console-baud 57600
   device      /dev/serial/by-id/usb-ARK_ARK_FMU_v6X.x_0-if00 -> /dev/ttyACM1
   usb         1-5.4  3185:0039  ARK FMU v6X.x
   power       control=on autosuspend_delay_ms=2000 (restored on exit)
   
   [1] suspended=True   read=12179   tail=0       board-side open failed: 
-ENOTCONN
   [2] suspended=True   read=0       tail=0       board-side open failed: 
-ENOTCONN
   [3] suspended=True   read=0       tail=0       board-side open failed: 
-ENOTCONN
   [4] suspended=True   read=0       tail=0       board-side open failed: 
-ENOTCONN
   [5] suspended=True   read=0       tail=0       board-side open failed: 
-ENOTCONN
   
   cycles with a verified suspend: 5/5
   of those, still streaming after resume: 0/5
   
   FAIL: the link died after suspend and did not come back.
   ```
   
   The 12179 bytes on the first cycle are the stale 
`CONFIG_CDCACM_TXBUFSIZE=12000` TX buffer flushing on resume, not a working 
link — hence `tail=0`, the bytes seen in the last second of the window.
   
   After:
   
   ```
   [1] suspended=True   read=40898   tail=23316   board-side open ok
   [2] suspended=True   read=42441   tail=22020   board-side open ok
   [3] suspended=True   read=36945   tail=20372   board-side open ok
   [4] suspended=True   read=44866   tail=22897   board-side open ok
   [5] suspended=True   read=37565   tail=20480   board-side open ok
   
   cycles with a verified suspend: 5/5
   of those, still streaming after resume: 5/5
   
   PASS: the link recovered from every suspend.
   ```
   
   Board side on the unpatched build, after a single host suspend, the port is 
gone for good:
   
   ```
   nsh> echo hi > /dev/ttyACM0
   nsh: echo: open failed: Transport endpoint is not connected
   ```
   
   Patched, the same command succeeds once the host resumes, and a 22 s suspend 
is followed by full recovery of the CDC stream (`tx 21946 B/s`, `txerr 0.0 
B/s`, 217 kB read by the host over the following 10 s).
   
   Build-tested beyond STM32H7: `cubepilot_cubeyellow` (stm32f7 OTGFS), 
`airmind_mindpx-v2` (common/stm32 OTGFS), `matek_gnss-m9n-f4` (common/stm32 
OTGHS).
   
   `./tools/checkpatch.sh -c -u -m -g master..HEAD` passes.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to