1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct UsbDevice {
7 pub bus: u8,
9 pub device_address: u8,
11 pub vendor_id: u16,
13 pub product_id: u16,
15 pub class: UsbClass,
17 pub speed: UsbSpeed,
19 pub manufacturer: Option<String>,
21 pub product: Option<String>,
23 pub serial: Option<String>,
25}
26
27impl UsbDevice {
28 pub fn vid_pid(&self) -> String {
30 format!("{:04x}:{:04x}", self.vendor_id, self.product_id)
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36pub enum UsbClass {
37 Audio,
39 Cdc,
41 Hid,
43 Physical,
45 Image,
47 Printer,
49 MassStorage,
51 Hub,
53 CdcData,
55 SmartCard,
57 ContentSecurity,
59 Video,
61 PersonalHealthcare,
63 AudioVideo,
65 Billboard,
67 TypeCBridge,
69 Diagnostic,
71 WirelessController,
73 Miscellaneous,
75 ApplicationSpecific,
77 VendorSpecific,
79 Unknown(u8),
81}
82
83impl UsbClass {
84 pub fn from_code(code: u8) -> Self {
86 match code {
87 0x01 => Self::Audio,
88 0x02 => Self::Cdc,
89 0x03 => Self::Hid,
90 0x05 => Self::Physical,
91 0x06 => Self::Image,
92 0x07 => Self::Printer,
93 0x08 => Self::MassStorage,
94 0x09 => Self::Hub,
95 0x0A => Self::CdcData,
96 0x0B => Self::SmartCard,
97 0x0D => Self::ContentSecurity,
98 0x0E => Self::Video,
99 0x0F => Self::PersonalHealthcare,
100 0x10 => Self::AudioVideo,
101 0x11 => Self::Billboard,
102 0x12 => Self::TypeCBridge,
103 0xDC => Self::Diagnostic,
104 0xE0 => Self::WirelessController,
105 0xEF => Self::Miscellaneous,
106 0xFE => Self::ApplicationSpecific,
107 0xFF => Self::VendorSpecific,
108 other => Self::Unknown(other),
109 }
110 }
111}
112
113impl std::fmt::Display for UsbClass {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 let name = match self {
116 Self::Audio => "Audio",
117 Self::Cdc => "CDC",
118 Self::Hid => "HID",
119 Self::Physical => "Physical",
120 Self::Image => "Image",
121 Self::Printer => "Printer",
122 Self::MassStorage => "Mass Storage",
123 Self::Hub => "Hub",
124 Self::CdcData => "CDC-Data",
125 Self::SmartCard => "Smart Card",
126 Self::ContentSecurity => "Content Security",
127 Self::Video => "Video",
128 Self::PersonalHealthcare => "Personal Healthcare",
129 Self::AudioVideo => "Audio/Video",
130 Self::Billboard => "Billboard",
131 Self::TypeCBridge => "Type-C Bridge",
132 Self::Diagnostic => "Diagnostic",
133 Self::WirelessController => "Wireless Controller",
134 Self::Miscellaneous => "Miscellaneous",
135 Self::ApplicationSpecific => "Application Specific",
136 Self::VendorSpecific => "Vendor Specific",
137 Self::Unknown(n) => return write!(f, "Unknown(0x{n:02x})"),
138 };
139 f.write_str(name)
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
145pub enum UsbSpeed {
146 Low,
148 Full,
150 High,
152 Super,
154 SuperPlus,
156 Unknown,
158}
159
160impl std::fmt::Display for UsbSpeed {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 let s = match self {
163 Self::Low => "Low-Speed (1.5 Mbit/s)",
164 Self::Full => "Full-Speed (12 Mbit/s)",
165 Self::High => "High-Speed (480 Mbit/s)",
166 Self::Super => "SuperSpeed (5 Gbit/s)",
167 Self::SuperPlus => "SuperSpeed+ (10 Gbit/s)",
168 Self::Unknown => "Unknown",
169 };
170 f.write_str(s)
171 }
172}
173
174#[derive(Debug, Error)]
176pub enum UsbError {
177 #[error("no device found with VID:PID {vendor_id:04x}:{product_id:04x}")]
179 DeviceNotFound {
180 vendor_id: u16,
182 product_id: u16,
184 },
185 #[error("failed to open device: {0}")]
187 OpenFailed(String),
188 #[error("failed to claim interface {0}: {1}")]
190 ClaimFailed(u8, String),
191 #[error("transfer failed on endpoint 0x{endpoint:02x}: {reason}")]
193 TransferFailed {
194 endpoint: u8,
196 reason: String,
198 },
199 #[error("transfer timed out on endpoint 0x{0:02x}")]
201 Timeout(u8),
202 #[error("USB error: {0}")]
204 Other(String),
205}