2024-10-07 06:21 AM - last edited on 2024-10-07 06:26 AM by Andrew Neil
I'm going to rip my hair out... I mean
There has been an issue with rapid disconnect and reconnect of the USB on our product, where the USB stops communicating. We enabled early suspend and when an erratic error (EERR) bit occurs during an early suspend. We plan on deinit and reinit the USB.
Our USB device is using a RNDIS (USB Ethernet) and CDC (Virtual Comm) interfaces. Using Device mode full speed (FS). We are not using the HAL library, We do use middleware from SEGGER emUSB.
To test that this is reliable, we added the deinit and reinit code to a one second timer on the device. It works fine until it doesn't. Looking at where it fails, The STM32 microcontroller thinks after onClassRequest, we have a get status request which was never sent from the computer, then the device stalls endpoint 0. Normally what i observed, instantly, the device would clear the stall on endpoint 0, but seems it never responds back. Then we would start the deinit and reinit process after and the peripheral never recovers. The device would never get up to setting the USB address phase. just keep the constant cycle of GetDescriptor request.
I tried everything besides ripping my hair out. If you have any ideas on what i can try or where to look, it would help.
This isnt the exact deinit, company would make me rip my hair out if i posted their code. This is just an example to show i tried flushing out the FIFO buffers and clearing interrupts to try and fix this issue.
void exampleDeinit(void) {
OTG_DOEPCTL0 &= ~(OTG_DxEPCTLx_STALL); // Ignore, bit can't be cleared, only set, core has to clear
OTG_DIEPCTL0 &= ~(OTG_DxEPCTLx_STALL);
// Deinitialize USB
OTG_GINTMSK = 0; // Disable USB Global Interrupt
OTG_GINTSTS = 0xFFFFFFFF; // Clear pending interrupts
OTG_PCGCCTL |= OTG_PCGCCTL_STOPCLK; // Disable PHY clock
OTG_GCCFG &= ~OTG_GCCFG_PWRDWN; // Power down the USB transceiver
// Disable all endpoints
OTG_DAINTMSK = 0;
OTG_DOEPMSK = 0;
OTG_DIEPMSK = 0;
// Flush transmit FIFO
OTG_GRSTCTL |= OTG_GRSTCTL_TX_FIFO_FLUSH;
while (OTG_GRSTCTL & OTG_GRSTCTL_TX_FIFO_FLUSH);
// Flush receive FIFO
OTG_GRSTCTL |= OTG_GRSTCTL_RX_FIFO_FLUSH;
while (OTG_GRSTCTL & OTG_GRSTCTL_RX_FIFO_FLUSH);
RCC_AHB2ENR &= ~(1 << 7); // OTGFSEN: Disable USB OTG FS clock enable
while (OTG_GRSTCTL & OTG_GRSTCTL_CSRST); // Wait for reset completion
}
USB analyzer screenshot:
USB STM32 print log:
635:301 Interrupt - EP0 Interrupt
635:301 Interrupt - _OnRx() Start
635:301 Interrupt - Enter HandleSetup: Setup 21 00 00 00 00 00 20 00
635:301 Interrupt - ENUM: OnClassRequest
635:301 Interrupt - Enter HandleSetup: Setup 00 00 28 01 00 02 25 01
635:301 Interrupt - ENUM: GetStatus
635:301 Interrupt - We got spagetti
635:301 Interrupt - _StallEP0: USB_Setup.c:802
635:301 Interrupt - _OnRx() End
635:303 Interrupt - _OnRx() Start
635:303 Interrupt - _OnRx() End
635:491 Comms - ONESECOND_DEINIT_REINIT
635:507 Interrupt - _OnRx() Start
635:507 Interrupt - _OnRx() End
635:507 Interrupt - USB__EarlySuspend()
635:511 Comms - Activate non-RNDIS interface
635:539 Comms - Config: Adding driver 003D82F0 to stack
635:539 Comms - CDC: Adding CDC interface with ControlInterface = 2, DataInterface = 3
635:539 Comms - USBD_Start
635:539 Comms - DWC2: Controller ID = 2000
635:569 Comms - EP 0 FIFO RAM = 64 bytes, DblBuff = 0
635:569 Comms - EP 1 FIFO RAM = 256 bytes, DblBuff = 1
635:569 Comms - EP 2 FIFO RAM = 64 bytes, DblBuff = 0
635:569 Comms - EP 3 FIFO RAM = 256 bytes, DblBuff = 1
635:569 Comms - EP 4 FIFO RAM = 64 bytes, DblBuff = 0
635:569 Comms - EP 5 FIFO RAM = 0 bytes, DblBuff = 0
635:569 Comms - USB DWC2: 192 bytes unused EP buffer RAM (USB_ENDPOINT_BUFFER_POOL_SIZE / USBD_AssignMemory())
635:575 Interrupt - _OnRx() Start
635:575 Interrupt - _OnRx() End
635:575 Interrupt - USB__EarlySuspend()
635:581 Interrupt - _OnRx() Start
635:581 Interrupt - _OnRx() End
635:581 Interrupt - USB__OnSuspend()
635:581 Interrupt - Activate non-RNDIS interface
637:490 Comms - ONESECOND_DEINIT_REINIT
637:507 Interrupt - _OnRx() Start
637:507 Interrupt - _OnRx() End
637:507 Interrupt - USB__EarlySuspend()
637:513 Comms - Activate non-RNDIS interface
637:533 Comms - Config: Adding driver 003D82F0 to stack
637:533 Comms - CDC: Adding CDC interface with ControlInterface = 2, DataInterface = 3
637:533 Comms - USBD_Start
637:533 Comms - DWC2: Controller ID = 2000
637:563 Comms - EP 0 FIFO RAM = 64 bytes, DblBuff = 0
637:563 Comms - EP 1 FIFO RAM = 256 bytes, DblBuff = 1
637:563 Comms - EP 2 FIFO RAM = 64 bytes, DblBuff = 0
637:563 Comms - EP 3 FIFO RAM = 256 bytes, DblBuff = 1
637:563 Comms - EP 4 FIFO RAM = 64 bytes, DblBuff = 0
637:563 Comms - EP 5 FIFO RAM = 0 bytes, DblBuff = 0
637:563 Comms - USB DWC2: 192 bytes unused EP buffer RAM (USB_ENDPOINT_BUFFER_POOL_SIZE / USBD_AssignMemory())
637:569 Interrupt - _OnRx() Start
637:569 Interrupt - _OnRx() End
637:569 Interrupt - USB__EarlySuspend()
637:575 Interrupt - _OnRx() Start
637:575 Interrupt - _OnRx() End
637:575 Interrupt - USB__OnSuspend()
637:575 Interrupt - Activate non-RNDIS interface
639:478 Comms - ONESECOND_DEINIT_REINIT
639:503 Interrupt - _OnRx() Start
639:503 Interrupt - _OnRx() End
639:503 Interrupt - USB__EarlySuspend()
639:507 Comms - Activate non-RNDIS interface
639:527 Comms - Config: Adding driver 003D82F0 to stack
639:527 Comms - CDC: Adding CDC interface with ControlInterface = 2, DataInterface = 3
639:527 Comms - USBD_Start
639:527 Comms - DWC2: Controller ID = 2000
639:561 Comms - EP 0 FIFO RAM = 64 bytes, DblBuff = 0
639:561 Comms - EP 1 FIFO RAM = 256 bytes, DblBuff = 1
639:561 Comms - EP 2 FIFO RAM = 64 bytes, DblBuff = 0
639:561 Comms - EP 3 FIFO RAM = 256 bytes, DblBuff = 1
639:561 Comms - EP 4 FIFO RAM = 64 bytes, DblBuff = 0
639:561 Comms - EP 5 FIFO RAM = 0 bytes, DblBuff = 0
639:561 Comms - USB DWC2: 192 bytes unused EP buffer RAM (USB_ENDPOINT_BUFFER_POOL_SIZE / USBD_AssignMemory())
639:568 Interrupt - _OnRx() Start
639:568 Interrupt - _OnRx() End
639:568 Interrupt - USB__EarlySuspend()
639:574 Interrupt - _OnRx() Start
639:574 Interrupt - _OnRx() End
639:574 Interrupt - USB__OnSuspend()
639:574 Interrupt - Activate non-RNDIS interface
641:491 Comms - ONESECOND_DEINIT_REINIT
641:507 Interrupt - _OnRx() Start
641:507 Interrupt - _OnRx() End
641:507 Interrupt - USB__EarlySuspend()
641:511 Comms - Activate non-RNDIS interface
641:531 Comms - Config: Adding driver 003D82F0 to stack
641:531 Comms - CDC: Adding CDC interface with ControlInterface = 2, DataInterface = 3
641:531 Comms - USBD_Start
641:531 Comms - DWC2: Controller ID = 2000
641:561 Comms - EP 0 FIFO RAM = 64 bytes, DblBuff = 0
641:561 Comms - EP 1 FIFO RAM = 256 bytes, DblBuff = 1
641:561 Comms - EP 2 FIFO RAM = 64 bytes, DblBuff = 0
641:561 Comms - EP 3 FIFO RAM = 256 bytes, DblBuff = 1
641:561 Comms - EP 4 FIFO RAM = 64 bytes, DblBuff = 0
641:561 Comms - EP 5 FIFO RAM = 0 bytes, DblBuff = 0
641:561 Comms - USB DWC2: 192 bytes unused EP buffer RAM (USB_ENDPOINT_BUFFER_POOL_SIZE / USBD_AssignMemory())
641:567 Interrupt - _OnRx() Start
641:567 Interrupt - _OnRx() End
641:567 Interrupt - USB__EarlySuspend()
641:573 Interrupt - _OnRx() Start
641:573 Interrupt - _OnRx() End
641:573 Interrupt - USB__OnSuspend()
641:573 Interrupt - Activate non-RNDIS interface
643:494 Comms - ONESECOND_DEINIT_REINIT
643:510 Interrupt - _OnRx() Start
643:510 Interrupt - _OnRx() End
643:510 Interrupt - USB__EarlySuspend()
643:514 Comms - Activate non-RNDIS interface
643:539 Comms - Config: Adding driver 003D82F0 to stack
643:539 Comms - CDC: Adding CDC interface with ControlInterface = 2, DataInterface = 3
643:539 Comms - USBD_Start
643:539 Comms - DWC2: Controller ID = 2000
643:569 Comms - EP 0 FIFO RAM = 64 bytes, DblBuff = 0
643:569 Comms - EP 1 FIFO RAM = 256 bytes, DblBuff = 1
643:569 Comms - EP 2 FIFO RAM = 64 bytes, DblBuff = 0
643:569 Comms - EP 3 FIFO RAM = 256 bytes, DblBuff = 1
643:569 Comms - EP 4 FIFO RAM = 64 bytes, DblBuff = 0
643:569 Comms - EP 5 FIFO RAM = 0 bytes, DblBuff = 0
643:569 Comms - USB DWC2: 192 bytes unused EP buffer RAM (USB_ENDPOINT_BUFFER_POOL_SIZE / USBD_AssignMemory())
643:575 Interrupt - _OnRx() Start
643:575 Interrupt - _OnRx() End
643:575 Interrupt - USB__EarlySuspend()
643:581 Interrupt - _OnRx() Start
643:581 Interrupt - _OnRx() End
643:581 Interrupt - USB__OnSuspend()
643:581 Interrupt - Activate non-RNDIS interface
645:333 Interrupt - _OnRx() Start
645:333 Interrupt - _OnRx() End
645:333 Interrupt - _OnBusReset()
645:333 Interrupt - EP 0 FIFO RAM = 64 bytes, DblBuff = 0
645:333 Interrupt - EP 1 FIFO RAM = 256 bytes, DblBuff = 1
645:333 Interrupt - EP 2 FIFO RAM = 64 bytes, DblBuff = 0
645:333 Interrupt - EP 3 FIFO RAM = 256 bytes, DblBuff = 1
645:333 Interrupt - EP 4 FIFO RAM = 64 bytes, DblBuff = 0
645:333 Interrupt - EP 5 FIFO RAM = 0 bytes, DblBuff = 0
645:333 Interrupt - Activate non-RNDIS interface
Solved! Go to Solution.
2024-10-17 11:48 AM - edited 2024-10-17 11:51 AM
Figured it out, Hopefully no one else ever has this problem.
I don't think this is the correct solution to the problem, but a kludge.
Not the exact problem but similar to what remco2 was saying where I was receiving a packet size greater than the endpoint.
After constant cycling of the USB where i reinit and deinit. There was a small chance that garbage can appear in my FIFO. I would have an interrupt triggered and the packet status in OTG_GRXSTSP would tell me I have a [OUT data packet received] packet. The byte count would tell me like 104 bytes going to EP0, but the max size for this endpoint is 64 bytes. I simply checked the size for this case in my code. would reset endpoint 0 by doing the whole flush of the FIFO and resetting Data PID to DATA0 (Idk was written by synopsis, they don't explain anything but left this comment in their code Refer to Chapter 5.8.5 in the USB Serial Bus Specification, Rev.2.0 for reset endpoint). Then the crisis was averted.
There was a second part to this issue, sometimes i would receive the invalid packet when OTG_GRXSTSP for status would tell me i have a [SETUP data packet received] packet. The size was 8 bytes, so i couldn't rely on the method above. Instead i had a flag checking if [SETUP transaction completed] was received to reset this flag. If I received two [SETUP data packet received] packets in a row without a complete. I would reset EP0 like i did above and not process anything after that.
static volatile uint8_t issue = 0;
case OUT_DATA_STATUS:
//Num bytes from OTG_GRXSTSP register
if (numBytes > EP0_MAX_PACKET_SIZE) {
_ResetEP(0);
break;
}
// Process SETUP data packet received here
break;
case SETUP_DATA_STATUS_COMPLETE:
issue = 0;
break;
case OUT_DATA_STATUS_COMPLETE:
// Process endpoint on complete here
break;
case SETUP_DATA_STATUS:
if(issue)
{
_ResetEP(0);
issue = 0;
break;
}
else
{
issue = 1;
}
// Process SETUP data packet received here
break;
I know for a fact this is not the right way, but this should fix what ever temporarily.
2024-10-07 11:08 AM - edited 2024-10-07 11:08 AM
> The STM32 microcontroller thinks after onClassRequest, we have a get status request which was never sent from the computer, then the device stalls endpoint 0.
I'm not sure what this means, but I would perhaps try to concentrate on not "getting" requests which were not sent. The rest might be a consequence.
Also, there's IMO no need for "deinitialization" of the USB hardware ever, unless you want to go to some low-power mode. But you definitively need to unwind any changes in software, reset structs etc.
Also, instead of "deinitialization" of the hardware, you may perhaps try to summarily reset it using the respective reset bit in RCC. I've never tried this, though, and I can envisage there may be some devil in the details.
JW
2024-10-08 03:40 AM
Hi @APTchassak
> Our USB device is using a RNDIS (USB Ethernet) and CDC (Virtual Comm) interfaces.
Do you mean composite CDC RNDIS + CDC ACM (Virtual com port)?
You need to ensure USB registers and states are properly reset during the deinitialization sequence. This includes clearing any pending interrupts and ensuring that the USB transceiver is powered down correctly. Rapid disconnect and reconnect cycle might be causing race conditions where the USB controller or the host is not fully ready before the next operation starts. Adding delays or ensuring proper synchronization during the DeInit() and DeInit() process might help.
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2024-10-17 11:48 AM - edited 2024-10-17 11:51 AM
Figured it out, Hopefully no one else ever has this problem.
I don't think this is the correct solution to the problem, but a kludge.
Not the exact problem but similar to what remco2 was saying where I was receiving a packet size greater than the endpoint.
After constant cycling of the USB where i reinit and deinit. There was a small chance that garbage can appear in my FIFO. I would have an interrupt triggered and the packet status in OTG_GRXSTSP would tell me I have a [OUT data packet received] packet. The byte count would tell me like 104 bytes going to EP0, but the max size for this endpoint is 64 bytes. I simply checked the size for this case in my code. would reset endpoint 0 by doing the whole flush of the FIFO and resetting Data PID to DATA0 (Idk was written by synopsis, they don't explain anything but left this comment in their code Refer to Chapter 5.8.5 in the USB Serial Bus Specification, Rev.2.0 for reset endpoint). Then the crisis was averted.
There was a second part to this issue, sometimes i would receive the invalid packet when OTG_GRXSTSP for status would tell me i have a [SETUP data packet received] packet. The size was 8 bytes, so i couldn't rely on the method above. Instead i had a flag checking if [SETUP transaction completed] was received to reset this flag. If I received two [SETUP data packet received] packets in a row without a complete. I would reset EP0 like i did above and not process anything after that.
static volatile uint8_t issue = 0;
case OUT_DATA_STATUS:
//Num bytes from OTG_GRXSTSP register
if (numBytes > EP0_MAX_PACKET_SIZE) {
_ResetEP(0);
break;
}
// Process SETUP data packet received here
break;
case SETUP_DATA_STATUS_COMPLETE:
issue = 0;
break;
case OUT_DATA_STATUS_COMPLETE:
// Process endpoint on complete here
break;
case SETUP_DATA_STATUS:
if(issue)
{
_ResetEP(0);
issue = 0;
break;
}
else
{
issue = 1;
}
// Process SETUP data packet received here
break;
I know for a fact this is not the right way, but this should fix what ever temporarily.