usb/device/
api.rs

1// SPDX-FileCopyrightText: 2024 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#[cfg(keyos)]
5pub use atsama5d27::udphs::{EndpointDirection, EndpointType};
6use server::{CheckedConn, CheckedPermissions, MessageAllowed, MessageId as _};
7
8use super::messages::*;
9#[cfg(all(doc, not(keyos)))]
10pub use super::messages::{EndpointDirection, EndpointType};
11use crate::error::UsbError;
12
13#[macro_export]
14macro_rules! use_device_api {
15    () => {
16        mod usb_device_permissions {
17            use $crate::device::messages::*;
18            #[derive(Clone, Default, server::Permissions)]
19            #[server_name = "os/usbdev"]
20            pub struct UsbDevicePermissions;
21        }
22        type UsbDeviceEmulation =
23            $crate::device::api::UsbDeviceEmulation<usb_device_permissions::UsbDevicePermissions>;
24        type UsbEmulatedEndpoint =
25            $crate::device::api::UsbEmulatedEndpoint<usb_device_permissions::UsbDevicePermissions>;
26        type UsbInterfaceConfig<'a, const N: usize> = $crate::device::api::UsbInterfaceConfig<'a, N>;
27        type UsbRegisteredInterface =
28            $crate::device::api::UsbRegisteredInterface<usb_device_permissions::UsbDevicePermissions>;
29    };
30}
31
32#[derive(Default)]
33pub struct UsbDeviceEmulation<P: CheckedPermissions>(CheckedConn<P>);
34
35pub struct UsbInterfaceConfig<'a, const N: usize> {
36    interface_number: u8,
37    if_class: u8,
38    if_subclass: u8,
39    if_protocol: u8,
40    endpoints: &'a [EndpointProperties; N],
41    interface_functional_descriptors: &'a [u8],
42    associated_interface_count: u8,
43    capabilities: Vec<DeviceCapability>,
44    setup_responder: Option<Box<dyn FnOnce(xous::PID) -> Result<xous::CID, UsbError>>>,
45}
46
47impl<'a, const N: usize> UsbInterfaceConfig<'a, N> {
48    pub fn new(
49        interface_number: u8,
50        if_class: u8,
51        if_subclass: u8,
52        if_protocol: u8,
53        endpoints: &'a [EndpointProperties; N],
54    ) -> Self {
55        Self {
56            interface_number,
57            if_class,
58            if_subclass,
59            if_protocol,
60            endpoints,
61            interface_functional_descriptors: &[],
62            associated_interface_count: 0,
63            capabilities: Vec::new(),
64            setup_responder: None,
65        }
66    }
67
68    pub fn with_functional_descriptors(mut self, descriptors: &'a [u8]) -> Self {
69        self.interface_functional_descriptors = descriptors;
70        self
71    }
72
73    pub fn with_associated_interface_count(mut self, count: u8) -> Self {
74        self.associated_interface_count = count;
75        self
76    }
77
78    pub fn with_capability(
79        mut self,
80        cap_type: u8,
81        cap_subtype: u8,
82        cap_uuid: uuid::Uuid,
83        capability_functional_descriptors: &[u8],
84    ) -> Self {
85        self.capabilities.push(DeviceCapability {
86            cap_type,
87            cap_subtype,
88            cap_uuid: cap_uuid.to_bytes_le().to_vec(),
89            capability_functional_descriptors: capability_functional_descriptors.into(),
90        });
91        self
92    }
93
94    pub fn with_setup_responder<S>(mut self, setup_responder: Option<S>) -> Self
95    where
96        S: server::Server + server::BlockingArchiveHandler<SetupPacketCallback> + Send + 'static,
97    {
98        if let Some(setup_responder) = setup_responder {
99            self.setup_responder = Some(Box::new(move |pid| {
100                let cid = server::listen_and_connect(setup_responder, pid);
101                xous::allow_messages_on_connection(
102                    pid,
103                    cid,
104                    SetupPacketCallback::ID..(SetupPacketCallback::ID + 1),
105                )?;
106                Ok(cid)
107            }));
108        }
109        self
110    }
111}
112
113impl<P: CheckedPermissions> UsbDeviceEmulation<P> {
114    /// Register a disabled interface driver with explicit runtime visibility control.
115    /// Returns a stable interface handle and the allocated endpoints.
116    /// Call [`UsbRegisteredInterface::set_enabled`] after local setup is ready.
117    pub fn register_interface<const N: usize>(
118        &mut self,
119        config: UsbInterfaceConfig<'_, N>,
120    ) -> Result<(UsbRegisteredInterface<P>, [UsbEmulatedEndpoint<P>; N]), UsbError>
121    where
122        P: MessageAllowed<RegisterInterface>,
123    {
124        let setup_responder = if let Some(connect) = config.setup_responder {
125            Some(connect(self.0.get_remote_pid())?)
126        } else {
127            None
128        };
129        let registered = self.0.send_blocking_archive(RegisterInterface {
130            interface_number: config.interface_number,
131            if_class: config.if_class,
132            if_subclass: config.if_subclass,
133            if_protocol: config.if_protocol,
134            endpoints: config.endpoints.into(),
135            interface_functional_descriptors: config.interface_functional_descriptors.into(),
136            associated_interface_count: config.associated_interface_count,
137            capabilities: config.capabilities,
138            setup_responder,
139        })?;
140        let interface =
141            UsbRegisteredInterface { connection: self.0.clone(), interface_number: config.interface_number };
142        let endpoints = core::array::from_fn(|i| UsbEmulatedEndpoint {
143            connection: self.0.clone(),
144            endpoint_number: registered.endpoints[i],
145        });
146        Ok((interface, endpoints))
147    }
148
149    /// Wait until the device is configured by the host
150    pub fn wait_for_connection(&self) -> Result<(), UsbError>
151    where
152        P: MessageAllowed<WaitForConnection>,
153    {
154        self.0.try_send_blocking_scalar(WaitForConnection)?;
155        Ok(())
156    }
157
158    pub fn is_enabled(&self) -> Result<bool, UsbError>
159    where
160        P: MessageAllowed<IsDeviceEmulationEnabled>,
161    {
162        Ok(self.0.try_send_blocking_scalar(IsDeviceEmulationEnabled)?)
163    }
164
165    pub fn is_connected(&self) -> Result<bool, UsbError>
166    where
167        P: MessageAllowed<IsDeviceEmulationConnected>,
168    {
169        Ok(self.0.try_send_blocking_scalar(IsDeviceEmulationConnected)?)
170    }
171
172    /// Returns true if the USB cable is connected (VBUS has power)
173    pub fn is_cable_connected(&self) -> Result<bool, UsbError>
174    where
175        P: MessageAllowed<IsCableConnected>,
176    {
177        Ok(self.0.try_send_blocking_scalar(IsCableConnected)?)
178    }
179
180    /// Returns true if in USB device mode (not acting as USB host via OTG)
181    pub fn is_device_mode(&self) -> Result<bool, UsbError>
182    where
183        P: MessageAllowed<IsDeviceMode>,
184    {
185        Ok(self.0.try_send_blocking_scalar(IsDeviceMode)?)
186    }
187
188    pub fn set_custom_vid_pid(&mut self, vid: Option<u16>, pid: Option<u16>)
189    where
190        P: MessageAllowed<SetVidPid>,
191    {
192        self.0.try_send_blocking_scalar(SetVidPid { vid, pid }).unwrap().unwrap();
193    }
194
195    pub fn reset_controller(&mut self)
196    where
197        P: MessageAllowed<ResetController>,
198    {
199        self.0.try_send_blocking_scalar(ResetController).unwrap().unwrap()
200    }
201}
202
203#[derive(Clone)]
204pub struct UsbRegisteredInterface<P: CheckedPermissions> {
205    connection: CheckedConn<P>,
206    interface_number: u8,
207}
208
209impl<P: CheckedPermissions> UsbRegisteredInterface<P> {
210    pub fn number(&self) -> u8 { self.interface_number }
211
212    pub fn set_enabled(&self, enabled: bool) -> Result<(), UsbError>
213    where
214        P: MessageAllowed<SetInterfaceEnabled>,
215    {
216        self.connection.try_send_blocking_scalar(SetInterfaceEnabled {
217            interface_number: self.interface_number,
218            enabled,
219        })??;
220        Ok(())
221    }
222}
223
224pub struct UsbEmulatedEndpoint<P: CheckedPermissions> {
225    connection: CheckedConn<P>,
226    endpoint_number: u8,
227}
228
229impl<P: CheckedPermissions> UsbEmulatedEndpoint<P> {
230    /// The endpoint number without the 0x80 (IN/OUT marker) bit
231    pub fn endpoint_number(&self) -> u8 { self.endpoint_number }
232
233    /// Received data from the host (OUT transaction and endpoint)
234    /// Returns the actual number of bytes received
235    pub fn read_buf(&mut self, buf: xous::MemoryRange, length: u16) -> Result<usize, UsbError>
236    where
237        P: MessageAllowed<ReadEndpoint>,
238    {
239        self.connection.lend_mut(ReadEndpoint { buf, endpoint: self.endpoint_number, length })
240    }
241
242    /// Transmit data to the host (IN transaction and endpoint).
243    ///
244    /// The USB server handles DMA chunking internally for transfers larger
245    /// than one DMA descriptor (64 KB).
246    ///
247    /// **`buf` must be backed by physically contiguous pages** (allocate with
248    /// `MemoryFlags::POPULATE`). The DMA controller reads from a single
249    /// physical start address; non-contiguous pages cause data corruption.
250    pub fn write_buf(&mut self, buf: xous::MemoryRange, length: usize) -> Result<usize, UsbError>
251    where
252        P: MessageAllowed<WriteEndpoint>,
253    {
254        self.connection.lend_mut(WriteEndpoint { buf, endpoint: self.endpoint_number, length, zlp: false })
255    }
256
257    /// Like [`write_buf`](Self::write_buf) but sends a ZLP after the transfer
258    /// if the total length is an exact multiple of `max_packet_len`, so the
259    /// host sees a proper USB transfer boundary.
260    ///
261    /// See [`write_buf`](Self::write_buf) for the physical contiguity requirement.
262    pub fn write_buf_zlp(&mut self, buf: xous::MemoryRange, length: usize) -> Result<usize, UsbError>
263    where
264        P: MessageAllowed<WriteEndpoint>,
265    {
266        self.connection.lend_mut(WriteEndpoint { buf, endpoint: self.endpoint_number, length, zlp: true })
267    }
268
269    /// Set or unset the stalled (a.k.a. halted) state on the endpoint
270    pub fn set_stalled(&mut self, stalled: bool)
271    where
272        P: MessageAllowed<SetEndpointStalled>,
273    {
274        self.connection
275            .try_send_scalar(SetEndpointStalled { endpoint: self.endpoint_number, stalled })
276            .unwrap();
277    }
278}