Skip to main content

Bus

Struct Bus 

Source
pub struct Bus<'a> { /* private fields */ }
Available on non-crate feature bcm2712 only.
Expand description

The USB bus behind the controller: which devices are on it, where, and at what addresses — kept across calls so devices that appear or vanish after the first walk can still be brought up or reported.

enumerate is this with the state thrown away, which is enough when everything is plugged in before the board boots and never moves. It stops being enough as soon as something attaches late. A device that takes seconds to come up is missed by a one-shot walk and there is no second look, and the reason there can’t be one is that the walk owns the two things bringing a device up requires: the next free address, and the hub topology every split target is derived from. A Bus holds both, so Self::poll can do what Self::enumerate did, later and for one port.

Typical use is Self::enumerate once at startup and Self::poll from the main loop:

ⓘ
let mut bus = Bus::new(&dwc2);
bus.enumerate(&timer, |channel, timer, event| { .. })?;
loop {
    bus.poll(&timer, |channel, timer, event| { .. })?;
    timer.delay_ms(250);
}

Both take the same callback, so the code that recognizes a device doesn’t care whether it was there at boot or arrived later — which is the point, since on some boards that is a matter of timing rather than of anything the user did.

Implementations§

Source§

impl<'a> Bus<'a>

Source

pub fn new(dwc2: &'a Dwc2Host) -> Self

A bus with nothing on it yet, for an already-initialized controller. Nothing is touched until Self::enumerate.

Source

pub fn enumerate<F>( &mut self, timer: &Timer, on_event: F, ) -> Result<(), EnumerationError>
where F: FnMut(&mut Channel<'_>, &Timer, Event) -> ControlFlow<()>,

Walks the whole bus from the root port, reporting every device it brings up as Event::Attached, and records the topology so Self::poll can pick up from here.

The caller must have waited for dwc2::Dwc2Host::port_connected. This resets the root port, brings up and configures the root hub (soldered on, on every board this targets — see this module’s doc), powers its downstream ports, then for each connected port resets it, reads the device’s descriptor, and assigns it an address before reporting it. Each device is addressed before the next port is touched, since two just-reset devices must never sit at address 0 at once.

A device that turns out to be a hub itself is brought up the same way the root hub was and walked in turn, depth first, so the whole bus is reported however many hubs deep it goes (up to the four levels below the root hub that USB allows). Each device carries the hub_address/port it was found on and its depth, and its endpoint already points at the right transaction translator for its position — the nearest high-speed hub above it, which for a device several levels down is not necessarily the hub it is plugged into (see Hub::split_target).

on_event receives the Channel the walk ran on (to do further transfers), the timer, and the Event; return ControlFlow::Break from it to stop early, e.g. once the device of interest is found. A hub is reported before the devices behind it, so breaking on one stops before it is walked.

A port whose reset or descriptor read fails is skipped rather than aborting the walk, as is a hub that fails to configure (it is still reported, just not descended into). Calling this again discards everything known and starts over, which is what a bus that has been reset out from under this type needs.

Source

pub fn poll<F>( &mut self, timer: &Timer, on_event: F, ) -> Result<(), EnumerationError>
where F: FnMut(&mut Channel<'_>, &Timer, Event) -> ControlFlow<()>,

Asks every tracked hub what has changed since the last look, bringing up anything newly attached and reporting anything gone. One sweep, then it returns — pacing is the caller’s.

Every port of every tracked hub is read with GET_PORT_STATUS and compared against what this type last saw there, so the cost of a sweep scales with how many ports are on the bus rather than with how much is happening on it. On a board whose root hub has four ports and carries one four-port hub, that is eight control transfers per sweep.

A hub also has a status-change endpoint that would name just the ports that moved (see Hub::status_endpoint), which is cheaper and is what a full host uses. It isn’t used here, because this crate’s DWC2 driver cannot yet schedule high-speed periodic transfers reliably: on the bench that endpoint returned TransferError::FrameOverrun, and — worse — completed successfully with an all-zero bitmap while the hub’s own port status showed a connection change outstanding. A device attached to such a hub is then never seen at all. Reading the ports says the same thing over transfers that do work.

Linux makes the same arrangement from the other direction: its hub driver runs on that endpoint, but hub_activate() reads every port directly whenever a hub is initialized, resumed or reset, and ten consecutive endpoint errors force exactly that reset — so a hub whose endpoint misbehaves degrades into polling rather than going silent. Here polling is the floor; the endpoint becomes an optimization on top of it once the periodic scheduling is fixed, which changes nothing about what this method promises.

A port that now reports a device gets the same bring-up Self::enumerate gives one — reset, address, report — including being walked in turn if it is a hub with devices already on it. A port that has lost one reports Event::Detached for it, and for everything that was behind it if it was a hub.

Requires Self::enumerate to have run: with no hubs tracked there is nothing to read, and this returns Ok(()) having done nothing. It does not notice the root port itself being disconnected — on every board this targets the root hub is soldered on, so that is a condition this type doesn’t model.

A port whose device fails to come up is tried once and then left alone until the port’s connection changes again, so a device this can’t talk to costs one attempt rather than a port reset on every sweep for as long as it stays plugged in.

Auto Trait Implementations§

§

impl<'a> !RefUnwindSafe for Bus<'a>

§

impl<'a> !Send for Bus<'a>

§

impl<'a> !Sync for Bus<'a>

§

impl<'a> !UnwindSafe for Bus<'a>

§

impl<'a> Freeze for Bus<'a>

§

impl<'a> Unpin for Bus<'a>

§

impl<'a> UnsafeUnpin for Bus<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.