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

Start an EHCI USB host driver + assorted required infrastructure #103

Merged
merged 23 commits into from
Feb 17, 2024
Merged

Conversation

IsaacWoods
Copy link
Owner

@IsaacWoods IsaacWoods commented Jan 24, 2024

This PR aims to add a mostly-working userspace driver for EHCI-compatible USB host controllers. This will require expanding lots of the infrastructure in the kernel + userspace for things like interrupt handling etc.

Closes #101

Other bits:

  • Add interrupt-map parsing for the device tree
  • Add a Driver Development Kit (DDK) with DMA utilities to poplar
  • Add support for finer-grained control of device support in platform_bus
  • Add a usb_hid driver that handles HID devices
  • Work out size for initial GetDescriptor for now

Future work (too complex for this PR):

  • Convert platform_bus to use an async runtime given its new complexity
  • Add an Event kernel object to indicate an interrupt to userspace
  • Add PCIe interrupt routing to userspace and hand off via platform_bus via pci_bus

This creates a Driver Development Kit (DDK) module and associated feature
in the `poplar` library that will hold userspace abstractions that are only/
mostly useful for drivers. This pulls in `linked_list_allocator`, and probably
a bunch of other stuff in the future, so it's useful to have it feature-gated.

The first abstraction in the DDK is a little `DmaPool` and `DmaObject` etc.
system for dynamically managing memory that needs to be addressed physically.
This has already proven useful in our EHCI driver.

The `ddk` module can be re-exported from our custom `std` too for easier
use.
`main.rs` is going to become very unwieldly as is, so these can be separated
out.
We need to have another look at where we got these definitions from / the
Rust `std` does a wacky job of working out what to export into the prelude.
These were somehow missing so just manually re-export them for now / forever.
Wow this took SO long to get working! There's a lot more work before this
does everything we need it to but it's good to see the stupid controller
doing anything tbh.
Wow this took a long time to track down... `mycelium_bitfield` doesn't
work quite how we expected it to, and it took a while because we haven't
set any of these top bits yet. Turns out when a setup packet doesn't make
sense, QEMU just kind of grinds the whole queue to a halt...

Oh well, fixed now.
This is still somewhat rudimentary (only allows one data stage) but fine
for control transfers etc.
Still need to work out exactly why we need to do the whole 'request a header',
then the full device for high-speed devices but we can now get a full
descriptor from a device (on QEMU)! This took ages to get fully working so
is a big step forward.
… handing them off

This is preparing for a USB HID driver, which needs to parse a USB device's
configuration hierachy to know if it is a HID device. That's not really
possible to write as a filter we can apply in `platform_bus`, so this is
an extra step where we ask a driver if it's sure it can drive a device
before handing it off. This allows a driver to use a more permissive initial
filter and then refuse devices that don't match more stringent criteria later.

The support for this in `platform_bus` is a bit crap tbh, and will fall over
if drivers actually refuse devices. That won't happen for our initial plans
and can effectively be pushed down the line until we have user async runtimes,
which should allow us to write much more expressive message-handling logic
(hopefully; obviously we don't really know what the ergonomics will look like
yet).

This also really needs docs with nice diagrams and stuff, otherwise it's
probably pretty impenitrable. Work on that at some point.
@IsaacWoods
Copy link
Owner Author

Thoughts about userspace interrupt handling

(this was a comment in the ehci code that laid out some thoughts re userspace interrupt delegation that shouldn't be committed, but I don't want to lose)

I'm thinking we could have a couple of new kernel objects. Ports would be like
a more lightweight version of Channels - they pass statically-sized packets around instead of
messages, can't transfer objects between tasks etc.
Events could then be other objects that can be attached to ports, such that a
a packet is sent to the port when the event is signalled.
An Event could then be passed into userspace by the pci_get_info syscall (or
somehow else for RISC-V systems etc) that represents the interrupt delegated to
userspace.
When the kernel handles an interrupt delegated to userspace, it would simply
need to signal the event, which would then put the packet in the right place.
There would then be some way to wait on a packet to come down a port, so that
you can yield from a task til an interrupt arrives.
There should probably be a future way to manage packets from ports
asynchronously, using some lockless queue or whatever, but that's not needed
initially.

Also thinking around getting the interrupt handler to run when an interrupt
actually comes in - I'm thinking we either need some way to build it into a
userspace async runtime so we await a transfer completion and it's waiting to
service the interrupt and then wakes the future, or we could spawn another task
(in this address space so wouldn't need much more infrastructure) that simply
waits for interrupts and then pokes something - not sure how we'd manage memory
correctly across the two (well it might be easy enough actually - we'd just need
to make sure it didn't think it could random kernel objects it didn't own etc.).
The first option could be cleaner if we want to use async stuff eventually
anyways though.

…hierachy

This gets all the ducks in a row re the hopefully correct ordering of `GetDescriptor`
requests, setting the address, and getting the configuration hierachy.
We won't know how close this is to working until we get a real device running
it, but it seems to work on QEMU at least. Also restructures the transaction
code to take ownership of the `SetupPacket` memory - more work will be needed
to take ownership of the data buffer too and then give it back.
This is the start of a driver for USB HID-class devices. It can detect devices
it can potentially support from the Platform Bus, and then interrogate
their configuration hierachies for HID-class interfaces. Also includes some
definitions for HID descriptors for the future.

Next steps will be actually parsing the HID descriptors (including the Report
descriptors, which look potentially complex), and then working on Interrupt
endpoints to be able to get data from a HID device! I think it would be worth
getting some of the other parts of this infrastructure, including userspace
interrupt delegation and potentially userspace async runtimes, working before
we tackle this.
@IsaacWoods
Copy link
Owner Author

This seems like a reasonable place to leave this PR - the EHCI driver Mostly Works on QEMU in principal. Remaining functionality will need significant new design work.

@IsaacWoods IsaacWoods merged commit 5f8bf94 into main Feb 17, 2024
1 of 2 checks passed
@IsaacWoods IsaacWoods deleted the ehci branch February 17, 2024 20:20
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.

USB support - EHCI
1 participant