From 8096371e68914824df679bbaa6cc8c9dbbc4774c Mon Sep 17 00:00:00 2001 From: Jacob Dahl Date: Sat, 22 Aug 2026 10:48:54 -0600 Subject: [PATCH] fix(arch/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. 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 | 5 +++-- arch/arm/src/stm32l4/stm32l4_otgfsdev.c | 5 +++-- arch/xtensa/src/esp32s3/esp32s3_otg_device.c | 5 +++-- 8 files changed, 24 insertions(+), 16 deletions(-) diff --git a/arch/arm/src/at32/at32_otgfsdev.c b/arch/arm/src/at32/at32_otgfsdev.c index 613ed8b34f293..068124bdaad32 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 af02d027c98cd..bc75e664f4aaf 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 4a0e61a213236..cec826ab19fe7 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 8d0af3ebfff2f..c8545bd184eba 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 96be757e9e3f9..fe2ce48008f62 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 d185b987a03fc..c18a738c4ce77 100644 --- a/arch/arm/src/stm32h7/stm32_otgdev.c +++ b/arch/arm/src/stm32h7/stm32_otgdev.c @@ -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 af2df0c32ada4..0e84582f5a354 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 6d9386652dd6d..ac4139c01494f 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);