Skip to main content

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(&note) = 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}