cancel
Showing results for 
Search instead for 
Did you mean: 

Issue with STM32 USB Transmit Interrupted by Receive

michaeljulius9
Associate II
Posted on August 11, 2015 at 07:05

The Scenario:

 

I have a custom board running an STM32F303VCTx connected via USB (Full Speed Device) to a Linux machine as a Custom HID peripheral.  A majority of the time the system works correctly sending data in both directions, but under some circumstances (details to follow), the system gets into a state where Linux’s libusb library begins to report timeouts (error code -7) for every write attempt.  Forcing a re-enumeration via Linux recovers the system.  I have tried this on an embedded Linux machine and a desktop Linux machine which is one of the reasons I suspect the STM32 side.

Source Code:

The STM32 driver libraries being used (including USB stack) are those provided by the STM32CubeMX4.9.0 application using the STM32Cube FW_F3 V1.2.0 firmware package.  All I have done is implement the custom HID callback functions and customise the descriptors.  I have 4 endpoints:

  HAL_PCDEx_PMAConfig(pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);

  HAL_PCDEx_PMAConfig(pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);

  HAL_PCDEx_PMAConfig(pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);

  HAL_PCDEx_PMAConfig(pdev->pData , 0x02 , PCD_SNG_BUF, 0x140);

Further Investigation:

I have been using LED toggling in the code and a scope to investigate where the system is when the issue occurs.  It appears that the scenario is that a call to HAL_PCD_EP_Transmit() (to send some data to the host), is being interrupted by an incoming packet (from the host)  (i.e. PCD_EP_ISR_Handler() enters the “if ((wEPVal & USB_EP_CTR_RX) != 0)�? condition).

My Best Guess:

The incoming data is received on endpoint 0x02, and the outgoing data is sent on endpoint 0x81, so I don’t see any reason why the one should impact the other. My best guess is that the incoming frame is corrupting the out-going frame, and so the frame that linux receives, triggers the Linux driver to block the device assuming it has malfunctioned.  A re-enumeration resets the linux driver.  This suggested the USB shared memory functions (PCD_WritePMA() and PCD_ReadPMA()) were trampling each other, but from what I can tell, the offsets and lengths of each are correct so they don’t overlap.  I even tried moving the offsets around and increasing the lengths, but that made no difference.

Help:

Can anyone propose a solution and/or further investigations, and/or point me to somewhere that might be able to shed some light on this issue?
7 REPLIES 7
michaeljulius9
Associate II
Posted on September 17, 2015 at 06:48

The same issue occur on an STM32F7

I think I’ve found the problem, and it appears to be a bug/limitation with the USB stack provided by STM.

The Scenario

This uses the example code auto generated for STM32CubeMx for a STM32F7 with a custom HID device:

When the application wants to send a USB message it calls USBD_CUSTOM_HID_SendReport() which calls USBD_LL_Transmit() then HAL_PCD_EP_Transmit() and grabs a mutex/lock with __HAL_LOCK().  If at this time a USB frame is received, the USB interrupt is triggered and eventually USBD_CUSTOM_HID_DataOut() is called, which calls USBD_LL_PrepareReceive() then HAL_PCD_EP_Receive() which tries to get the mutex/lock with __HAL_LOCK().  Obviously the mutex is taken, and slightly obscured in the __HAL_LOCK macro is a “return�?, which means the function bails out before starting the next RX transfer.  This leaves the system in a state where it’s not receiving USB packets and never will!

Solution(s):

I have thought of a couple of solution options, and implemented one (described below):

1)

     

Disable the USB interrupt in  HAL_PCD_EP_Transmit() before grabbing the mutex.  I’m not sure if there are other places where this should also occur.  Perhaps the macro should be modified to include disabling interrupts?

2)

     

Don’t require the mutex for _PCD_EP_Receive().  It doesn’t appear to be doing anything that needs mutex access, but I’m guessing there is some good reason why its done...

3)

     

Don’t call USBD_CUSTOM_HID_DataOut() from the interrupt.  This is the one I’ve implemented.  The changes required are:
  • USBD_LL_PrepareReceive() and USBD_LL_Transmit() need to be changed to pass on the return values of HAL_PCD_EP_Receive() and HAL_PCD_EP_Transmit() respectively, so that we know what’s going on.  The current code just pretends everything is always ok.
  • Set a bool flag in USBD_CUSTOM_HID_DataOut() instead of calling USBD_LL_PrepareReceive()
  • Poll regularly from a non-interrupt context to check the flag and if its set, call USBD_LL_PrepareReceive().  If USBD_LL_PrepareReceive() succeeds we can clear the flag, otherwise we’ll try again later.  I have disabled the USB interrupt in this function to provide a mutex for the flag.  There could be a scenario where a frame is received between the call to USBD_LL_PrepareReceive() and clearing the flag, which would mean that we would again not receive any RX frames.

michaeldischinger9
Associate
Posted on March 15, 2016 at 15:04

Concerning Solution No. 2:

In function USB_EPStartXfer() a read-modify-write is executed: 

USBx_DEVICE->DIEPEMPMSK |= 1 << ep->num;

jk.
Associate

Same problem, Then i tried to update the STMCubeF1 library to version v1.7.0 from v1.4.0 using the latest CubeMX Gui (in my case F1) and the problem is resolved. On a quick look is seems that they choose to remove the lock mechanism.

REdmo.1
Associate III

Is it possible that removing this lock has exposed a vulnerability?

I have a USB-MIDI driver "working" on STM32L4. I can send large amounts of data in either direction successfully. However, if I attempt to pass data in both directions at the same time (actually a large multi-packet message sent by the host to my STM32L4  device and being looped back), I occasionally see a failure. The problem manifests a failure of USBD_LL_Transmit() to place the bytes on the bus. I have counted the total number of bytes offered to USBD_LL_Transmit() (i.e. the sum of the size size parameter) and it is as expected, but my USB analyser is saying that occasionally packets are missing. either it is a complete packet missing (the last one) or it is a truncated pack e.g. a 1 byte packet on the bus when the size parameter is 4 byte. In this latter case, this one byte sent  (attempted three times) is the first byte of the packet but the messages are understandably being flagged as invalid.

It is looking increasingly like this is a problem in the USB peripheral. I am struggling to come up with a workaround. Should I try to disable "Receive" i.e. host to device when I want to perform a "Transmit" device to host? Would I use HAL_PCD_EP_Abort() for this and then do HAL_PCD_EP_Receive() again after the Transmit is complete (perhaps in the DataIn callback). It's ugly but I'm struggling to think of better ways to get round this apparent limitation.

Has anyone else seen this issue and if so, how did they get over it?
Thanks,

Richard

The API functions of ST's USB stack can be called only from an interrupt of the same priority as USB interrupt, or from a critical section, which disables either the USB or all interrupts. I recommend to stop using a broken bloatware and use a decent code like the TinyUSB.

richardst9
Associate II

Thanks for your response.

That does sound scary. It seems like something that they really should have told us (maybe they did and I didn't see it in dm00108129).

>The API functions of ST's USB stack can be called only from an interrupt of the same priority as USB interrupt
That sounds as if the best way to mitigate the risk associated with the manufacturer supplied libraries is to decouple the application from the usb interrupt with some thread-safe circular buffers and use the USB StartfOfFrame interrupt to handle transfers to and from those buffers. This is pretty much what I was trying to do.

>a critical section
My concern here was that I might reasonably expect a sufficiently high priority SOF interrupt to occur at start of frame and therefore not risk calling the fragile API whilst a bulk transfer was occurring. If I employ a critical section then this might delay the timing of the processing of the SOF interrupt. I wasn't sure that the USB peripheral accesses would be safe in those situations.

I will certainly look at TinyUSB though I don't always have the luxury of deciding which USB library to use. Faced with the prospect of debugging an issue that might occur only once for every few million bytes transferred might be enough to persuade me to turn away work that requires the manufacturers USB libraries in future.

Many thanks for the valuable information,
Richard

REdmo.1
Associate III

Oops, looks like I somehow changed account for that reply (I have struggles with logging in and seem to have ended up with multiple accounts). Perhaps I'd better resubmit as the original account...

.................................................

Thanks for your response.

That does sound scary. It seems like something that they really should have told us (maybe they did and I didn't see it in dm00108129).

>The API functions of ST's USB stack can be called only from an interrupt of the same priority as USB interrupt
That sounds as if the best way to mitigate the risk associated with the manufacturer supplied libraries is to decouple the application from the usb interrupt with some thread-safe circular buffers and use the USB StartfOfFrame interrupt to handle transfers to and from those buffers. This is pretty much what I was trying to do.

>a critical section
My concern here was that I might reasonably expect a sufficiently high priority SOF interrupt to occur at start of frame and therefore not risk calling the fragile API whilst a bulk transfer was occurring. If I employ a critical section then this might delay the timing of the processing of the SOF interrupt. I wasn't sure that the USB peripheral accesses would be safe in those situations.

I will certainly look at TinyUSB though I don't always have the luxury of deciding which USB library to use. Faced with the prospect of debugging an issue that might occur only once for every few million bytes transferred might be enough to persuade me to turn away work that requires the manufacturers USB libraries in future.

Many thanks for the valuable information,
Richard

........................................