godspeed/driver/bus.rs
1// SPDX-License-Identifier: Apache-2.0
2//! `bus` - a device a driver reaches through the host of the bus it is on, not through registers of its own.
3//!
4//! A USB device has no register window and no DMA arena a driver could be granted: its host controller owns both,
5//! and talks to every device on its bus. So a USB device's driver is the host's CLIENT. The host binds the device
6//! (`xhci`, `dwc2` and `ehci` do, for one device each that they do not drive themselves), the supervisor starts the driver
7//! the device's VID:PID names with that host as its peer, and the driver asks the host for the device's transfers:
8//! who it is, control transfers on its default endpoint, and its bulk endpoints. That conversation is
9//! `godspeed_sdk::usbfn`; this module is it in the one shape a driver should use.
10//!
11//! **A bus, not a class.** Nothing here knows what the device is - a radio, a serial adapter, a sensor - any more
12//! than `registers` knows what a register does. It moves transfers, and the device's meaning is its driver's
13//! (`docs/driver-library.md`, "What belongs here").
14//!
15//! What a host answers is narrow on purpose: the one device it bound for this driver, and nothing else on the bus.
16
17use godspeed_sdk::{Message, ServiceContext};
18
19pub use godspeed_sdk::usbfn as wire;
20
21/// The hosts that bind a device for a driver of its own. A driver is given exactly one of them as its peer, so
22/// trying each in turn finds the one it was given: the one it holds a capability to, else the one it may reacquire
23/// by name (`UsbDevice::granted`).
24const HOSTS: [&str; 3] = ["xhci", "dwc2", "ehci"];
25/// The bound on one request to the host. A control transfer takes milliseconds; a host retrying a device that does
26/// not answer can use up this bound, so a request that is not answered in it means the device is not answering.
27const HOST_SECS: i64 = 2;
28/// How many of the host's notices may arrive in place of an answer before that is said rather than chased
29/// (`wire::OP_SYNC`).
30const SYNC_MAX: u32 = 8;
31
32/// Why a transfer did not happen.
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum UsbError {
35 /// The host has no device bound for this driver: unplugged, or not enumerated yet.
36 NoDevice,
37 /// The host tried, and the device or the bus did not complete it.
38 Failed,
39 /// The request was not one the host takes: too long, an endpoint the device does not have, or empty.
40 BadRequest,
41 /// The host itself did not answer, or answered something that was not this protocol.
42 Host(crate::Error),
43}
44
45impl UsbError {
46 /// A sentence for a log line.
47 pub fn as_str(&self) -> &'static str {
48 match self {
49 UsbError::NoDevice => "the host has no device bound for this driver",
50 UsbError::Failed => "the device or the bus did not complete the transfer",
51 UsbError::BadRequest => "the host refused the request as malformed",
52 UsbError::Host(e) => e.as_str(),
53 }
54 }
55}
56
57/// Where the host says the bound device is, from a host that says (`wire::INFO_WHERE_AT`): for a driver's
58/// report of its hardware, never to address it by - the host does that.
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub struct Place {
61 /// The root port it is on, from 1.
62 pub root: u8,
63 /// The hub port it is behind, or 0 when it is on the root port itself.
64 pub hub: u8,
65 /// The host's slot for it (`xhci`), or its USB address (`ehci`).
66 pub slot: u8,
67 /// Its bulk IN endpoint's address, or 0 when it has none; and that endpoint's packet size.
68 pub in_addr: u8,
69 pub in_mps: u16,
70 /// Its bulk OUT endpoints' addresses, in the order `bulk_out` counts them: the first `n_out`.
71 pub outs: [u8; 4],
72 pub n_out: u8,
73}
74
75/// Who the bound device is, as its host enumerated it.
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77pub struct Bound {
78 pub vid: u16,
79 pub pid: u16,
80 /// Which binding of it this is: the same device bound again (a replug) has a different one.
81 pub gen: u32,
82}
83
84/// The device this driver was started for, reached through its host.
85pub struct UsbDevice<'a> {
86 ctx: &'a ServiceContext,
87 host: &'static str,
88}
89
90impl<'a> UsbDevice<'a> {
91 /// The host this driver was given as its peer, or `None` when it was given none - it was not started for a
92 /// USB device. A host spawned after this driver is named but not yet wired, so one this driver may REACQUIRE by
93 /// name is taken next. The kernel mints a capability by name only for a peer this driver was spawned with (or for
94 /// a holder of `ACQUIRE_ANY`), so this reaches no host the driver was not given, and widens nothing.
95 pub fn granted(ctx: &'a ServiceContext) -> Option<UsbDevice<'a>> {
96 HOSTS.iter().copied().find(|h| crate::ipc::peer(ctx, h).is_some())
97 .or_else(|| HOSTS.iter().copied().find(|h| crate::cap::reacquire(ctx, h)))
98 .map(|host| UsbDevice { ctx, host })
99 }
100
101 /// The host's name, for a log line.
102 pub fn host(&self) -> &'static str {
103 self.host
104 }
105
106 /// Who the bound device is (`wire::OP_INFO`).
107 pub fn info(&self) -> Result<Bound, UsbError> {
108 let r = self.ask(&[wire::OP_INFO])?;
109 let p = r.payload_bytes();
110 status(p, wire::OP_INFO)?;
111 if p.len() < wire::INFO_GEN_AT + 4 {
112 return Err(UsbError::Host(crate::Error::Malformed));
113 }
114 Ok(Bound {
115 vid: u16::from_le_bytes([p[2], p[3]]),
116 pid: u16::from_le_bytes([p[4], p[5]]),
117 gen: u32::from_le_bytes([p[6], p[7], p[8], p[9]]),
118 })
119 }
120
121 /// Where the host says the device is, or `None` from a host that does not say, or did not answer.
122 pub fn place(&self) -> Option<Place> {
123 let r = self.ask(&[wire::OP_INFO]).ok()?;
124 let p = r.payload_bytes();
125 let w = wire::INFO_WHERE_AT;
126 if status(p, wire::OP_INFO).is_err() || p.len() < w + 7 {
127 return None;
128 }
129 let n_out = (p[w + 6] as usize).min(4).min(p.len() - (w + 7));
130 let mut outs = [0u8; 4];
131 outs[..n_out].copy_from_slice(&p[w + 7..w + 7 + n_out]);
132 Some(Place {
133 root: p[w], hub: p[w + 1], slot: p[w + 2], in_addr: p[w + 3],
134 in_mps: u16::from_le_bytes([p[w + 4], p[w + 5]]), outs, n_out: n_out as u8,
135 })
136 }
137
138 /// One control transfer IN on the default endpoint: `setup` is the 8-byte setup packet as USB defines it, and
139 /// what the device returned is copied into `out`. How many bytes it returned.
140 pub fn control_in(&self, setup: [u8; 8], out: &mut [u8]) -> Result<usize, UsbError> {
141 let mut req = [0u8; 9];
142 req[0] = wire::OP_CONTROL;
143 req[1..9].copy_from_slice(&setup);
144 let r = self.ask(&req)?;
145 let p = r.payload_bytes();
146 status(p, wire::OP_CONTROL)?;
147 let n = (p.len() - 2).min(out.len());
148 out[..n].copy_from_slice(&p[2..2 + n]);
149 Ok(n)
150 }
151
152 /// One control transfer OUT on the default endpoint, with `data` as its data stage (at most
153 /// `wire::CONTROL_MAX` bytes; the setup packet's length field is the caller's to set to match).
154 pub fn control_out(&self, setup: [u8; 8], data: &[u8]) -> Result<(), UsbError> {
155 self.control_out_as(wire::OP_CONTROL, setup, data)
156 }
157
158 /// `control_out`, attempted exactly once: the host does not retry it. For a write whose effect a second attempt
159 /// would repeat - a device that acts on the first copy and NAKs the rest (`wire::OP_CONTROL_ONCE`).
160 pub fn control_out_once(&self, setup: [u8; 8], data: &[u8]) -> Result<(), UsbError> {
161 self.control_out_as(wire::OP_CONTROL_ONCE, setup, data)
162 }
163
164 fn control_out_as(&self, op: u8, setup: [u8; 8], data: &[u8]) -> Result<(), UsbError> {
165 if data.len() > wire::CONTROL_MAX {
166 return Err(UsbError::BadRequest);
167 }
168 let mut req = [0u8; 9 + wire::CONTROL_MAX];
169 req[0] = op;
170 req[1..9].copy_from_slice(&setup);
171 req[9..9 + data.len()].copy_from_slice(data);
172 let r = self.ask(&req[..9 + data.len()])?;
173 status(r.payload_bytes(), op)
174 }
175
176 /// Ask the host to reset the device's port and bind it afresh, as unplugging it would (`wire::OP_REPLUG`): for a
177 /// device that has stopped answering. On `Ok` the host reports the device gone and then bound again, so this
178 /// driver is stopped and a fresh one started. `Failed` is a host that has reset it as often as it may;
179 /// `BadRequest` one that cannot reset a port (`xhci`, `ehci`).
180 pub fn replug(&self) -> Result<(), UsbError> {
181 let r = self.ask(&[wire::OP_REPLUG])?;
182 status(r.payload_bytes(), wire::OP_REPLUG)
183 }
184
185 /// One bulk OUT transfer to the device's `out`th bulk OUT endpoint, counted in the order its configuration
186 /// descriptor lists them, from 0. At most `wire::BULK_OUT_MAX` bytes.
187 pub fn bulk_out(&self, out: u8, data: &[u8]) -> Result<(), UsbError> {
188 if data.is_empty() || data.len() > wire::BULK_OUT_MAX {
189 return Err(UsbError::BadRequest);
190 }
191 let mut req = [0u8; 2 + wire::BULK_OUT_MAX];
192 req[0] = wire::OP_BULK_OUT;
193 req[1] = out;
194 req[2..2 + data.len()].copy_from_slice(data);
195 let r = self.ask(&req[..2 + data.len()])?;
196 status(r.payload_bytes(), wire::OP_BULK_OUT)
197 }
198
199 /// The bulk IN transfer the host holds from the device, copied into `out`: how many bytes, 0 when it holds
200 /// none. The host keeps one IN armed and arms it again as each is collected, so the first call is what starts
201 /// receiving; it sends `wire::NOTE_BULK_IN` when one is held, for a driver that waits rather than asks (`xhci`,
202 /// `dwc2`). `ehci` holds nothing and sends no notice: each call waits up to 20 ms for the device and returns what
203 /// came, so a driver there asks again rather than waiting to be told.
204 pub fn bulk_in(&self, out: &mut [u8]) -> Result<usize, UsbError> {
205 let r = self.ask(&[wire::OP_BULK_IN])?;
206 let p = r.payload_bytes();
207 status(p, wire::OP_BULK_IN)?;
208 let n = (p.len() - 2).min(out.len());
209 out[..n].copy_from_slice(&p[2..2 + n]);
210 Ok(n)
211 }
212
213 /// One request, bounded. A notice the host sent in place of the answer is named back with `OP_SYNC`, so the
214 /// answer still on its way is the one returned and nothing is left owed (`wire::OP_SYNC`).
215 #[inline(never)]
216 fn ask(&self, body: &[u8]) -> Result<Message, UsbError> {
217 let ask = |m: &[u8]| crate::call::request_within(self.ctx, self.host, &Message::from_bytes(m), HOST_SECS)
218 .map_err(UsbError::Host);
219 let mut r = ask(body)?;
220 let mut syncs = 0u32;
221 while let Some(¬e) = r.payload_bytes().first().filter(|&&b| b == wire::NOTE_BULK_IN || b == wire::NOTE_RADIO) {
222 if syncs == SYNC_MAX {
223 return Err(UsbError::Host(crate::Error::Malformed));
224 }
225 syncs += 1;
226 r = ask(&[wire::OP_SYNC, note])?;
227 }
228 Ok(r)
229 }
230}
231
232/// The host's status for `op`, as an error where it is not `ST_OK`.
233fn status(p: &[u8], op: u8) -> Result<(), UsbError> {
234 match (p.first().copied(), p.get(1).copied()) {
235 (Some(o), Some(wire::ST_OK)) if o == op => Ok(()),
236 (Some(o), Some(wire::ST_NO_DEVICE)) if o == op => Err(UsbError::NoDevice),
237 (Some(o), Some(wire::ST_FAILED)) if o == op => Err(UsbError::Failed),
238 (Some(o), Some(wire::ST_BAD_REQUEST)) if o == op => Err(UsbError::BadRequest),
239 _ => Err(UsbError::Host(crate::Error::Malformed)),
240 }
241}