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
//! USB host support for the BCM2836/2837's built-in DWC2 OTG
//! controller, built up in stages rather than attempted as one unit:
//! host controller bring-up first, enumeration and a HID class driver
//! next (the smallest real thing that validates the controller
//! actually works end to end), Ethernet only once those are solid
//! (its own class driver plus a full TCP/IP stack on top, far more
//! surface area than HID). So far there's controller/root-port
//! bring-up ([`dwc2::Dwc2Host`](crate::usb::dwc2::Dwc2Host)); the
//! standard control transfers that enumerate a device (reading its
//! [device](crate::usb::control::get_device_descriptor) and
//! [configuration](crate::usb::control::get_configuration_descriptor)
//! descriptors, [SET_ADDRESS](crate::usb::control::set_address),
//! [SET_CONFIGURATION](crate::usb::control::set_configuration)); and the
//! hub-class requests that drive the on-board hub's downstream ports
//! ([hub descriptor](crate::usb::control::get_hub_descriptor),
//! [`set_port_power`](crate::usb::control::set_port_power),
//! [`get_port_status`](crate::usb::control::get_port_status),
//! [`set_port_reset`](crate::usb::control::set_port_reset)). A
//! device behind the hub can be reset and its descriptor read through
//! it — at high speed directly, or at full/low speed via split
//! transactions through the hub's transaction translator (see
//! [`dwc2::SplitTarget`](crate::usb::dwc2::SplitTarget)) — and
//! interrupt-IN endpoints can be polled
//! ([`dwc2::Channel::interrupt_in`](crate::usb::dwc2::Channel::interrupt_in)),
//! directly (the hub's own status-change endpoint) or through the hub
//! at full/low speed via periodic split scheduling (the report endpoint
//! of a HID keyboard on a physical port). [`enumerate`](crate::usb::enumerate) ties the whole
//! bring-up together — root-port reset, root-hub configuration, and
//! per-port reset/probe/address — and hands each downstream device to a
//! callback (see the `usb_enum`/`usb_hid_keyboard` examples). On top of
//! that sit the class drivers — HID ([`hid`](crate::usb::hid)), turning a
//! device's report endpoint into input events: a boot-protocol keyboard
//! and mouse, and a gamepad decoded through its own HID report descriptor
//! ([`hid::gamepad`](crate::usb::hid::gamepad)) — and the beginnings of the
//! on-board LAN9514
//! USB-Ethernet controller ([`lan9514`](crate::usb::lan9514)), so far
//! reaching its registers over vendor control transfers. Still missing:
//! recursion into hubs plugged into the root hub's ports (only one level
//! is walked today), bulk transfers (which Ethernet frame RX/TX needs),
//! and the rest of the LAN9514 driver and a network stack on top. Each is
//! real, separate work still to come, layered on top of
//! [`dwc2`](crate::usb::dwc2)/[`control`](crate::usb::control).
//!
//! On this project's target board (a Pi 2 Model B rev 1.1), the
//! on-board USB port(s) and the Ethernet jack are both wired through
//! an SMSC LAN9514 (a combined USB hub + 10/100 Ethernet controller)
//! sitting on this DWC2 controller's single root port. That means the
//! very first "device" this stack will ever see attached, on real
//! hardware, is that hub itself — reaching anything actually plugged
//! into a physical port (a keyboard, a mouse, the Ethernet path) needs
//! hub traversal, not just enumerating one device directly on the
//! root port.
//!
//! All of the above is confirmed working on real hardware: this stack
//! enumerates the on-board LAN9514 hub, powers and resets its
//! downstream ports, reaches through it to read the descriptors of
//! both the hub's own high-speed Ethernet function and a full-speed
//! keyboard on a physical port (the latter via split transactions),
//! polls the hub's interrupt status-change endpoint to detect a
//! device being unplugged/replugged, and reads live key presses from
//! that keyboard's interrupt report endpoint over a periodic split.
//! Getting the initial bring-up
//! working depended on one thing that no amount of DWC2 register tuning
//! could substitute for — the controller must be
//! powered on through the VideoCore mailbox first (see
//! [`dwc2`](crate::usb::dwc2)'s module doc); before that the register
//! block responds normally but no transaction ever runs.
/// USB protocol-level control transfers built on [`dwc2`] — see
/// [`control::get_device_descriptor`].
/// Typed views over the standard USB descriptors (device, configuration,
/// interface, endpoint) — see [`descriptor::DeviceDescriptor`].
/// Host-mode bring-up for the DWC2 core itself, plus the low-level
/// DMA-mode control-transfer channel primitives — see
/// [`dwc2::Dwc2Host`].
/// HID class drivers built on the enumeration above — boot-protocol
/// keyboard and mouse (see [`hid::keyboard::Keyboard`]) plus a
/// report-descriptor-driven gamepad (see [`hid::gamepad::Gamepad`]).
/// Driving a hub's downstream ports (bring-up, port reset, speed/split
/// detection) — see [`hub::Hub`].
/// The on-board LAN9514 USB-Ethernet controller — see
/// [`lan9514::Lan9514`].
use ControlFlow;
use crate;
use crateTimer;
use crateprobe_and_address;
use crateDeviceDescriptor;
use crate;
use crateHub;
/// Address assigned to the on-board root hub during [`enumerate`].
/// Downstream devices are addressed from here upward.
const ROOT_HUB_ADDRESS: u8 = 1;
/// Powers the USB host controller on through the VideoCore mailbox — the
/// mandatory first step before [`dwc2::Dwc2Host::init`], since the
/// firmware hands the core off only partially powered (see [`dwc2`]'s
/// module doc: without this the register block responds normally but no
/// transaction ever runs). Returns `true` once the controller is powered
/// and present, `false` if the mailbox call fails or the firmware reports
/// the controller absent.
/// What went wrong bringing up the bus or the root hub in [`enumerate`].
/// Failures enumerating an individual downstream port are *not* reported
/// this way — [`enumerate`] skips a port that misbehaves and carries on —
/// so this only covers the shared bring-up every device depends on.
/// A downstream device found and addressed by [`enumerate`], handed to
/// its per-device callback. Everything needed to talk to the device
/// further (read its configuration, configure it, poll its endpoints) is
/// here.
/// Enumerates every device behind the on-board root hub, calling
/// `on_device` once per successfully-addressed downstream device.
///
/// Given an already-[initialized](dwc2::Dwc2Host::init) controller with a
/// device connected on the root port (the caller having waited for
/// [`dwc2::Dwc2Host::port_connected`]), this resets the root port, brings
/// up and configures the root hub (the on-board SMSC LAN9514 on this
/// board — 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 invoking `on_device`. Addresses are
/// handed out sequentially starting just above the root hub's, so each
/// device is addressed before the next port is touched (two devices must
/// never sit at address 0 at once).
///
/// `on_device` receives the [`Channel`] enumeration ran on (to do
/// further transfers), the timer, and the [`Device`]; return
/// [`ControlFlow::Break`] from it to stop enumerating early (e.g. once
/// the device of interest is found), or [`ControlFlow::Continue`] to
/// keep going. A port whose reset or descriptor read fails is skipped
/// rather than aborting the whole enumeration; only a failure of the
/// shared root-hub bring-up returns an [`EnumerationError`].
///
/// The controller is borrowed immutably, so a callback that needs a
/// channel of its own — one to keep polling a device's endpoint after
/// enumeration finishes, say — can capture the same `&Dwc2Host` and
/// call [`Dwc2Host::alloc_channel`] on it. The channel passed in is
/// enumeration's own and goes away when this returns.
///
/// This drives a single level of hubs (the root hub's own ports); a hub
/// plugged into one of those ports is reported as a device but not itself
/// recursed into.