pub struct Bus<'a> { /* private fields */ }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>
impl<'a> Bus<'a>
Sourcepub fn new(dwc2: &'a Dwc2Host) -> Self
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.
Sourcepub fn enumerate<F>(
&mut self,
timer: &Timer,
on_event: F,
) -> Result<(), EnumerationError>
pub fn enumerate<F>( &mut self, timer: &Timer, on_event: F, ) -> Result<(), EnumerationError>
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.
Sourcepub fn poll<F>(
&mut self,
timer: &Timer,
on_event: F,
) -> Result<(), EnumerationError>
pub fn poll<F>( &mut self, timer: &Timer, on_event: F, ) -> Result<(), EnumerationError>
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.