use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsbDevice {
pub bus: u8,
pub device_address: u8,
pub vendor_id: u16,
pub product_id: u16,
pub class: UsbClass,
pub speed: UsbSpeed,
pub manufacturer: Option<String>,
pub product: Option<String>,
pub serial: Option<String>,
}
impl UsbDevice {
pub fn vid_pid(&self) -> String {
format!("{:04x}:{:04x}", self.vendor_id, self.product_id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum UsbClass {
Audio,
Cdc,
Hid,
Physical,
Image,
Printer,
MassStorage,
Hub,
CdcData,
SmartCard,
ContentSecurity,
Video,
PersonalHealthcare,
AudioVideo,
Billboard,
TypeCBridge,
Diagnostic,
WirelessController,
Miscellaneous,
ApplicationSpecific,
VendorSpecific,
Unknown(u8),
}
impl UsbClass {
pub fn from_code(code: u8) -> Self {
match code {
0x01 => Self::Audio,
0x02 => Self::Cdc,
0x03 => Self::Hid,
0x05 => Self::Physical,
0x06 => Self::Image,
0x07 => Self::Printer,
0x08 => Self::MassStorage,
0x09 => Self::Hub,
0x0A => Self::CdcData,
0x0B => Self::SmartCard,
0x0D => Self::ContentSecurity,
0x0E => Self::Video,
0x0F => Self::PersonalHealthcare,
0x10 => Self::AudioVideo,
0x11 => Self::Billboard,
0x12 => Self::TypeCBridge,
0xDC => Self::Diagnostic,
0xE0 => Self::WirelessController,
0xEF => Self::Miscellaneous,
0xFE => Self::ApplicationSpecific,
0xFF => Self::VendorSpecific,
other => Self::Unknown(other),
}
}
}
impl std::fmt::Display for UsbClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
Self::Audio => "Audio",
Self::Cdc => "CDC",
Self::Hid => "HID",
Self::Physical => "Physical",
Self::Image => "Image",
Self::Printer => "Printer",
Self::MassStorage => "Mass Storage",
Self::Hub => "Hub",
Self::CdcData => "CDC-Data",
Self::SmartCard => "Smart Card",
Self::ContentSecurity => "Content Security",
Self::Video => "Video",
Self::PersonalHealthcare => "Personal Healthcare",
Self::AudioVideo => "Audio/Video",
Self::Billboard => "Billboard",
Self::TypeCBridge => "Type-C Bridge",
Self::Diagnostic => "Diagnostic",
Self::WirelessController => "Wireless Controller",
Self::Miscellaneous => "Miscellaneous",
Self::ApplicationSpecific => "Application Specific",
Self::VendorSpecific => "Vendor Specific",
Self::Unknown(n) => return write!(f, "Unknown(0x{n:02x})"),
};
f.write_str(name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum UsbSpeed {
Low,
Full,
High,
Super,
SuperPlus,
Unknown,
}
impl std::fmt::Display for UsbSpeed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Low => "Low-Speed (1.5 Mbit/s)",
Self::Full => "Full-Speed (12 Mbit/s)",
Self::High => "High-Speed (480 Mbit/s)",
Self::Super => "SuperSpeed (5 Gbit/s)",
Self::SuperPlus => "SuperSpeed+ (10 Gbit/s)",
Self::Unknown => "Unknown",
};
f.write_str(s)
}
}
#[derive(Debug, Error)]
pub enum UsbError {
#[error("no device found with VID:PID {vendor_id:04x}:{product_id:04x}")]
DeviceNotFound { vendor_id: u16, product_id: u16 },
#[error("failed to open device: {0}")]
OpenFailed(String),
#[error("failed to claim interface {0}: {1}")]
ClaimFailed(u8, String),
#[error("transfer failed on endpoint 0x{endpoint:02x}: {reason}")]
TransferFailed { endpoint: u8, reason: String },
#[error("transfer timed out on endpoint 0x{0:02x}")]
Timeout(u8),
#[error("USB error: {0}")]
Other(String),
}