use crate::debug;
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[allow(non_snake_case)] pub struct SetupPacket {
pub bmRequestType: u8,
pub bRequest: u8,
pub wValue: u16,
pub wIndex: u16,
pub wLength: u16,
}
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[allow(non_snake_case)] #[allow(missing_docs)]
pub struct DeviceDescriptor {
pub bLength: u8,
pub bDescriptorType: u8,
pub bcdUSB: [u8; 2],
pub bDeviceClass: u8,
pub bDeviceSubClass: u8,
pub bDeviceProtocol: u8,
pub bMaxPacketSize0: u8,
pub idVendor: [u8; 2],
pub idProduct: [u8; 2],
pub bcdDevice: [u8; 2],
pub iManufacturer: u8,
pub iProduct: u8,
pub iSerialNumber: u8,
pub bNumConfigurations: u8,
}
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Copy, Clone)]
#[allow(non_snake_case)] #[allow(missing_docs)]
pub struct ConfigurationDescriptor {
pub bLength: u8,
pub bDescriptorType: u8,
pub wTotalLength: [u8; 2],
pub bNumInterfaces: u8,
pub bConfigurationValue: u8,
pub iConfiguration: u8,
pub bmAttributes: u8,
pub bMaxPower: u8,
}
unsafe impl bytemuck::Zeroable for ConfigurationDescriptor {}
unsafe impl bytemuck::Pod for ConfigurationDescriptor {}
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Copy, Clone)]
#[allow(non_snake_case)] #[allow(missing_docs)]
pub struct InterfaceDescriptor {
pub bLength: u8,
pub bDescriptorType: u8,
pub bInterfaceNumber: u8,
pub bAlternateSetting: u8,
pub bNumEndpoints: u8,
pub bInterfaceClass: u8,
pub bInterfaceSubClass: u8,
pub bInterfaceProtocol: u8,
pub iInterface: u8,
}
unsafe impl bytemuck::Zeroable for InterfaceDescriptor {}
unsafe impl bytemuck::Pod for InterfaceDescriptor {}
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Copy, Clone)]
#[allow(non_snake_case)] #[allow(missing_docs)]
pub struct EndpointDescriptor {
pub bLength: u8,
pub bDescriptorType: u8,
pub bEndpointAddress: u8,
pub bmAttributes: u8,
pub wMaxPacketSize: [u8; 2],
pub bInterval: u8,
}
unsafe impl bytemuck::Zeroable for EndpointDescriptor {}
unsafe impl bytemuck::Pod for EndpointDescriptor {}
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Copy, Clone)]
#[allow(non_snake_case)] #[allow(missing_docs)]
pub struct HubDescriptor {
bDescLength: u8,
bDescriptorType: u8,
bNbrPorts: u8,
wHubCharacteristics: [u8; 2],
bPwrOn2PwrGood: u8,
bHubContrCurrent: u8,
DeviceRemovable: u8, PortPwrCtrlMask: u8, }
unsafe impl bytemuck::Zeroable for HubDescriptor {}
unsafe impl bytemuck::Pod for HubDescriptor {}
pub const DEVICE_TO_HOST: u8 = 0x80;
pub const HOST_TO_DEVICE: u8 = 0;
pub const STANDARD_REQUEST: u8 = 0;
pub const CLASS_REQUEST: u8 = 0x20;
pub const VENDOR_REQUEST: u8 = 0x40;
pub const RECIPIENT_DEVICE: u8 = 0;
pub const RECIPIENT_INTERFACE: u8 = 1;
pub const RECIPIENT_ENDPOINT: u8 = 2;
pub const RECIPIENT_OTHER: u8 = 3;
pub const GET_STATUS: u8 = 0;
pub const CLEAR_FEATURE: u8 = 1;
pub const SET_FEATURE: u8 = 3;
pub const SET_ADDRESS: u8 = 5;
pub const GET_DESCRIPTOR: u8 = 6;
pub const SET_DESCRIPTOR: u8 = 7;
pub const SET_CONFIGURATION: u8 = 9;
pub const DEVICE_DESCRIPTOR: u8 = 1;
pub const CONFIGURATION_DESCRIPTOR: u8 = 2;
pub const STRING_DESCRIPTOR: u8 = 3;
pub const INTERFACE_DESCRIPTOR: u8 = 4;
pub const ENDPOINT_DESCRIPTOR: u8 = 5;
pub const HUB_DESCRIPTOR: u8 = 0x29;
pub const HUB_CLASSCODE: u8 = 9;
pub const PORT_RESET: u16 = 4;
pub const PORT_POWER: u16 = 8;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum EndpointType {
Control = 0,
Isochronous = 1,
Bulk = 2,
Interrupt = 3,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Direction {
In,
Out,
}
pub trait DescriptorVisitor {
fn on_configuration(&mut self, _c: &ConfigurationDescriptor) {}
fn on_interface(&mut self, _i: &InterfaceDescriptor) {}
fn on_endpoint(&mut self, _e: &EndpointDescriptor) {}
fn on_other(&mut self, _d: &[u8]) {}
}
pub struct ShowDescriptors;
impl DescriptorVisitor for ShowDescriptors {
fn on_configuration(&mut self, c: &ConfigurationDescriptor) {
debug::println!("{:?}", c);
}
fn on_interface(&mut self, i: &InterfaceDescriptor) {
debug::println!(" {:?}", i);
}
fn on_endpoint(&mut self, e: &EndpointDescriptor) {
debug::println!(" {:?}", e);
}
fn on_other(&mut self, d: &[u8]) {
let dlen = d[0];
let dtype = d[1];
let domain = match dtype & 0x60 {
0x00 => "standard",
0x20 => "class",
0x40 => "vendor",
_ => "reserved",
};
debug::println!(" {} type {} len {} skipped", domain, dtype, dlen);
}
}
pub fn parse_descriptors(buf: &[u8], v: &mut impl DescriptorVisitor) {
let mut index = 0;
while buf.len() > index + 2 {
let dlen = buf[index] as usize;
let dtype = buf[index + 1];
if dlen < 2 || buf.len() < index + dlen {
return;
}
match dtype {
CONFIGURATION_DESCRIPTOR => {
let min_dlen =
Ord::min(dlen, size_of::<ConfigurationDescriptor>());
if let Ok(c) =
bytemuck::try_from_bytes(&buf[index..index + min_dlen])
{
v.on_configuration(c);
}
}
INTERFACE_DESCRIPTOR => {
let min_dlen =
Ord::min(dlen, size_of::<InterfaceDescriptor>());
if let Ok(i) =
bytemuck::try_from_bytes(&buf[index..index + min_dlen])
{
v.on_interface(i);
}
}
ENDPOINT_DESCRIPTOR => {
let min_dlen = Ord::min(dlen, size_of::<EndpointDescriptor>());
if let Ok(e) =
bytemuck::try_from_bytes(&buf[index..index + min_dlen])
{
v.on_endpoint(e);
}
}
_ => {
v.on_other(&buf[index..(index + dlen)]);
}
}
index += dlen;
}
}
#[cfg(all(test, feature = "std"))]
#[path = "tests/wire.rs"]
mod tests;