Skip to content

Adjust API to allow UsbDevice and UsbClass-es to be Send without wrapping every UsbBus implementation method in a critical section #165

@FeldrinH

Description

@FeldrinH

Currently every UsbBus implementation that I am aware of is Sync and it synchronizes access by wrapping most UsbBus methods in a critical section. This can cause latency issues, because it means that during USB reads, writes and polling any higher priority interrupt handlers will be delayed. This has been a problem in practice (see stm32-rs/stm32-usbd#32, stm32-rs/synopsys-usb-otg#27).

In theory a UsbBus could be implemented which is not Sync, as the Sync bound was removed in #149. Unfortunately, in practice such a UsbBus ends up being extremely restrictive. This is because UsbDevice and UsbClass-es contain a reference to UsbBus and if UsbBus isn't Sync then &UsbBus isn't send and therefore UsbDevice and UsbClass-es using the bus aren't Send. This means that there is no safe way to move them into interrupt handlers or elsewhere, even with mutexes and other synchronization. This rules out practically any architecture except for using UsbDevice and UsbClass-es in the main loop.

Given all this, there is a practical need for a way to implement UsbBus without critical sections everywhere while still allowing you to move UsbDevice and UsbClass-es to other threads.

My main claim is this:
I believe it isn't possible for individual UsbBus implementations to fix this problem on their own. There is a need to adjust the usb-device API to resolve this.

I don't have a specific proposal, but I have two high-level solution outlines:

  1. Split UsbBus into components which can be owned exclusively by UsbDevice and UsbClass-es. E.g. the UsbDevice would have exclusive ownership of the core USB registers (interrupts, statuses and whatnot) and each UsbClass would have exclusive ownership of its own endpoints. Since all endpoints are allocated ahead of time there should be minimal interaction between these components at runtime and thus minimal need for synchronization using critical sections.

  2. Make UsbDevice exclusively own the UsbClass-es. This would allow UsbDevice to exclusively own the entire UsbBus, thus requiring no synchronization. (This is compatible with solution 2 from this semi-related issue: Figure out a better way to keep track of the list of classes #9.)

I would love to have some feedback on what people think of this. Can this be done? Should this be done? Is anyone using workarounds for this in practice and if so what are these workarounds?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions