Skip to content

Add USBHost #4321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 127 commits into
base: main
Choose a base branch
from
Draft

Add USBHost #4321

wants to merge 127 commits into from

Conversation

lynxD
Copy link

@lynxD lynxD commented Jun 19, 2025

I'm opening new pr since author of previous one has become unresponsive.
Previous PR: #4032

I've rebased code form the previous pr.

This code is WIP.
Currently there are a lot of warnings due to missing doc-strings and the like.

ferris and others added 30 commits June 19, 2025 16:37
Use stat_tx and stat_rx instead of nak() flag
Should only be included for usb_v4 peripherals.
lynxD added 23 commits June 19, 2025 16:38
0.000431 DEBUG Detecting device
└─ usb_host_keyboard::____embassy_main_task::{async_fn#0} @ src/bin/usb_host_keyboard.rs:25
Found device with speed = Low
0.161293 ERROR panicked at src/bin/usb_host_keyboard.rs:36:65:
called `Result::unwrap()` on an `Err` value: ChannelError(Stall)
USBDescriptor trait requires the descriptor to be of a fixed size and
this function is meant for getting descriptors of variable size.
Notice how in following DS4 gamepad configuration descriptor there are
multiple interface descriptors with bInterfaceNumber = 1.
//Decoded with http://eleccelerator.com/usbdescreqparser/

0x09,        // bLength
0x02,        // bDescriptorType (Configuration)
0xE1, 0x00,  // wTotalLength 225
0x04,        // bNumInterfaces 4
0x01,        // bConfigurationValue
0x00,        // iConfiguration (String Index)
0xC0,        // bmAttributes Self Powered
0xFA,        // bMaxPower 500mA

0x09,        // bLength
0x04,        // bDescriptorType (Interface)
0x00,        // bInterfaceNumber 0
0x00,        // bAlternateSetting
0x00,        // bNumEndpoints 0
0x01,        // bInterfaceClass (Audio)
0x01,        // bInterfaceSubClass (Audio Control)
0x00,        // bInterfaceProtocol
0x00,        // iInterface (String Index)

0x0A,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x01,        // bDescriptorSubtype (CS_INTERFACE -> HEADER)
0x00, 0x01,  // bcdADC 1.00
0x47, 0x00,  // wTotalLength 71
0x02,        // binCollection 0x02
0x01,        // baInterfaceNr 1
0x02,        // baInterfaceNr 2

0x0C,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x02,        // bDescriptorSubtype (CS_INTERFACE -> INPUT_TERMINAL)
0x01,        // bTerminalID
0x01, 0x01,  // wTerminalType (USB Streaming)
0x06,        // bAssocTerminal
0x02,        // bNrChannels 2
0x03, 0x00,  // wChannelConfig (Left and Right Front)
0x00,        // iChannelNames
0x00,        // iTerminal

0x0A,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x06,        // bDescriptorSubtype (CS_INTERFACE -> FEATURE_UNIT)
0x02,        // bUnitID
0x01,        // bSourceID
0x01,        // bControlSize 1
0x03, 0x00,  // bmaControls[0] (Mute,Volume)
0x00, 0x00,  // bmaControls[1] (None)

0x09,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x03,        // bDescriptorSubtype (CS_INTERFACE -> OUTPUT_TERMINAL)
0x03,        // bTerminalID
0x02, 0x04,  // wTerminalType (Headset)
0x04,        // bAssocTerminal
0x02,        // bSourceID
0x00,        // iTerminal

0x0C,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x02,        // bDescriptorSubtype (CS_INTERFACE -> INPUT_TERMINAL)
0x04,        // bTerminalID
0x02, 0x04,  // wTerminalType (Headset)
0x03,        // bAssocTerminal
0x01,        // bNrChannels 1
0x00, 0x00,  // wChannelConfig
0x00,        // iChannelNames
0x00,        // iTerminal

0x09,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x06,        // bDescriptorSubtype (CS_INTERFACE -> FEATURE_UNIT)
0x05,        // bUnitID
0x04,        // bSourceID
0x01,        // bControlSize 1
0x03, 0x00,  // bmaControls[0] (Mute,Volume)
0x00,        // iFeature

0x09,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x03,        // bDescriptorSubtype (CS_INTERFACE -> OUTPUT_TERMINAL)
0x06,        // bTerminalID
0x01, 0x01,  // wTerminalType (USB Streaming)
0x01,        // bAssocTerminal
0x05,        // bSourceID
0x00,        // iTerminal

0x09,        // bLength
0x04,        // bDescriptorType (Interface)
0x01,        // bInterfaceNumber 1
0x00,        // bAlternateSetting
0x00,        // bNumEndpoints 0
0x01,        // bInterfaceClass (Audio)
0x02,        // bInterfaceSubClass (Audio Streaming)
0x00,        // bInterfaceProtocol
0x00,        // iInterface (String Index)

0x09,        // bLength
0x04,        // bDescriptorType (Interface)
0x01,        // bInterfaceNumber 1
0x01,        // bAlternateSetting
0x01,        // bNumEndpoints 1
0x01,        // bInterfaceClass (Audio)
0x02,        // bInterfaceSubClass (Audio Streaming)
0x00,        // bInterfaceProtocol
0x00,        // iInterface (String Index)

0x07,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x01,        // bDescriptorSubtype (CS_INTERFACE -> AS_GENERAL)
0x01,        // bTerminalLink
0x01,        // bDelay 1
0x01, 0x00,  // wFormatTag (PCM)

0x0B,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x02,        // bDescriptorSubtype (CS_INTERFACE -> FORMAT_TYPE)
0x01,        // bFormatType 1
0x02,        // bNrChannels (Stereo)
0x02,        // bSubFrameSize 2
0x10,        // bBitResolution 16
0x01,        // bSamFreqType 1
0x00, 0x7D, 0x00,  // tSamFreq[1] 32000 Hz

0x09,        // bLength
0x05,        // bDescriptorType (See Next Line)
0x01,        // bEndpointAddress (OUT/H2D)
0x09,        // bmAttributes (Isochronous, Adaptive, Data EP)
0x84, 0x00,  // wMaxPacketSize 132
0x01,        // bInterval 1 (unit depends on device speed)
0x00,        // bRefresh
0x00,        // bSyncAddress

0x07,        // bLength
0x25,        // bDescriptorType (See Next Line)
0x01,        // bDescriptorSubtype (CS_ENDPOINT -> EP_GENERAL)
0x00,        // bmAttributes (None)
0x00,        // bLockDelayUnits
0x00, 0x00,  // wLockDelay 0

0x09,        // bLength
0x04,        // bDescriptorType (Interface)
0x02,        // bInterfaceNumber 2
0x00,        // bAlternateSetting
0x00,        // bNumEndpoints 0
0x01,        // bInterfaceClass (Audio)
0x02,        // bInterfaceSubClass (Audio Streaming)
0x00,        // bInterfaceProtocol
0x00,        // iInterface (String Index)

0x09,        // bLength
0x04,        // bDescriptorType (Interface)
0x02,        // bInterfaceNumber 2
0x01,        // bAlternateSetting
0x01,        // bNumEndpoints 1
0x01,        // bInterfaceClass (Audio)
0x02,        // bInterfaceSubClass (Audio Streaming)
0x00,        // bInterfaceProtocol
0x00,        // iInterface (String Index)

0x07,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x01,        // bDescriptorSubtype (CS_INTERFACE -> AS_GENERAL)
0x06,        // bTerminalLink
0x01,        // bDelay 1
0x01, 0x00,  // wFormatTag (PCM)

0x0B,        // bLength
0x24,        // bDescriptorType (See Next Line)
0x02,        // bDescriptorSubtype (CS_INTERFACE -> FORMAT_TYPE)
0x01,        // bFormatType 1
0x01,        // bNrChannels (Mono)
0x02,        // bSubFrameSize 2
0x10,        // bBitResolution 16
0x01,        // bSamFreqType 1
0x80, 0x3E, 0x00,  // tSamFreq[1] 16000 Hz

0x09,        // bLength
0x05,        // bDescriptorType (See Next Line)
0x82,        // bEndpointAddress (IN/D2H)
0x05,        // bmAttributes (Isochronous, Async, Data EP)
0x22, 0x00,  // wMaxPacketSize 34
0x01,        // bInterval 1 (unit depends on device speed)
0x00,        // bRefresh
0x00,        // bSyncAddress

0x07,        // bLength
0x25,        // bDescriptorType (See Next Line)
0x01,        // bDescriptorSubtype (CS_ENDPOINT -> EP_GENERAL)
0x00,        // bmAttributes (None)
0x00,        // bLockDelayUnits
0x00, 0x00,  // wLockDelay 0

0x09,        // bLength
0x04,        // bDescriptorType (Interface)
0x03,        // bInterfaceNumber 3
0x00,        // bAlternateSetting
0x02,        // bNumEndpoints 2
0x03,        // bInterfaceClass
0x00,        // bInterfaceSubClass
0x00,        // bInterfaceProtocol
0x00,        // iInterface (String Index)

0x09,        // bLength
0x21,        // bDescriptorType (HID)
0x11, 0x01,  // bcdHID 1.11
0x00,        // bCountryCode
0x01,        // bNumDescriptors
0x22,        // bDescriptorType[0] (HID)
0xFB, 0x01,  // wDescriptorLength[0] 507

0x07,        // bLength
0x05,        // bDescriptorType (Endpoint)
0x84,        // bEndpointAddress (IN/D2H)
0x03,        // bmAttributes (Interrupt)
0x40, 0x00,  // wMaxPacketSize 64
0x05,        // bInterval 5 (unit depends on device speed)

0x07,        // bLength
0x05,        // bDescriptorType (Endpoint)
0x03,        // bEndpointAddress (OUT/H2D)
0x03,        // bmAttributes (Interrupt)
0x40, 0x00,  // wMaxPacketSize 64
0x05,        // bInterval 5 (unit depends on device speed)

// 225 bytes
Examples for stm32g0 not build.
@lynxD lynxD mentioned this pull request Jun 19, 2025
4 tasks
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ControlPipe trait assumes endpoint to be 0, however the spec allows for multiple control endpoints.
https://stackoverflow.com/questions/22417493/what-is-the-use-of-multiple-control-endpoints-non-ep0

This seems to be of limited use but at least the API should allow for it.
Anybody has idea how to handle this best? Add endpoint number parameter to setup method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My 2 cents: Non-0 control endpoints are not used in any of the standard device class specs. It's possible that no one who uses this library will ever have a use for such a thing, so I'd prioritize landing this feature rather than developing an interface to meet a theoretical need.

@AlexCharlton
Copy link
Contributor

Here's a commit that restores the ability to compile with the log feature: AlexCharlton@46c9d8c

This fix had been reverted because I had originally added the {:x?} format directive, which evidently dfmt doesn't recognize. I've simply used {:?} instead.

@AlexCharlton
Copy link
Contributor

And another commit that fixes iter_interface: AlexCharlton@4c3afbd

It incorrectly assumed that interface descriptors would always come immediately after config descriptors. Embassy's own descriptor builder may stick an interface association descriptor before any interface descriptors, though. Since the IAD isn't actually needed to parse IDs (and since this iterator is also used to parse configurations from potentially unknown sources), I simply made it so that we skip over any non-interface descriptors, which is also what the previous interface iterator did.

@AlexCharlton
Copy link
Contributor

One additional fix for this branch: AlexCharlton@3ef6a05

get_active_configuration (unlike get_configuration) was requesting a config descriptor that was always equal to the maximum available buffer size. Some devices don't like this and may not respond, so we're limiting the request to be exactly the size of the descriptor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants