rp-hal icon indicating copy to clipboard operation
rp-hal copied to clipboard

USB host support (experimental)

Open nilclass opened this issue 2 years ago • 5 comments

Update 2023-08-06

  • Merged in latest HAL & PAC updates, the corresponding new PAC PR is https://github.com/rp-rs/rp2040-pac/pull/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:
    • documentation can be found here: https://nilclass.github.io/usbh/doc/usbh/
    • a simple keyboard driver is included (code)
  • 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: https://github.com/rp-rs/rp-hal/commit/0cf375f787c83a6d355dadb50e6eff5d087a5d30 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 :stuck_out_tongue:

  • 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 :)

nilclass avatar Aug 01 '23 08:08 nilclass

The failing check is caused by cargo-hack no longer being compatible to our MSRV. https://github.com/rp-rs/rp-hal/pull/666 is a possible fix.

jannic avatar Aug 07 '23 16:08 jannic

@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 avatar Nov 18 '23 04:11 tommy-gilligan

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 avatar Nov 23 '23 11:11 tommy-gilligan

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.

tommy-gilligan avatar Nov 23 '23 12:11 tommy-gilligan

@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 :stuck_out_tongue:). 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.

nilclass avatar Dec 03 '23 09:12 nilclass