From ce0038f2bc3ef2ba336ab132fb4a78ce59c15b6f Mon Sep 17 00:00:00 2001 From: Jacob Dahl Date: Sat, 22 Aug 2026 10:48:54 -0600 Subject: [PATCH] {arm,xtensa}/otg: unmask WKUP so USB resume reaches the class driver. Every DWC2-derived USB device driver enables USBSUSP in GINTMSK but not WKUP, and every one of them ANDs GINTSTS with GINTMSK before dispatch. The resume handler is therefore unreachable: CLASS_SUSPEND is delivered on suspend, CLASS_RESUME never is. For CDC/ACM that is fatal. cdcacm_suspend() calls uart_connected(false), after which serial.c refuses every open() and write() with -ENOTCONN, and the cdcacm_resume() that would clear it never runs. On a Linux host with the default USB autosuspend (power/control=auto, 2000 ms) simply closing the tty is enough to trip it, and the port stays dead for the rest of the boot while the device remains enumerated. Verified on STM32H7 (ARK FMU v6X): before, one host suspend leaves the CDC/ACM port permanently -ENOTCONN; after, ten forced suspend/resume cycles all recover with the MAVLink stream intact. The remaining drivers carry a line-for-line copy of the same initialisation. Assisted-by: Claude:claude-fable-5 Signed-off-by: Jacob Dahl --- arch/arm/src/at32/at32_otgfsdev.c | 5 +++-- arch/arm/src/common/stm32/stm32_otgfsdev_m3m4_v1.c | 5 +++-- arch/arm/src/common/stm32/stm32_otghsdev_m3m4_v1.c | 5 +++-- arch/arm/src/efm32/efm32_usbdev.c | 5 +++-- arch/arm/src/stm32f7/stm32_otgdev.c | 5 +++-- arch/arm/src/stm32h7/stm32_otgdev.c | 7 ++++--- arch/arm/src/stm32l4/stm32l4_otgfsdev.c | 5 +++-- arch/xtensa/src/esp32s3/esp32s3_otg_device.c | 5 +++-- 8 files changed, 25 insertions(+), 17 deletions(-) diff --git a/arch/arm/src/at32/at32_otgfsdev.c b/arch/arm/src/at32/at32_otgfsdev.c index 613ed8b34f2..068124bdaad 100644 --- a/arch/arm/src/at32/at32_otgfsdev.c +++ b/arch/arm/src/at32/at32_otgfsdev.c @@ -5539,8 +5539,9 @@ static void at32_hwinitialize(struct at32_usbdev_s *priv) /* Enable the interrupts in the INTMSK */ - regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_ENUMDNE | - OTGFS_GINT_IEP | OTGFS_GINT_OEP | OTGFS_GINT_USBRST); + regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_WKUP | + OTGFS_GINT_ENUMDNE | OTGFS_GINT_IEP | OTGFS_GINT_OEP | + OTGFS_GINT_USBRST); #ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (OTGFS_GINT_IISOIXFR | OTGFS_GINT_IISOOXFR); diff --git a/arch/arm/src/common/stm32/stm32_otgfsdev_m3m4_v1.c b/arch/arm/src/common/stm32/stm32_otgfsdev_m3m4_v1.c index af02d027c98..bc75e664f4a 100644 --- a/arch/arm/src/common/stm32/stm32_otgfsdev_m3m4_v1.c +++ b/arch/arm/src/common/stm32/stm32_otgfsdev_m3m4_v1.c @@ -5533,8 +5533,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv) /* Enable the interrupts in the INTMSK */ - regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_ENUMDNE | - OTGFS_GINT_IEP | OTGFS_GINT_OEP | OTGFS_GINT_USBRST); + regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_WKUP | + OTGFS_GINT_ENUMDNE | OTGFS_GINT_IEP | OTGFS_GINT_OEP | + OTGFS_GINT_USBRST); #ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (OTGFS_GINT_IISOIXFR | OTGFS_GINT_IISOOXFR); diff --git a/arch/arm/src/common/stm32/stm32_otghsdev_m3m4_v1.c b/arch/arm/src/common/stm32/stm32_otghsdev_m3m4_v1.c index 4a0e61a2132..cec826ab19f 100644 --- a/arch/arm/src/common/stm32/stm32_otghsdev_m3m4_v1.c +++ b/arch/arm/src/common/stm32/stm32_otghsdev_m3m4_v1.c @@ -5426,8 +5426,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv) /* Enable the interrupts in the INTMSK */ - regval = (OTGHS_GINT_RXFLVL | OTGHS_GINT_USBSUSP | OTGHS_GINT_ENUMDNE | - OTGHS_GINT_IEP | OTGHS_GINT_OEP | OTGHS_GINT_USBRST); + regval = (OTGHS_GINT_RXFLVL | OTGHS_GINT_USBSUSP | OTGHS_GINT_WKUP | + OTGHS_GINT_ENUMDNE | OTGHS_GINT_IEP | OTGHS_GINT_OEP | + OTGHS_GINT_USBRST); #ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (OTGHS_GINT_IISOIXFR | OTGHS_GINT_IISOOXFR); diff --git a/arch/arm/src/efm32/efm32_usbdev.c b/arch/arm/src/efm32/efm32_usbdev.c index 8d0af3ebfff..c8545bd184e 100644 --- a/arch/arm/src/efm32/efm32_usbdev.c +++ b/arch/arm/src/efm32/efm32_usbdev.c @@ -5488,8 +5488,9 @@ static void efm32_hwinitialize(struct efm32_usbdev_s *priv) /* Enable the interrupts in the INTMSK */ regval = (USB_GINTMSK_RXFLVLMSK | USB_GINTMSK_USBSUSPMSK | - USB_GINTMSK_ENUMDONEMSK | USB_GINTMSK_IEPINTMSK | - USB_GINTMSK_OEPINTMSK | USB_GINTMSK_USBRSTMSK); + USB_GINTMSK_WKUPINTMSK | USB_GINTMSK_ENUMDONEMSK | + USB_GINTMSK_IEPINTMSK | USB_GINTMSK_OEPINTMSK | + USB_GINTMSK_USBRSTMSK); #ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (USB_GINTMSK_INCOMPISOINMSK | USB_GINTMSK_INCOMPLPMSK); diff --git a/arch/arm/src/stm32f7/stm32_otgdev.c b/arch/arm/src/stm32f7/stm32_otgdev.c index 96be757e9e3..fe2ce48008f 100644 --- a/arch/arm/src/stm32f7/stm32_otgdev.c +++ b/arch/arm/src/stm32f7/stm32_otgdev.c @@ -5666,8 +5666,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv) /* Enable the interrupts in the INTMSK */ - regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_ENUMDNE | - OTG_GINT_IEP | OTG_GINT_OEP | OTG_GINT_USBRST); + regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_WKUP | + OTG_GINT_ENUMDNE | OTG_GINT_IEP | OTG_GINT_OEP | + OTG_GINT_USBRST); # ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR); diff --git a/arch/arm/src/stm32h7/stm32_otgdev.c b/arch/arm/src/stm32h7/stm32_otgdev.c index d185b987a03..9a14ad87665 100644 --- a/arch/arm/src/stm32h7/stm32_otgdev.c +++ b/arch/arm/src/stm32h7/stm32_otgdev.c @@ -3362,7 +3362,7 @@ static inline void stm32_rxinterrupt(struct stm32_usbdev_s *priv) regval = stm32_getreg(STM32_OTG_DOEPCTL(0)); regval |= OTG_DOEPCTL0_CNAK; stm32_putreg(regval, STM32_OTG_DOEPCTL(0)); - } + } } break; @@ -5565,8 +5565,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv) /* Enable the interrupts in the INTMSK */ - regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_ENUMDNE | - OTG_GINT_IEP | OTG_GINT_OEP | OTG_GINT_USBRST); + regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_WKUP | + OTG_GINT_ENUMDNE | OTG_GINT_IEP | OTG_GINT_OEP | + OTG_GINT_USBRST); #ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR); diff --git a/arch/arm/src/stm32l4/stm32l4_otgfsdev.c b/arch/arm/src/stm32l4/stm32l4_otgfsdev.c index af2df0c32ad..0e84582f5a3 100644 --- a/arch/arm/src/stm32l4/stm32l4_otgfsdev.c +++ b/arch/arm/src/stm32l4/stm32l4_otgfsdev.c @@ -5594,8 +5594,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv) /* Enable the interrupts in the INTMSK */ - regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_ENUMDNE | - OTGFS_GINT_IEP | OTGFS_GINT_OEP | OTGFS_GINT_USBRST); + regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_WKUP | + OTGFS_GINT_ENUMDNE | OTGFS_GINT_IEP | OTGFS_GINT_OEP | + OTGFS_GINT_USBRST); #ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (OTGFS_GINT_IISOIXFR | OTGFS_GINT_IISOOXFR); diff --git a/arch/xtensa/src/esp32s3/esp32s3_otg_device.c b/arch/xtensa/src/esp32s3/esp32s3_otg_device.c index 6d9386652dd..ac4139c0149 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_otg_device.c +++ b/arch/xtensa/src/esp32s3/esp32s3_otg_device.c @@ -5540,8 +5540,9 @@ static void esp32s3_hwinitialize(struct esp32s3_usbdev_s *priv) /* Enable the interrupts in the INTMSK */ - regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_ENUMDNE | - OTG_GINT_IEP | OTG_GINT_OEP | OTG_GINT_USBRST); + regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_WKUP | + OTG_GINT_ENUMDNE | OTG_GINT_IEP | OTG_GINT_OEP | + OTG_GINT_USBRST); #ifdef CONFIG_USBDEV_ISOCHRONOUS regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR);