This is intended to be a discussion. I want to get the views of those in the community.
Basic premise: I have a STM32F7 USB driver based on Cube library code that I am happy with. The client wants to use this driver in a FreeRTOS environment. They are placing a requirement that as little work is done in interrupt routines as possible. That seems a worthy aspiration, but possibly isn't as the Cube library code is intended to operate. I think they regard the USB_OTG interrupt as we might other interrupts; set a flag to say it happened and deal with it in a User thread. Those who have been there know that the USB_OTG handler in the library is 400 lines of code dealing with a vast range of USB-related events many/most/almost all of which have to be dealt with before the interrupt exits.
I am not too worried about Receive or Transmit. I can see sensible ways to implement these operations. I can provide details if required.
My concern is enumeration. The client seems to think that this should be performed in a separate thread rather than in the USB_OTG interrupt routine.
What does the community think about this? Is it likely to be safe?
Transmit: This can be initiated from a User thread anyway so I'm not expecting too many problems. I plan to use the HAL_PCD_DataInStageCallback() in usbd_conf.c to generate an RTOS signal to indicate that transmission is complete and that the output buffer is available.
Receive: I plan to use the Itf_Receive callback in the USB class interface to post received raw USB data to a message queue/mailbox.
Enumeration is much less clean. I can trap Setup transactions in HAL_PCD_SetupStageCallback() and, instead of directly calling USBD_LL_SetupStage(), send a message (containing the received setup "command") to a separate thread that will use USBD_LL_SetupStage() to complete the transaction.
It didn't work straight out of the box, because USB_EP0StartXfer() chooses to re-enable the interrupt just before initiating the transfer (enabling the interrupt causes the interrupt to immediately run and corrupt the values that have been loaded into the USB registers). But moving the write to DIEPEMPMSK after the write to DIEPCTL got around that problem.
With this change to stm32f7xx_ll_usb.c, the STM32F7 enumerates successfully and I am able to perform basic Receive and Transmit operations through RTOS threads. If it was for a home hobby, that would be it. But I need to be confident that this is going to work reliably in the client's products in the field.
Topics for discussion.
- Is it a reasonable goal to move as much processing out of the USB_OTG interrupt and out into a separate thread? [Can you tell which way I lean on this?] If so, how much?
- Is it even possible to do it in a safe way? Running all Endpoint 0 accesses in the USB_OTG interrupt ensured that they were serialised. Running some of them in separate thread opens up a whole can of worms regarding thread safety (as I found). Did the authors of the Cube libraries envisage sharing the enumeration processing between the USB_OTG interrupt and a separate thread and therefore make sure the libraries worked reliably in this situation?
- Do the benefits of trying to handle USB enumeration in a separate non-interrupt thread outweigh the risks?
- What do others do when faced with this situation? Are others rejigging the USB_OTG interrupt to move some of its processing out into separate threads?
Keen to hear your thoughts,