1#![doc = include_str!("../README.md")]
2#![deny(unsafe_code)]
3#![warn(missing_docs)]
4
5#[cfg(any(feature = "host", feature = "device", feature = "web"))]
6use std::io::{Error, ErrorKind};
7use std::time::Duration;
8
9#[cfg(feature = "device")]
10pub mod device;
11
12#[cfg(any(feature = "host", feature = "web"))]
13pub mod host;
14
15mod trace;
16
17pub const INFO_SIZE: usize = 4096;
19
20pub const MAX_SIZE: usize = 16_777_216;
22
23#[doc(hidden)]
29pub const TRANSFER_PACKETS: usize = 128;
30
31#[allow(dead_code)]
33const QUEUE_LEN: usize = 32;
34
35#[allow(dead_code)]
37const FLUSH_TIMEOUT: Duration = Duration::from_millis(100);
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct Class {
42 pub class: u8,
44 pub sub_class: u8,
46 pub protocol: u8,
48}
49
50impl Class {
51 pub const VENDOR_SPECIFIC: u8 = 0xff;
53
54 pub const fn new(class: u8, sub_class: u8, protocol: u8) -> Self {
56 Self { class, sub_class, protocol }
57 }
58
59 pub const fn vendor_specific(sub_class: u8, protocol: u8) -> Self {
61 Self::new(Self::VENDOR_SPECIFIC, sub_class, protocol)
62 }
63}
64
65#[cfg(feature = "device")]
66impl From<Class> for usb_gadget::Class {
67 fn from(Class { class, sub_class, protocol }: Class) -> Self {
68 usb_gadget::Class { class, sub_class, protocol }
69 }
70}
71
72#[allow(dead_code)]
74mod ctrl_req {
75 pub const PROBE: u8 = 0;
77 pub const OPEN: u8 = 1;
79 pub const CLOSE: u8 = 2;
81 pub const INFO: u8 = 3;
83 pub const CLOSE_SEND: u8 = 4;
85 pub const CLOSE_RECV: u8 = 5;
87 pub const STATUS: u8 = 6;
89 pub const CAPABILITIES: u8 = 7;
91 pub const ECHO: u8 = 8;
93
94 pub const PROBE_RESPONSE: &[u8] = b"UPC";
96}
97
98#[allow(dead_code)]
100mod status {
101 pub const RECV_CLOSED: u8 = 1;
103 pub const MAX_SIZE: usize = 8;
105}
106
107#[allow(dead_code)]
109mod tlv {
110 use std::io::{Error, ErrorKind};
111
112 pub fn encode(buf: &mut Vec<u8>, tag: u8, value: &[u8]) {
114 buf.push(tag);
115 buf.extend_from_slice(&(value.len() as u16).to_le_bytes());
116 buf.extend_from_slice(value);
117 }
118
119 pub fn decode(data: &[u8]) -> std::io::Result<Vec<(u8, &[u8])>> {
124 let mut entries = Vec::new();
125 let mut pos = 0;
126 while pos < data.len() {
127 if pos + 3 > data.len() {
128 return Err(Error::new(ErrorKind::InvalidData, "capabilities data truncated"));
129 }
130 let tag = data[pos];
131 let len = u16::from_le_bytes([data[pos + 1], data[pos + 2]]) as usize;
132 pos += 3;
133 if pos + len > data.len() {
134 return Err(Error::new(ErrorKind::InvalidData, "capabilities data truncated"));
135 }
136 entries.push((tag, &data[pos..pos + len]));
137 pos += len;
138 }
139 Ok(entries)
140 }
141}
142
143#[cfg(any(feature = "host", feature = "device", feature = "web"))]
149#[derive(Debug, Clone, PartialEq, Eq)]
150pub(crate) struct DeviceCapabilities {
151 pub ping_timeout: Option<Duration>,
153 pub status_supported: bool,
155 pub echo_supported: bool,
157 pub max_size: u64,
159}
160
161#[cfg(any(feature = "host", feature = "device", feature = "web"))]
162impl Default for DeviceCapabilities {
163 fn default() -> Self {
164 Self { ping_timeout: None, status_supported: false, echo_supported: false, max_size: MAX_SIZE as u64 }
165 }
166}
167
168#[cfg(any(feature = "host", feature = "device", feature = "web"))]
169impl DeviceCapabilities {
170 #[cfg(any(feature = "host", feature = "web"))]
172 const SIZE: usize = 256;
173
174 const TAG_PING_TIMEOUT: u8 = 0x01;
176 const TAG_STATUS_SUPPORTED: u8 = 0x02;
178 const TAG_MAX_PACKET_SIZE: u8 = 0x03;
180 const TAG_ECHO_SUPPORTED: u8 = 0x04;
182
183 #[cfg(feature = "device")]
185 pub fn encode(&self) -> Vec<u8> {
186 let mut buf = Vec::new();
187
188 let millis: u32 = self.ping_timeout.map_or(0, |d| d.as_millis().try_into().unwrap_or(u32::MAX));
190 tlv::encode(&mut buf, Self::TAG_PING_TIMEOUT, &millis.to_le_bytes());
191
192 tlv::encode(&mut buf, Self::TAG_STATUS_SUPPORTED, &[u8::from(self.status_supported)]);
194
195 tlv::encode(&mut buf, Self::TAG_MAX_PACKET_SIZE, &self.max_size.to_le_bytes());
197
198 tlv::encode(&mut buf, Self::TAG_ECHO_SUPPORTED, &[u8::from(self.echo_supported)]);
200
201 buf
202 }
203
204 #[cfg(any(feature = "host", feature = "web"))]
208 pub fn decode(data: &[u8]) -> std::io::Result<Self> {
209 let mut caps = Self::default();
210 for (tag, value) in tlv::decode(data)? {
211 match tag {
212 Self::TAG_PING_TIMEOUT => {
213 if value.len() >= 4 {
214 let millis = u32::from_le_bytes([value[0], value[1], value[2], value[3]]);
215 caps.ping_timeout =
216 if millis == 0 { None } else { Some(Duration::from_millis(millis.into())) };
217 }
218 }
219
220 Self::TAG_STATUS_SUPPORTED => {
221 if !value.is_empty() {
222 caps.status_supported = value[0] != 0;
223 }
224 }
225
226 Self::TAG_MAX_PACKET_SIZE => {
227 if value.len() >= 8 {
228 let size = u64::from_le_bytes([
229 value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7],
230 ]);
231 caps.max_size = size;
232 }
233 }
234
235 Self::TAG_ECHO_SUPPORTED => {
236 if !value.is_empty() {
237 caps.echo_supported = value[0] != 0;
238 }
239 }
240
241 _ => { }
242 }
243 }
244 Ok(caps)
245 }
246}
247
248#[cfg(any(feature = "host", feature = "device", feature = "web"))]
254#[derive(Debug, Clone, PartialEq, Eq)]
255pub(crate) struct HostCapabilities {
256 pub max_size: u64,
258}
259
260#[cfg(any(feature = "host", feature = "device", feature = "web"))]
261impl Default for HostCapabilities {
262 fn default() -> Self {
263 Self { max_size: MAX_SIZE as u64 }
264 }
265}
266
267#[cfg(any(feature = "host", feature = "device", feature = "web"))]
268impl HostCapabilities {
269 #[allow(dead_code)]
271 #[cfg(feature = "device")]
272 const SIZE: usize = 256;
273
274 const TAG_MAX_PACKET_SIZE: u8 = 0x03;
276
277 #[cfg(any(feature = "host", feature = "web"))]
279 pub fn encode(&self) -> Vec<u8> {
280 let mut buf = Vec::new();
281 tlv::encode(&mut buf, Self::TAG_MAX_PACKET_SIZE, &self.max_size.to_le_bytes());
282 buf
283 }
284
285 #[cfg(feature = "device")]
289 pub fn decode(data: &[u8]) -> std::io::Result<Self> {
290 let mut caps = Self::default();
291 for (tag, value) in tlv::decode(data)? {
292 match tag {
293 Self::TAG_MAX_PACKET_SIZE => {
294 if value.len() >= 8 {
295 let size = u64::from_le_bytes([
296 value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7],
297 ]);
298 caps.max_size = size;
299 }
300 }
301 _ => { }
302 }
303 }
304 Ok(caps)
305 }
306}
307
308#[cfg(any(feature = "host", feature = "device", feature = "web"))]
313pub(crate) fn channel_error(kind: ErrorKind) -> Error {
314 if kind == ErrorKind::BrokenPipe {
315 Error::new(kind, "UPC channel closed")
316 } else {
317 kind.into()
318 }
319}