Skip to content
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

USB host support (experimental) #660

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

Conversation

nilclass
Copy link
Contributor

@nilclass nilclass commented Aug 1, 2023

Update 2023-08-06

  • Merged in latest HAL & PAC updates, the corresponding new PAC PR is Additional registers and fields for USB host support rp2040-pac#85
  • Interrupt endpoints are now working
  • It is no longer necessary to handle delays in application code (they are handled by enabling / disabling the SOF interrupt as needed, and counting X frames to wait X milliseconds)
  • usbh is in a state where it's possible to write simple drivers:
  • The example has been updated as well, it is now much more readable
  • Still missing (this list is incomplete):
    • Hub support: only a single device can be handled right now
    • Isochronous and Bulk transfers
    • Devices with descriptors that are larger than 64 bytes (connecting devices with longer descriptors currently will produce an error)

Original description

As mentioned on matrix, I've been looking into USB host support.
While it is by no means finished or even usable, I want to share my progress and findings.

My initial draft (now removed from this branch) can be found in this commit: 0cf375f
It works to the point where a keyboard can be attached and the LEDs can be controlled.

Since then I've tried to translate that draft into a more reasonable, interrupt driven implementation.

It's tricky, since I'm not certain yet which parts of the USB host stack should go into the HAL and which parts should go into a more generic package.
I'm thinking that embedded USB host support in general could mirror the way things work on the device side:
For devices there is the usb-device package, which provides the glue between different USB functions (usbd-serial, usbd-audio, ...) on one side and the specific USB device hardware on the other side.
For hosts, a similar package would provide the glue between function drivers and the specific USB host hardware.
There is some prior art in that direction: the aptly named usb-host crate defines such an interface, although it is in a very early state.
For now I decided not to target that crate though, but instead do my own experiments here: usbh.

One difference between the two is that the usb-host API for control transfers is blocking, while my attempt is using only interrupts and callbacks on the driver side to.
In my version also the state machine for control transfers would not be part of the HAL, but part of the common usbh package. That means the HAL only provides methods to issue SETUP, DATA IN and DATA OUT transactions on the control endpoint. The usbh package then calls these methods as needed for a control in / control out transfer.

How to try it out?

This takes a couple of pieces:

  1. Changes to the PAC: I'm currently using this branch, which is based on an older version of the PAC. The changes there will need some adjustments to work with the latest PAC. However, the latest PAC did not work for me, since it requires a bunch of other changes to the HAL as well (I tried basing my work on the experimental-update-pac branch by @jannic but ran into a bunch of errors which I did not want to figure out at the time)
  2. The usbh package https://github.com/nilclass/usbh (it's not published yet, so you'll have to use it from git or from a local clone)
  3. The example application, which can be found here: https://github.com/nilclass/rp-hal-usb-host-example It reads the device descriptor and the first configuration descriptor and logs them, then goes on assuming the attached device is a keyboard (it doesn't actually interpret the descriptor yet to check) and tries to poll for keypresses (not working yet)

You'll likely have to adjust some paths in various Cargo.toml files to get this to work. The example also requires rp2040-monotonic, I'm using a local copy of that as well, with the Cargo.toml adjusted so that rp2040-hal and rp2040-monotonic both depend on the same local rp2040-pac copy.

What is working, what is missing?

A bunch of things are working, but nothing completely useful yet 😛

  • control transfers: as mentioned above, the usbh package facilitates control transfers (code)
  • enumeration: when a device is detected it issues a reset, attempts a GetDescriptor control transfer on address 0, then resets again and assigns an address to the device. This functionality is also part of the usbh package, not HAL specific (code)

Currently I am trying to get interrupt endpoints to work, but it is a bit tricky.

Besides defining a driver interface, another big thing that would be very useful is hub support.

This is a big topic, and I am not sure I can get it to a satisfactory state on my own, so if somebody wants to collaborate, please reply here or ping me on matrix :)

This example blinks the LEDs of an attached USB keyboard. It does not
work with any keyboard yet, but it does work with some.

Usage:
    let host = hal::usb::BlockingHost::new(
        usbctrl_reg,
        usbctrl_dpram,
        usb_clock,
        &mut resets
    );
    host.reset();
    host.run();
@ithinuel ithinuel self-requested a review August 1, 2023 10:43
@jannic jannic mentioned this pull request Aug 7, 2023
@jannic
Copy link
Member

jannic commented Aug 7, 2023

The failing check is caused by cargo-hack no longer being compatible to our MSRV. #666 is a possible fix.

@tommy-gilligan
Copy link
Contributor

@nilclass I'm keen to help in whatever way I can 🙋‍♂️ I don't want to be too annoying but I will probably try to contact you on matrix also

@tommy-gilligan
Copy link
Contributor

sorry, i was meant to provide some useful feedback here but haven't gotten around to it yet. i've been sidetracked trying to come up with a pio based driver that uses the same usbh trait.

fwiw there are no tricky conflicts to resolve in rebasing this PR onto v0.9.1 . there's no rush to do so but i just thought it's worth knowing about.

i think it makes sense to bring the example into this repo if at all possible. it will make it easier for others to participate in this PR and i think it's somewhat inevitable anyways because users will want an example too.
(i will try to do this presently but it will mean opening a PR on top of your branch, which might be a bit annoying/confusing)

@tommy-gilligan
Copy link
Contributor

ac2da05c12814a82b3b124f86f38d75c2589078d

it's maybe a bit unfortunate i had to remove console output from the example (didn't want the defmt dependency) but making the onboard led blink is probably enough to determine whether or not the example is working.
uart could be used instead of defmt but i figure the example is already a bit more complex than the others in the repo.

@nilclass
Copy link
Contributor Author

nilclass commented Dec 3, 2023

@tommy-gilligan thanks, I've picked the commit over here.
Unfortunately I'm having some trouble with the example currently that I need to figure out: it works when I first connect a keyboard, but when I remove it and reattach it, it's no longer recognized. I haven't had the time to debug this yet (without logging it's a bit more tricky 😛).
Does it work for you?

Alternatively the example could also go into the rp-pico examples in the rp-hal-boards repo, some of those already depend on defmt, so in that case we could keep the logging.

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.

3 participants