[](https://github.com/pdh11/cotton/actions)
[](https://codecov.io/gh/pdh11/cotton)
[](https://deps.rs/repo/github/pdh11/cotton)
[](https://crates.io/crates/cotton-usb-host)
[](https://crates.io/crates/cotton-usb-host)
[](https://docs.rs/cotton-usb-host/latest/cotton_usb_host/)
[](http://creativecommons.org/publicdomain/zero/1.0/)
# cotton-usb-host
Part of the [Cotton](https://github.com/pdh11/cotton) project.
## A no-std, no-alloc USB host stack for embedded devices
This crate enables the USB host-controller peripheral on the RP2040
microcontroller, allowing USB devices (memory sticks, keyboards, hubs,
etc.) to be connected directly to the RP2040 and controlled by it.
USB operation is _asynchronous_ and so this crate is suited for use
with embedded asynchronous executors such as
[RTIC 2](https://rtic.rs/2/book/en/) and
[Embassy](https://embassy.dev).
Includes:
- control, interrupt, and bulk endpoint support;
- hub support;
- hot-plug, and hot-unplug, including of hubs.
Currently supports:
- RP2040 (USB 1.1 host)
Also supports, via other crates:
- i.MX RT 1062 as used in Teensy 4.1, see
[imxrt-usbh](https://github.com/imxrt-rs/imxrt-usbh)
System-tests and examples:
- [rp2040-usb-hid-boot-keyboard](https://github.com/pdh11/cotton/blob/main/cross/rp2040-w5500-rtic2/src/bin/rp2040-usb-hid-boot-keyboard.rs): identifying
and driving USB keyboards ("Human Interface Devices", HID);
- [rp2040-usb-msc](https://github.com/pdh11/cotton/blob/main/cross/rp2040-w5500-rtic2/src/bin/rp2040-usb-msc.rs):
identifying and driving mass-storage class devices (e.g., USB flash
drives); **WARNING** this _writes_ to the USB drive, don't use one with
data on that you want to keep;
- [rp2040-usb-otge100](https://github.com/pdh11/cotton/blob/main/cross/rp2040-w5500-rtic2/src/bin/rp2040-usb-otge100.rs):
identifying (not yet really "driving") a Plugable USB2-OTGE100
Ethernet adaptor (based on ASIX AX88772).
Limitations:
- maximum of 31 devices total (including hubs);
- maximum of 15 hubs;
- maximum of 15 ports on any one hub;
- maximum of 256 bytes of configuration descriptors;
- isochronous endpoints not yet implemented;
- supports Low Speed (1.5Mbits/s) and Full Speed (12Mbits/s)
operation only -- not High Speed (480Mbits/s) or above.
12Mbits/s), but as the _only_ changes in USB 2.0 compared to 1.1
were related to the addition of HS (480Mbits/s), it seems more honest
to describe it as USB 1.1.
USB 2.0 and earlier did not, but even for USB 2.0 such wide
hubs are either very rare or, quite possibly, non-existent. Most
freestanding hubs which _appear_ to have more than about 7 downstream
ports, are in fact multiple hubs in a trenchcoat.
Library documentation is [on
docs.rs](https://docs.rs/cotton-usb-host/latest/cotton_usb_host/).
## Using cotton-usb-host with a Raspberry Pi Pico
This crate configures the Raspberry Pi Pico's USB peripheral for
USB host mode _only_, and not USB device mode. So before running your
code, make sure that the USB connector on your Raspberry Pi Pico
is plugged into a USB device, and not into another USB host such as a
laptop[^3]. (You can still use a SWD connection via the 3-pin debug
connector to program and debug your Raspberry Pi Pico -- just not
the USB connection.)
If your Raspberry Pi Pico is itself powered by USB (perhaps via a
Pico Debug Probe), then it will not have enough power to reliably
supply USB power to downstream devices unless you power your Pico's
VUSB/GND pins from a separate 5V power supply — and perhaps not
even then. For best results with multiple devices, you should use a
_powered_ hub. (Powered hubs with micro-USB plugs, compatible with the
Raspberry Pi Pico in host mode, are often sold as "[OTG
hubs](https://www.amazon.co.uk/AuviPal-Adapter-Playstation-Classic-Raspberry-Black/dp/B083WML1XB/)".)
The crate is split between a generic (hardware-agnostic) `usb_bus::UsbBus`
class, and a host-controller driver specific to the RP2040. So the
minimal code example would involve:
- creating a `UsbShared` object, making sure it's shared between the
software tasks and the hardware interrupt handler;
- arranging that the `USBCTRL_IRQ` interrupt handler calls `UsbShared::on_irq()`;
- creating a `UsbStatics` object, which needn't be shared, but must
be `&'static` — for instance, by using the `static-cell` crate;
- constructing a `host::rp2040::Rp2040HostController` from the UsbShared,
the UsbStatics, and the USB register banks from `rp2040-pac`;
- constructing a `UsbBus` from the host-controller driver;
- obtaining a stream of device-status events from
`UsbBus::device_events()` — or, alternatively,
`UsbBus::device_events_no_hubs()` for smaller code-size if
supporting USB hubs isn't required;
- waiting on the stream until it produces a `DeviceEvent::Connect`
indicating that the device has been detected;
- using APIs such as `UsbBus::control_transfer` to read descriptors,
`UsbBus::configure` to configure the device appropriately, and
`UsbBus::interrupt_endpoint` to read data from the device.
A complete example for RP2040 is at
<https://github.com/pdh11/cotton/blob/main/cross/rp2040-w5500-rtic2/src/bin/rp2040-usb-msc.rs>;
**WARNING** this _writes_ to the USB drive, don't use one with data
on that you want to keep.
matter) have USB Micro-B receptacles (sockets), capable of receiving
Micro-B plugs only. Because they are capable of both USB device and
USB host, they _should_ arguably have had USB Micro-AB receptacles,
capable of receiving both Micro-A (rectangular) and Micro-B
(trapezium) plugs. But USB cables and adaptors with Micro-B plugs for
their _host_ side connection (instead of Micro-A which it [should
technically be](https://en.wikipedia.org/wiki/USB_On-The-Go)) are
common. Both ST and Renesas devboards get this right: they have Micro-AB
receptacles (though the very newest ST ones have type-C instead).
## Writing drivers for USB devices
This crate includes an example of identifying and communicating with
a Plugable USB2-OTGE100 Ethernet adaptor based on the ASIX AX88772
chip. A more complete example driver, for USB mass-storage class devices,
is in the cotton-usb-host-msc crate.
Once your code has successfully created the `UsbBus` object and has
called `UsbBus::device_events()`, it will receive `DeviceInfo` objects
which allow your code to identify relevant devices either by class
code (for generic class drivers such as mass-storage or HID) or by VID
and PID (for device-specific drivers).
The cotton-usb-host-msc crate includes example usage of bulk endpoints;
the cotton-usb-host-hid crate uses an interrupt endpoint. (Isochronous
endpoints are not currently supported.)
## Writing drivers for alternative host controllers
The `UsbBus` code _should_ be generic enough to be usable with other
microcontrollers' USB host peripherals. You'll need to implement the
`host_controller::HostController` trait, which encapsulates all the
actual hardware interaction. Typically such host controllers have a
smallish, fixed number of "pipes" (actively-used endpoints) which can
be used simultaneously; you might find `async_pool::Pool`, as used by
the RP2040 host-controller driver, to be a convenient way of
allocating those pipes as required.
The RP2040 support is in this repo to provide a convenient worked example;
specific host-controller support for other microcontrollers probably
belongs in those microcontrollers' HAL crates.
Support for i.MX RT processors (such as used in the Teensy 4.1) has been
implemented in [imxrt-usbh](https://github.com/imxrt-rs/imxrt-usbh).
## TODO
TODO before merge
- [x] Hub state machine
- [x] Unit tests
- [x] Interlocking to avoid contending on pipe 0
- [x] Rename types to wire
- [x] Introduce delay to process_hub_packet and do away with currently_resetting
- [x] Move all tests to src/tests
TODO before 0.1.0:
- [x] System test
- [x] Allocate endpoints from device object?
- [x] UnconfiguredDevice/Device?
- [x] Bulk in/out
- [x] Enough configuration descriptor smarts to recognise MSC when we see it
- [x] More of MSC -- at the very least, detect DASD
- [ ] Max packet size for bulk endpoints
- [x] doc-comments
- [x] At least one real example (MSC? HID?)
- [ ] Review register usage for contention (buff_status?)
- [ ] STM32?
TODO later:
- [ ] Non-async version?
- [ ] rp-pac vs rp2040-pac?
- [ ] More microcontrollers