1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#![no_std]
#![no_main]
// Enumerates every USB device on the board and prints the bus as a tree.
// Powers the USB controller on through the VideoCore mailbox, brings up
// the DWC2 core, then hands the whole enumeration off to
// `rpi_hal::usb::enumerate` -- which resets the root port, configures the
// hub it finds there, and reaches through it to reset and address each
// connected downstream device (high-speed directly, full/low-speed via
// split transactions), descending into any of those that is itself a hub.
// Each line is indented by how many hubs deep the device sits, and prints
// the transaction translator its transfers are routed through, which for
// a device below a full-speed hub is not the hub it is plugged into but
// the high-speed one further up. See usb_hid_keyboard.rs for an example
// that talks to a device it finds rather than only describing it.
//
// Then the raw port dump, which is what makes an empty tree readable. A
// port with nothing on it and a port whose device failed to come up look
// identical from the tree -- both are simply absent from it -- and the
// difference is in the status word and the hub descriptor's
// `DeviceRemovable` bits, which say which ports have a device soldered to
// them and so can never legitimately read as empty.
//
// Pi 2/3 only. Every device here hangs off a soldered-on hub behind the
// DWC2 controller: one chip on a Pi 2/3B (the SMSC LAN9514, hub and
// Ethernet in one), two cascaded hubs on a 3B+. A Pi 4 has neither: its
// USB host is a VL805 xHCI behind PCIe, which this crate doesn't drive
// yet, so this builds for `bcm2711` and then finds an empty root port.
use core::fmt::Write;
use core::ops::ControlFlow;
use rpi_hal::halt;
use rpi_hal::mailbox::Mailbox;
use rpi_hal::pac;
use rpi_hal::timer::Timer;
use rpi_hal::uart::Uart;
use rpi_hal::usb;
use rpi_hal::usb::control::{get_device_descriptor, get_hub_descriptor, get_port_status};
use rpi_hal::usb::descriptor::DeviceDescriptor;
use rpi_hal::usb::dwc2::{Channel, ControlEndpoint, Dwc2Host};
use rpi_hal::usb::{Bus, Event};
/// How many hubs the port dump keeps track of. Four covers the board's
/// own hub(s) plus one or two plugged into them, which is what the dump
/// is for.
const MAX_HUBS: usize = 4;
/// How long to wait between `Bus::poll` sweeps. A quarter-second is well
/// inside what a person notices when plugging something in, and a sweep
/// is one control transfer per port on the bus.
const POLL_INTERVAL_MS: u32 = 250;
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
let peripherals = unsafe { pac::Peripherals::steal() };
let mut uart = Uart::init(&peripherals.GPIO, peripherals.UART0);
let _ = writeln!(uart, "PANIC: {info}");
halt();
}
#[no_mangle]
pub extern "C" fn kmain() -> ! {
let peripherals = unsafe { pac::Peripherals::steal() };
let mut uart = Uart::init(&peripherals.GPIO, peripherals.UART0);
let timer = Timer::new(peripherals.SYSTMR);
// The USB controller comes up only partially powered from firmware;
// power it fully via the mailbox before touching DWC2.
let mut mailbox = Mailbox::new(peripherals.VCMAILBOX);
if !usb::power_on(&mut mailbox) {
let _ = writeln!(uart, "USB power-on failed");
halt();
}
let dwc2 = Dwc2Host::init(
peripherals.USB_OTG_GLOBAL,
peripherals.USB_OTG_HOST,
peripherals.USB_OTG_PWRCLK,
&timer,
);
// Bounded, because the hub is soldered on: a root port that hasn't
// reported in five seconds has nothing behind it at all, which is
// what a Pi 4 looks like (see the header). Falling through rather
// than halting here -- `usb::enumerate` reports that as
// `EnumerationError::NotConnected` through the same error path
// everything else goes through, where an unbounded wait would sit
// here silently and look like a lock-up.
let _ = writeln!(uart, "waiting for the on-board hub...");
let deadline_us = timer.now_micros() + 5_000_000;
while !dwc2.port_connected() && timer.now_micros() < deadline_us {
timer.delay_ms(100);
}
let mut count = 0u32;
let mut hubs = [None::<ControlEndpoint>; MAX_HUBS];
let mut hub_count = 0usize;
let mut bus = Bus::new(&dwc2);
let result = bus.enumerate(&timer, |_channel, _timer, event| {
if let Event::Attached(device) = event {
count += 1;
if device.descriptor.device_class == 9 && hub_count < hubs.len() {
hubs[hub_count] = Some(device.endpoint);
hub_count += 1;
}
}
report(&mut uart, event);
ControlFlow::Continue(())
});
match result {
Ok(()) => {
let _ = writeln!(uart, "enumeration complete: {count} device(s)");
}
Err(e) => {
let _ = writeln!(uart, "enumeration failed: {e:?}");
}
}
// Then every hub's ports again, raw. Enumeration deliberately skips a
// downstream port it can't bring up rather than failing the whole
// walk, so a hub that reports devices the tree above doesn't leaves
// no trace of itself otherwise -- and a hub that answers nothing at
// all here says the problem is the hub, not its ports. Both of those
// are invisible in the tree, which only ever shows what succeeded.
let mut root = None;
if let Some(mut channel) = dwc2.alloc_channel() {
// The root hub first, which the tree above never shows: enumeration
// configures it rather than reporting it, so it is the one hub on
// the board whose ports nothing has printed. Address 1 is where
// enumeration puts it, and it is high speed on this board, so its
// endpoint can be rebuilt here from the device descriptor alone.
let probe = ControlEndpoint {
address: 1,
low_speed: false,
max_packet_size: 8,
split: None,
};
match get_device_descriptor(&mut channel, &timer, probe) {
Ok(bytes) => {
let descriptor = DeviceDescriptor::from_bytes(&bytes);
let _ = writeln!(
uart,
"root hub: {:04x}:{:04x}",
descriptor.vendor_id, descriptor.product_id
);
root = Some(ControlEndpoint {
max_packet_size: descriptor.max_packet_size0 as u16,
..probe
});
}
Err(e) => {
let _ = writeln!(uart, "root hub: device descriptor read failed: {e:?}");
}
}
for hub in root.iter().chain(hubs.iter().flatten()) {
dump_hub_ports(&mut channel, &timer, &mut uart, *hub);
}
}
// And then keep watching. The walk above is a snapshot, and a bus is
// not a still thing: devices are plugged and pulled, and some that are
// soldered on take seconds to announce themselves and so are simply
// absent from any snapshot taken at boot. `Bus::poll` rereads every
// hub's ports and reports what has changed since the last look.
let _ = writeln!(uart, "watching for changes...");
loop {
let result = bus.poll(&timer, |_channel, _timer, event| {
report(&mut uart, event);
ControlFlow::Continue(())
});
// Printed and carried on with rather than fatal: a hub that fails
// one sweep may answer the next, and stopping here would throw
// away the rest of the bus over one of them.
if let Err(e) = result {
let _ = writeln!(uart, "poll failed: {e:?}");
}
timer.delay_ms(POLL_INTERVAL_MS);
}
}
/// Prints one bus event: an attached device as a line of the tree,
/// indented by how many hubs deep it sits, or a departure as the address
/// that has gone.
fn report(uart: &mut Uart, event: Event) {
let device = match event {
Event::Attached(device) => device,
Event::Detached {
hub_address,
port,
address,
} => {
let _ = writeln!(
uart,
"hub {hub_address} port {port}: address {address} gone"
);
return;
}
};
// One level of indent per hub below the root hub, so the bus reads as
// the tree it is. `depth` is bounded by USB's own five-hub limit, so
// this can't run away.
for _ in 0..device.depth {
let _ = write!(uart, " ");
}
let _ = write!(
uart,
"hub {} port {}: {:04x}:{:04x} class={} -> address {} ({})",
device.hub_address,
device.port,
device.descriptor.vendor_id,
device.descriptor.product_id,
device.descriptor.device_class,
device.endpoint.address,
// The endpoint records only whether the device is low speed, since
// that is the one bit a transfer puts on the wire -- but the split
// target separates the other two, because a device needs a
// transaction translator exactly when it is slower than the
// high-speed bus above it. The one case this reads wrong is a
// full-speed device on a full-speed root port, which needs no
// translator either; the board's root hub enumerates at high
// speed, so that doesn't arise here.
if device.endpoint.low_speed {
"low speed"
} else if device.endpoint.split.is_some() {
"full speed"
} else {
"high speed"
},
);
match device.endpoint.split {
// Whose translator this is, rather than just "split": for a device
// more than one hub down these are what show the routing, since
// the translator is the nearest high-speed hub above the device
// and not necessarily its parent.
Some(split) => {
let _ = writeln!(
uart,
", split through hub {} port {}",
split.hub_address, split.port
);
}
None => {
let _ = writeln!(uart, ", direct");
}
}
}
/// Prints `hub`'s class descriptor and the raw status of every one of its
/// downstream ports.
///
/// The whole descriptor goes out as bytes rather than just the port count
/// because the two fields that decide whether an empty-looking port is
/// really empty are in there: `bPwrOn2PwrGood` (byte 5, in 2ms units) says
/// how long the hub needs after powering a port, and `DeviceRemovable`
/// (from byte 7, one bit per port) says which ports have a device soldered
/// to them and so can never legitimately read as unoccupied.
fn dump_hub_ports(channel: &mut Channel, timer: &Timer, uart: &mut Uart, hub: ControlEndpoint) {
let mut descriptor = [0u8; 16];
let len = match get_hub_descriptor(channel, timer, hub, &mut descriptor) {
Ok(len) => len,
Err(e) => {
let _ = writeln!(uart, "hub {}: descriptor read failed: {e:?}", hub.address);
return;
}
};
// bNbrPorts is byte 2 of the hub class descriptor.
if len < 6 {
let _ = writeln!(uart, "hub {}: short descriptor ({len} bytes)", hub.address);
return;
}
let _ = write!(
uart,
"hub {}: {} port(s), descriptor",
hub.address, descriptor[2]
);
for byte in &descriptor[..len] {
let _ = write!(uart, " {byte:02x}");
}
let _ = writeln!(uart);
for port in 1..=descriptor[2] {
match get_port_status(channel, timer, hub, port) {
Ok((status, change)) => {
let _ = writeln!(
uart,
" port {port}: status {status:04x} change {change:04x}"
);
}
Err(e) => {
let _ = writeln!(uart, " port {port}: status read failed: {e:?}");
}
}
}
}