Files
usbd_uac2/src/descriptors.rs
T

1525 lines
54 KiB
Rust

use crate::constants::*;
use crate::debug;
use crate::error;
use crate::{constants::ClassSpecificACInterfaceDescriptorSubtype, cursor::Cursor};
use byteorder_embedded_io::{LittleEndian, WriteBytesExt};
use modular_bitfield::prelude::*;
use usb_device::UsbError;
use usb_device::{bus::StringIndex, descriptor::DescriptorWriter};
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ClockType {
External = 0,
InternalFixed = 1,
InternalVariable = 2,
InternalProgrammable = 3,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Specifier)]
#[bits = 2]
pub enum AccessControl {
NotPresent = 0,
ReadOnly = 1,
Programmable = 3,
}
#[bitfield(bits = 32)]
#[repr(u32)]
#[derive(Copy, Clone, Debug)]
pub struct ChannelConfig {
pub front_left: bool,
pub front_right: bool,
pub front_center: bool,
pub low_frequency_effects: bool,
pub back_left: bool,
pub back_right: bool,
pub front_left_of_center: bool,
pub front_right_of_center: bool,
pub back_center: bool,
pub side_left: bool,
pub side_right: bool,
pub top_center: bool,
pub top_front_left: bool,
pub top_front_center: bool,
pub top_front_right: bool,
pub top_back_left: bool,
pub top_back_center: bool,
pub top_back_right: bool,
pub top_front_left_of_center: bool,
pub top_front_right_of_center: bool,
pub left_row_low_frequency_effects: bool,
pub right_row_low_frequency_effects: bool,
pub top_side_left: bool,
pub top_side_right: bool,
pub bottom_center: bool,
pub back_left_of_center: bool,
pub back_right_of_center: bool,
#[skip]
__: B4,
raw_data: bool,
}
impl ChannelConfig {
/// Creates a new ChannelConfig with a default channel configuration based on the number of channels.
///
/// Supports:
/// - 1 channel: Front Left
/// - 2 channels: Front Left, Front Right
/// - 4 channels: Front Left, Front Right, Back Left, Back Right
/// - 6 channels: Front Left, Front Right, Back Left, Back Right, Front Center, LFE
/// - 8 channels: Front Left, Front Right, Back Left, Back Right, Front Center, Side Left, Side Right, LFE
pub fn default_chans(n: u8) -> Self {
match n {
1 => ChannelConfig::new().with_front_left(true),
2 => ChannelConfig::new()
.with_front_left(true)
.with_front_right(true),
4 => ChannelConfig::new()
.with_front_left(true)
.with_front_right(true)
.with_back_left(true)
.with_back_right(true),
6 => ChannelConfig::new()
.with_front_left(true)
.with_front_center(true)
.with_front_right(true)
.with_back_left(true)
.with_back_right(true)
.with_low_frequency_effects(true),
8 => ChannelConfig::new()
.with_front_left(true)
.with_front_center(true)
.with_front_right(true)
.with_back_left(true)
.with_back_right(true)
.with_front_center(true)
.with_side_left(true)
.with_side_right(true)
.with_low_frequency_effects(true),
_ => panic!("Unsupported number of channels"),
}
}
}
#[derive(Copy, Clone)]
#[bitfield(bits = 32)]
pub struct FeatureControls {
mute: AccessControl,
volume: AccessControl,
bass: AccessControl,
mid: AccessControl,
treble: AccessControl,
graphic_eq: AccessControl,
agc: AccessControl,
delay: AccessControl,
bass_boost: AccessControl,
loudness: AccessControl,
input_gain: AccessControl,
input_gain_pad: AccessControl,
phase_inverter: AccessControl,
underflow: AccessControl,
overflow: AccessControl,
hpf: AccessControl,
}
pub trait Descriptor {
/// The maximum possible size of the descriptor, including the length and descriptor type bytes. For creation of Sized buffers.
const MAX_SIZE: usize;
/// The bDescriptorType of the descriptor
fn descriptor_type(&self) -> u8;
/// The actual size of the descriptor, including the length and descriptor type bytes.
fn size(&self) -> u8;
/// The payload of the descriptor, not including the bLength and bDescriptorType bytes
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error>;
/// Write the descriptor to the provided buffer. Includes the length and descriptor type bytes. The default implementation defers to write_payload and self.size().
fn write<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(self.size())?;
writer.write_u8(self.descriptor_type())?;
self.write_payload(writer)?;
Ok(())
}
/// Write the descriptor using the provided usb_device DescriptorWriter. The default implementation defers to write_payload.
fn write_descriptor<'w, 'd>(
&self,
writer: &'w mut DescriptorWriter<'d>,
) -> Result<(), usb_device::UsbError> {
debug!("writer.write {}", core::any::type_name_of_val(self));
writer.write_with(self.descriptor_type(), |buf| {
let mut cur = Cursor::new(buf);
if let Err(e) = self.write_payload(&mut cur) {
error!("Write payload err {}", defmt::Display2Format(&e));
Err(e.into())
} else {
Ok(cur.position())
}
})
}
}
#[derive(Clone)]
pub struct ClockSource {
pub id: u8,
pub clock_type: ClockType,
pub sof_sync: bool,
pub frequency_access: AccessControl,
pub validity_access: AccessControl,
pub assoc_terminal: u8,
pub string: Option<StringIndex>,
}
impl ClockSource {
fn bm_attributes(&self) -> u8 {
self.clock_type as u8 | if self.sof_sync { 4 } else { 0 }
}
fn bm_controls(&self) -> u8 {
((self.validity_access as u8) << 2) | self.frequency_access as u8
}
}
impl Descriptor for ClockSource {
const MAX_SIZE: usize = 8;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
Self::MAX_SIZE as u8
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::ClockSource as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bClockId
writer.write_u8(self.bm_attributes())?; // bmAttributes
writer.write_u8(self.bm_controls())?; // bmControls
writer.write_u8(self.assoc_terminal)?; // bAssocTerminal
writer.write_u8(self.string.map_or(0, |n| u8::from(n)))?; // iClockSource
Ok(())
}
}
#[derive(Clone)]
pub struct InputTerminal {
pub id: u8,
pub terminal_type: TerminalType,
pub assoc_terminal: u8,
pub clock_source: u8,
pub num_channels: u8,
pub channel_config: ChannelConfig,
pub channel_names: u8,
pub copy_protect_control: AccessControl,
pub connector_control: AccessControl,
pub overload_control: AccessControl,
pub cluster_control: AccessControl,
pub underflow_control: AccessControl,
pub overflow_control: AccessControl,
pub phantom_power_control: AccessControl,
pub string: Option<StringIndex>,
}
impl Descriptor for InputTerminal {
const MAX_SIZE: usize = 17;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
Self::MAX_SIZE as u8
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::InputTerminal as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bTerminalID
writer.write_u16::<LittleEndian>(self.terminal_type as u16)?; // wTerminalType
writer.write_u8(self.assoc_terminal)?; // bAssocTerminal
writer.write_u8(self.clock_source)?; // bCSourceID
writer.write_u8(self.num_channels)?; // bNrChannels
writer.write(&self.channel_config.into_bytes())?;
writer.write_u8(self.channel_names)?;
writer.write_u8(
self.copy_protect_control as u8
| ((self.connector_control as u8) << 2)
| ((self.overload_control as u8) << 4)
| ((self.cluster_control as u8) << 6),
)?;
writer.write_u8(
self.underflow_control as u8
| ((self.overflow_control as u8) << 2)
| ((self.phantom_power_control as u8) << 4),
)?;
writer.write_u8(self.string.map_or(0, |s| u8::from(s)))?;
Ok(())
}
}
#[derive(Clone)]
pub struct OutputTerminal {
pub id: u8,
pub terminal_type: TerminalType,
pub assoc_terminal: u8,
pub source_id: u8,
pub clock_source: u8,
pub copy_protect_control: AccessControl,
pub connector_control: AccessControl,
pub overload_control: AccessControl,
pub underflow_control: AccessControl,
pub overflow_control: AccessControl,
pub string: Option<StringIndex>,
}
impl Descriptor for OutputTerminal {
const MAX_SIZE: usize = 12;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
Self::MAX_SIZE as u8
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::OutputTerminal as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bTerminalID
writer.write_u16::<LittleEndian>(self.terminal_type as u16)?; // wTerminalType
writer.write_u8(self.assoc_terminal)?; // bAssocTerminal
writer.write_u8(self.source_id)?; // bSourceID
writer.write_u8(self.clock_source)?; // bCSourceID
writer.write_u8(
self.copy_protect_control as u8
| ((self.connector_control as u8) << 2)
| ((self.overload_control as u8) << 4)
| ((self.underflow_control as u8) << 6),
)?;
writer.write_u8(self.overflow_control as u8)?;
writer.write_u8(self.string.map_or(0, |n| u8::from(n)))?; // iTerminal
Ok(())
}
}
#[derive(Clone)]
pub enum Terminal {
Input(InputTerminal),
Output(OutputTerminal),
}
impl Descriptor for Terminal {
const MAX_SIZE: usize = if InputTerminal::MAX_SIZE > OutputTerminal::MAX_SIZE {
InputTerminal::MAX_SIZE
} else {
OutputTerminal::MAX_SIZE
};
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
match self {
Self::Input(t) => t.size(),
Self::Output(t) => t.size(),
}
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
match self {
Self::Input(t) => t.write_payload(writer),
Self::Output(t) => t.write_payload(writer),
}
}
}
#[derive(Clone, Debug)]
pub struct ClockSelector<const MAX_SOURCES: usize> {
pub id: u8,
pub n_sources: u8,
pub sources: [u8; MAX_SOURCES], // baCSourceID[]
pub selector_access: AccessControl,
pub string: u8, // iClockSelector
}
impl<const N: usize> Descriptor for ClockSelector<N> {
const MAX_SIZE: usize = 7 + N;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
7 + self.n_sources
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::ClockSelector as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bClockID
writer.write_u8(self.n_sources)?; // bNrInPins
writer.write(&self.sources[0..(self.n_sources as usize)])?;
writer.write_u8(self.selector_access as u8)?; // bmControls (CX_CLOCK_SELECTOR)
writer.write_u8(self.string)?; // iClockSelector (last byte)
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct ClockMultiplier {
pub id: u8,
pub source_id: u8,
pub numerator_access: AccessControl,
pub denominator_access: AccessControl,
pub string: u8, // iClockMultiplier
}
impl ClockMultiplier {
fn bm_controls(&self) -> u8 {
(self.numerator_access as u8) | ((self.denominator_access as u8) << 2)
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<usize, T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::ClockMultiplier as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bClockID
writer.write_u8(self.source_id)?; // bCSourceID
writer.write_u8(self.bm_controls())?; // bmControls
writer.write_u8(self.string)?; // iClockMultiplier
Ok(self.size() as usize)
}
}
impl Descriptor for ClockMultiplier {
const MAX_SIZE: usize = 7;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
Self::MAX_SIZE as u8
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::ClockMultiplier as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bClockID
writer.write_u8(self.source_id)?; // bCSourceID
writer.write_u8(self.bm_controls())?; // bmControls
writer.write_u8(self.string)?; // iClockMultiplier
Ok(())
}
}
// Feature Unit's size depends on the number of channels in its source node, which we don't have a way
// to look up with the current API. Ignore and leave unimplemented for now.
//
// pub struct FeatureUnit<const MAX_CONTROLS: usize> {
// pub id: u8,
// pub source_id: u8,
// pub controls: [FeatureControls; MAX_CONTROLS], // bmaControls[] (N == channels + 1)
// pub n_controls: u8,
// pub string: u8, // iFeature
// }
// impl<const N: usize> Descriptor for FeatureUnit<N> {
// const MAX_SIZE: usize = 6 + 4 * N;
// fn size(&self) -> u8 {
// 6 + 4 * (self.n_controls as u8)
// }
// fn write(&self, buf: &mut [u8]) -> Result<usize, embedded_io::ErrorKind> {
// let mut cur = Cursor::new(buf);
// cur.write_u8(self.size())?; // bLength
// cur.write_u8(ClassSpecificDescriptorType::Interface as u8)?; // bDescriptorType
// cur.write_u8(ClassSpecificACInterfaceDescriptorSubtype::FeatureUnit as u8)?; // bDescriptorSubtype
// cur.write_u8(self.id)?; // bUnitID
// cur.write_u8(self.source_id)?; // bSourceID
// // bmaControls[i] as LE u32
// for v in self.controls.iter() {
// cur.write(&v.bytes)?;
// }
// cur.write_u8(self.string)?; // iFeature
// assert_eq!(cur.position(), self.size() as usize);
// Ok(cur.position())
// }
// }
// This implementation is not correct due to how pins/channels/mixer channels are computed
// it needs to know more about the rest of the devices
// pub struct MixerUnit<const MAX_SOURCES: usize, const MAX_CONTROLS: usize> {
// pub id: u8,
// pub sources: [u8; MAX_SOURCES], // baSourceID[]
// pub n_sources: u8,
// pub num_channels: u8, // bNrChannels
// pub channel_config: ChannelConfig, // bmChannelConfig (u32)
// pub channel_names: u8, // iChannelNames
// pub mixer_controls: [u8; MAX_CONTROLS], // bmMixerControls[]
// pub n_controls: u8,
// pub cluster_control: AccessControl,
// pub underflow_control: AccessControl,
// pub overflow_control: AccessControl,
// pub latency_control: AccessControl,
// pub string: u8, // iMixer
// }
// impl<const MS: usize, const MC: usize> MixerUnit<MS, MC> {
// fn bm_controls(&self) -> u8 {
// (self.cluster_control as u8)
// | ((self.underflow_control as u8) << 2)
// | ((self.overflow_control as u8) << 4)
// | ((self.latency_control as u8) << 6)
// }
// }
// impl<const MS: usize, const MC: usize> Descriptor for MixerUnit<MS, MC> {
// const MAX_SIZE: usize = 13 + MS + MC;
// fn size(&self) -> u8 {
// 13 + self.n_sources as u8 + self.n_controls as u8
// }
// fn write(&self, buf: &mut [u8]) -> Result<usize, embedded_io::ErrorKind> {
// let mut cur = Cursor::new(buf);
// cur.write_u8(self.size())?; // bLength
// cur.write_u8(ClassSpecificDescriptorType::Interface as u8)?; // bDescriptorType
// cur.write_u8(ClassSpecificACInterfaceDescriptorSubtype::MixerUnit as u8)?; // bDescriptorSubtype
// cur.write_u8(self.id)?; // bUnitID
// cur.write_u8(self.n_sources as u8)?; // bNrInPins
// cur.write(&self.sources[0..(self.n_sources as usize)])?; // baSourceID[]
// cur.write_u8(self.num_channels)?; // bNrChannels
// cur.write(&self.channel_config.bytes)?; // bmChannelConfig (already u32 LE)
// cur.write_u8(self.channel_names)?; // iChannelNames
// cur.write(&self.mixer_controls[0..(self.n_controls as usize)])?; // bmMixerControls[]
// cur.write_u8(self.bm_controls())?; // bmControls
// cur.write_u8(self.string)?; // iMixer
// assert_eq!(cur.position(), self.size() as usize);
// Ok(cur.position())
// }
// }
#[derive(Clone, Debug)]
pub struct SelectorUnit<const MAX_SOURCES: usize> {
pub id: u8,
pub sources: [u8; MAX_SOURCES], // baSourceID[]
pub n_sources: u8,
pub selector_control: AccessControl,
pub string: u8, // iSelector
}
impl<const N: usize> SelectorUnit<N> {
fn bm_controls(&self) -> u8 {
self.selector_control as u8
}
}
impl<const N: usize> Descriptor for SelectorUnit<N> {
const MAX_SIZE: usize = 7 + N;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
7 + self.n_sources
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::SelectorUnit as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bUnitID
writer.write_u8(self.n_sources)?; // bNrInPins
writer.write(&self.sources[0..(self.n_sources as usize)])?;
writer.write_u8(self.bm_controls())?; // bmControls
writer.write_u8(self.string)?; // iSelector
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct ProcessingUnit<const MAX_SOURCES: usize> {
pub id: u8,
pub process_type: u16, // wProcessType
pub sources: [u8; MAX_SOURCES], // baSourceID[]
pub num_channels: u8, // bNrChannels
pub n_sources: u8,
pub channel_config: ChannelConfig, // bmChannelConfig
pub channel_names: u8, // iChannelNames
pub enable_control: AccessControl,
pub mode_select_control: AccessControl,
pub cluster_control: AccessControl,
pub underflow_control: AccessControl,
pub overflow_control: AccessControl,
pub latency_control: AccessControl,
pub string: u8, // iProcessing
}
impl<const N: usize> ProcessingUnit<N> {
fn bm_controls(&self) -> u16 {
(self.enable_control as u16)
| ((self.mode_select_control as u16) << 2)
| ((self.cluster_control as u16) << 4)
| ((self.underflow_control as u16) << 6)
| ((self.overflow_control as u16) << 8)
| ((self.latency_control as u16) << 10)
}
}
impl<const N: usize> Descriptor for ProcessingUnit<N> {
const MAX_SIZE: usize = 17 + N;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
17 + self.n_sources
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::ProcessingUnit as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bUnitID
writer.write_u16::<LittleEndian>(self.process_type)?; // wProcessType
writer.write_u8(self.n_sources)?; // bNrInPins
writer.write(&self.sources[0..(self.n_sources as usize)])?; // baSourceID[]
writer.write_u8(self.num_channels)?; // bNrChannels
writer.write(&self.channel_config.into_bytes())?; // bmChannelConfig (already u32 LE)
writer.write_u8(self.channel_names)?; // iChannelNames
writer.write_u16::<LittleEndian>(self.bm_controls())?; // bmControls (PU is 2 bytes in UAC2)
writer.write_u8(self.string)?; // iProcessing
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct ExtensionUnit<const MAX_SOURCES: usize> {
pub id: u8,
pub extension_code: u16, // wExtensionCode
pub sources: [u8; MAX_SOURCES], // baSourceID[]
pub n_sources: u8,
pub num_channels: u8, // bNrChannels
pub channel_config: ChannelConfig, // bmChannelConfig
pub channel_names: u8, // iChannelNames
pub enable_control: AccessControl,
pub cluster_control: AccessControl,
pub underflow_control: AccessControl,
pub overflow_control: AccessControl,
pub string: u8, // iExtension
}
impl<const N: usize> ExtensionUnit<N> {
fn bm_controls(&self) -> u8 {
(self.enable_control as u8)
| ((self.cluster_control as u8) << 2)
| ((self.underflow_control as u8) << 4)
| ((self.overflow_control as u8) << 6)
}
}
impl<const N: usize> Descriptor for ExtensionUnit<N> {
const MAX_SIZE: usize = 16 + N;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
16 + self.n_sources
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::ExtensionUnit as u8)?; // bDescriptorSubtype
writer.write_u8(self.id)?; // bUnitID
writer.write_u16::<LittleEndian>(self.extension_code)?; // wExtensionCode
writer.write_u8(self.n_sources)?; // bNrInPins
writer.write(&self.sources[0..(self.n_sources as usize)])?; // baSourceID[]
writer.write_u8(self.num_channels)?; // bNrChannels
writer.write(&self.channel_config.into_bytes())?; // bmChannelConfig
writer.write_u8(self.channel_names)?; // iChannelNames
writer.write_u8(self.bm_controls())?; // bmControls (XU is 1 byte in UAC2)
writer.write_u8(self.string)?; // iExtension
Ok(())
}
}
// Effect unit is also variable based on its source node, leave unimplemented for now.
//
// pub struct EffectUnit<const MAX_CONTROLS: usize> {
// pub id: u8,
// pub effect_type: u16, // wEffectType
// pub source_id: u8, // bSourceID
// pub controls: [u32; MAX_CONTROLS], // bmaControls[] (N == channels + 1)
// pub n_controls: u8,
// pub string: u8, // iEffect
// }
// impl<const N: usize> Descriptor for EffectUnit<N> {
// const MAX_SIZE: usize = 16 + 4 * N;
// fn size(&self) -> u8 {
// 16 + 4 * self.n_controls
// }
// fn write(&self, buf: &mut [u8]) -> Result<usize, embedded_io::ErrorKind> {
// let mut cur = Cursor::new(buf);
// cur.write_u8(self.size())?; // bLength
// cur.write_u8(ClassSpecificDescriptorType::Interface as u8)?; // bDescriptorType
// cur.write_u8(ClassSpecificACInterfaceDescriptorSubtype::EffectUnit as u8)?; // bDescriptorSubtype
// cur.write_u8(self.id)?; // bUnitID
// cur.write_u16::<LittleEndian>(self.effect_type)?; // wEffectType
// cur.write_u8(self.source_id)?; // bSourceID
// for v in self.controls.iter() {
// cur.write_u32::<LittleEndian>(*v)?;
// }
// cur.write_u8(self.string)?; // iEffect
// assert_eq!(cur.position(), self.size() as usize);
// Ok(cur.position())
// }
// }
#[derive(Clone)]
/// Enum covering basic sized audio class descriptors for building the Class-Specific
/// AC Interface descriptor. Dynamically sized descriptors are not supported yet.
pub enum AudioClassDescriptor {
ClockSource(ClockSource),
ClockMultiplier(ClockMultiplier),
InputTerminal(InputTerminal),
OutputTerminal(OutputTerminal),
}
impl AudioClassDescriptor {
pub fn size(&self) -> u8 {
match self {
AudioClassDescriptor::ClockSource(cs) => cs.size(),
AudioClassDescriptor::ClockMultiplier(cm) => cm.size(),
AudioClassDescriptor::InputTerminal(it) => it.size(),
AudioClassDescriptor::OutputTerminal(ot) => ot.size(),
}
}
pub fn write<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
match self {
AudioClassDescriptor::ClockSource(cs) => cs.write(writer),
AudioClassDescriptor::ClockMultiplier(cm) => cm.write(writer),
AudioClassDescriptor::InputTerminal(it) => it.write(writer),
AudioClassDescriptor::OutputTerminal(ot) => ot.write(writer),
}
}
pub fn write_descriptor(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> {
match self {
AudioClassDescriptor::ClockSource(cs) => cs.write_descriptor(writer),
AudioClassDescriptor::ClockMultiplier(cm) => cm.write_descriptor(writer),
AudioClassDescriptor::InputTerminal(it) => it.write_descriptor(writer),
AudioClassDescriptor::OutputTerminal(ot) => ot.write_descriptor(writer),
}
}
}
impl From<ClockSource> for AudioClassDescriptor {
fn from(cs: ClockSource) -> Self {
AudioClassDescriptor::ClockSource(cs)
}
}
impl From<ClockMultiplier> for AudioClassDescriptor {
fn from(cm: ClockMultiplier) -> Self {
AudioClassDescriptor::ClockMultiplier(cm)
}
}
impl From<InputTerminal> for AudioClassDescriptor {
fn from(it: InputTerminal) -> Self {
AudioClassDescriptor::InputTerminal(it)
}
}
impl From<OutputTerminal> for AudioClassDescriptor {
fn from(ot: OutputTerminal) -> Self {
AudioClassDescriptor::OutputTerminal(ot)
}
}
impl From<Terminal> for AudioClassDescriptor {
fn from(t: Terminal) -> Self {
match t {
Terminal::Input(it) => AudioClassDescriptor::InputTerminal(it),
Terminal::Output(ot) => AudioClassDescriptor::OutputTerminal(ot),
}
}
}
pub struct AudioClassInterfaceDescriptor<const NUM_DESCRIPTORS: usize> {
inner: [AudioClassDescriptor; NUM_DESCRIPTORS],
category: FunctionCode,
}
impl<const N: usize> AudioClassInterfaceDescriptor<N> {
pub fn new(inner: [AudioClassDescriptor; N], category: FunctionCode) -> Self {
Self { inner, category }
}
/// Total length of the interface descriptor and all its associated descriptors.
/// wTotalLength in the Class-Specific AC Interface Header
pub fn total_length(&self) -> u16 {
9 + self
.inner
.iter()
.map(|desc| desc.size() as u16)
.sum::<u16>()
}
fn write_header<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
let total_length = self.total_length();
writer.write_u8(9)?; // bLength
writer.write_u8(ClassSpecificDescriptorType::Interface as u8)?; // bDescriptorType
writer.write_u8(ClassSpecificACInterfaceDescriptorSubtype::Header as u8)?; // bDescriptorSubtype
writer.write_u8(0)?; // bcdADC msd
writer.write_u8(2)?; // bcdADC lsd
writer.write_u8(self.category as u8)?; // bCategory
writer.write_u16::<LittleEndian>(total_length)?; // wTotalLength
writer.write_u8(0)?; // bmControls
Ok(())
}
pub fn write<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
self.write_header(writer)?;
for desc in &self.inner {
desc.write(writer)?;
}
Ok(())
}
}
/// USB Device Class Definition for Audio Data Formats Type I Format Type Descriptor
pub enum SamplingFrequencySet<'a> {
Discrete(&'a [u32]),
Continuous(u32, u32),
}
impl<'a> SamplingFrequencySet<'a> {
pub fn size(&self) -> u8 {
match self {
SamplingFrequencySet::Discrete(freqs) => freqs.len() as u8 * 3,
SamplingFrequencySet::Continuous(_, _) => 6,
}
}
}
pub struct FormatType1 {
pub bytes_per_sample: u8, // bSubframeSize
pub bit_resolution: u8, // bBitResolution
}
impl Descriptor for FormatType1 {
const MAX_SIZE: usize = 6;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
6
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificASInterfaceDescriptorSubtype::FormatType as u8)?;
writer.write_u8(FormatType::Type1 as u8)?; // bFormatType
writer.write_u8(self.bytes_per_sample)?; // bSubslotSize
writer.write_u8(self.bit_resolution)?; // bBitResolution
Ok(())
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum Type1FormatBitmap {
Pcm = (1 << 0),
Pcm8 = (1 << 1),
IeeeFloat = (1 << 2),
Alaw = (1 << 3),
Mulaw = (1 << 4),
Raw = (1 << 31),
}
pub struct AudioStreamingInterface {
pub terminal_id: u8,
pub active_alt_setting: AccessControl,
pub valid_alt_settings: AccessControl,
/// Only type 1 format is supported
pub format_type: FormatType,
pub format_bitmap: Type1FormatBitmap,
pub num_channels: u8,
pub channel_config: ChannelConfig,
pub string: Option<StringIndex>,
}
impl AudioStreamingInterface {
fn bm_controls(&self) -> u8 {
self.active_alt_setting as u8 | ((self.valid_alt_settings as u8) << 2)
}
}
impl Descriptor for AudioStreamingInterface {
const MAX_SIZE: usize = 16;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Interface as u8
}
fn size(&self) -> u8 {
Self::MAX_SIZE as u8
}
fn write_payload<T: embedded_io::Write>(&self, writer: &mut T) -> Result<(), T::Error> {
writer.write_u8(ClassSpecificASInterfaceDescriptorSubtype::General as u8)?;
writer.write_u8(self.terminal_id)?;
writer.write_u8(self.bm_controls())?;
writer.write_u8(self.format_type as u8)?;
writer.write_u32::<LittleEndian>(self.format_bitmap as u32)?;
writer.write_u8(self.num_channels)?;
writer.write(&self.channel_config.bytes)?;
writer.write_u8(self.string.map_or(0, |s| u8::from(s)))?;
Ok(())
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LockDelay {
Undefined(u16),
Milliseconds(u16),
DecodedSamples(u16),
}
impl LockDelay {
pub fn units(&self) -> u8 {
match self {
LockDelay::Undefined(_) => 0,
LockDelay::Milliseconds(_) => 1,
LockDelay::DecodedSamples(_) => 2,
}
}
pub fn value(&self) -> u16 {
match self {
LockDelay::Undefined(v) => *v,
LockDelay::Milliseconds(v) => *v,
LockDelay::DecodedSamples(v) => *v,
}
}
}
pub struct AudioStreamingEndpoint {
pub max_packets_only: bool,
pub pitch_control: AccessControl,
pub overrun_control: AccessControl,
pub underrun_control: AccessControl,
pub lock_delay: LockDelay,
}
impl AudioStreamingEndpoint {
fn bm_controls(&self) -> u8 {
(self.pitch_control as u8)
| (self.overrun_control as u8) << 2
| (self.underrun_control as u8) << 4
}
}
impl Descriptor for AudioStreamingEndpoint {
const MAX_SIZE: usize = 8;
fn descriptor_type(&self) -> u8 {
ClassSpecificDescriptorType::Endpoint as u8
}
fn size(&self) -> u8 {
Self::MAX_SIZE as u8
}
fn write_payload<T: embedded_io::Write>(
&self,
writer: &mut T,
) -> core::result::Result<(), T::Error> {
writer.write_u8(ClassSpecificEndpointDescriptorSubtype::General as u8)?; // bDescriptorSubtype
writer.write_u8((self.max_packets_only as u8) << 7)?; // bmAttributes
writer.write_u8(self.bm_controls())?; // bmControls
writer.write_u8(self.lock_delay.units())?; // bLockDelayUnits
writer.write_u16::<LittleEndian>(self.lock_delay.value())?; // wLockDelay
Ok(())
}
}
#[cfg(test)]
extern crate std;
#[cfg(test)]
mod tests {
use super::*;
use crate::cursor::Cursor;
use std::{print, println};
#[test]
fn test_clock_source() {
let string = Some(unsafe { core::mem::transmute(10u8) });
let cs = ClockSource {
id: 8,
clock_type: ClockType::InternalFixed,
sof_sync: false,
frequency_access: AccessControl::ReadOnly,
validity_access: AccessControl::ReadOnly,
assoc_terminal: 6,
string,
};
let mut buf = [0u8; ClockSource::MAX_SIZE];
let mut cur = Cursor::new(&mut buf[..]);
let len = cs.size();
cs.write(&mut cur).unwrap();
let descriptor = &buf[..len as usize];
assert_eq!(
descriptor,
&[
8, // bLength
0x24, // CS_INTERFACE
0x0a, // CLOCK_SOURCE
8, // bClockId
1, // bmAttributes
5, // bmControls
6, // bAssocTerminal
10, // iClockSource
]
)
}
#[test]
fn test_clock_selector() {
let cs = ClockSelector {
id: 8,
n_sources: 3,
sources: [2, 3, 4, 0],
selector_access: AccessControl::Programmable,
string: 10,
};
let mut buf = [0u8; ClockSelector::<4>::MAX_SIZE];
let mut cur = Cursor::new(&mut buf[..]);
let len = cs.size();
cs.write(&mut cur).unwrap();
let descriptor = &buf[..len as usize];
assert_eq!(
descriptor,
&[
10, // bLength
0x24, // CS_INTERFACE
0x0b, // CLOCK_SELECTOR
8, // bClockId
3, // bNrInPins
2, // baCSourceId
3, 4, 3, // bmControls
10, // iClockSelector
]
)
}
#[test]
fn test_clock_multiplier() {
let cm = ClockMultiplier {
id: 8,
source_id: 10,
numerator_access: AccessControl::Programmable,
denominator_access: AccessControl::ReadOnly,
string: 20,
};
let mut buf = [0u8; ClockMultiplier::MAX_SIZE];
let mut cur = Cursor::new(&mut buf[..]);
let len = cm.size();
cm.write(&mut cur).unwrap();
let descriptor = &buf[0..len as usize];
assert_eq!(
descriptor,
&[
7, // bLength
0x24, // CS_INTERFACE
0x0c, // CLOCK_MULTIPLIER
8, // bClockId
10, // bCSourceId
7, // bmControls
20, // iClockMultiplier
]
)
}
#[test]
fn test_input_terminal() {
let it = InputTerminal {
id: 8,
terminal_type: TerminalType::InMicrophone,
assoc_terminal: 10,
clock_source: 12,
num_channels: 2,
channel_config: ChannelConfig::new()
.with_front_left(true)
.with_front_right(true),
channel_names: 14,
copy_protect_control: AccessControl::NotPresent,
connector_control: AccessControl::Programmable,
overload_control: AccessControl::ReadOnly,
cluster_control: AccessControl::Programmable,
underflow_control: AccessControl::ReadOnly,
overflow_control: AccessControl::ReadOnly,
phantom_power_control: AccessControl::NotPresent,
string: Some(unsafe { core::mem::transmute(20u8) }),
};
let mut buf = [0u8; InputTerminal::MAX_SIZE];
let mut cur = Cursor::new(&mut buf[..]);
let len = it.size();
it.write(&mut cur).unwrap();
let descriptor = &buf[..len as usize];
assert_eq!(
descriptor,
&[
17, // bLength
0x24, // CS_INTERFACE
0x02, // INPUT_TERMINAL
8,
0x01, // wTerminalType (u16)
0x02,
10, // bAssocTerminal
12, // bCSourceId
2, // bNrChannels
3, // bmChannelConfig (u32)
0,
0,
0,
14, // iChannelNames
(3 << 2) | (1 << 4) | (3 << 6), // bmControls (u16)
(1 << 0) | (1 << 2),
20, // iTerminal
]
)
}
#[test]
fn test_output_terminal() {
let ot = OutputTerminal {
id: 8,
terminal_type: TerminalType::OutSpeaker,
assoc_terminal: 10,
source_id: 11,
clock_source: 12,
copy_protect_control: AccessControl::NotPresent,
connector_control: AccessControl::Programmable,
overload_control: AccessControl::ReadOnly,
underflow_control: AccessControl::ReadOnly,
overflow_control: AccessControl::ReadOnly,
string: Some(unsafe { core::mem::transmute(20u8) }),
};
let mut buf = [0u8; OutputTerminal::MAX_SIZE];
let mut cur = Cursor::new(&mut buf[..]);
let len = ot.size();
ot.write(&mut cur).unwrap();
let descriptor = &buf[..len as usize];
assert_eq!(
descriptor,
&[
12, // bLength
0x24, // CS_INTERFACE
0x03, // OUTPUT_TERMINAL
8, // bTerminalId
01, // wTerminalType (u16)
03,
10, // bAssocTerminal
11, // bSourceId
12, // bCSourceId
(3 << 2) | (1 << 4) | (1 << 6), // bmControls (u16)
1,
20, // iTerminal
]
)
}
#[test]
fn test_selector_unit() {
let su = SelectorUnit {
id: 8,
n_sources: 3,
sources: [2, 3, 4, 0],
selector_control: AccessControl::Programmable,
string: 20,
};
let mut buf = [0u8; SelectorUnit::<4>::MAX_SIZE];
let mut cur = Cursor::new(&mut buf[..]);
let len = su.size();
su.write(&mut cur).unwrap();
let descriptor = &buf[..len as usize];
assert_eq!(
descriptor,
&[
10, // bLength
0x24, // CS_INTERFACE
0x05, // SELECTOR_UNIT
8, // bUnitId
3, // bNrInPins
2, // baSourceId[3]
3, 4, 3, // bmControls
20, // iSelector
]
)
}
/// Write a minimal PCAP file containing a single synthetic USB control
/// transfer that returns the provided descriptor bytes.
///
/// The resulting file can be opened in Wireshark and expanded under:
/// USB → URB → Descriptors
///
/// Note: this is slop
pub fn write_usb_descriptor_pcap(
path: &str,
descriptor_type: u8, // e.g. 0x02 = Configuration
descriptor_index: u8,
descriptor_bytes: &[u8],
) -> std::io::Result<()> {
use std::fs::File;
use std::io::{self, Write};
use std::time::{SystemTime, UNIX_EPOCH};
use std::vec::Vec;
const DLT_USBPCAP: u32 = 249;
// URB function codes (Windows). 0x0008 is URB_FUNCTION_CONTROL_TRANSFER.
const URB_FUNCTION_CONTROL_TRANSFER: u16 = 0x0008;
// USBPcap transfer types
const USBPCAP_TRANSFER_CONTROL: u8 = 2;
// USBPcap control stages
const USBPCAP_CONTROL_STAGE_SETUP: u8 = 0;
const USBPCAP_CONTROL_STAGE_COMPLETE: u8 = 3;
let mut f = File::create(path)?;
// PCAP GLOBAL HEADER (little endian)
f.write_all(&[
0xd4,
0xc3,
0xb2,
0xa1, // magic
0x02,
0x00, // version major
0x04,
0x00, // version minor
0x00,
0x00,
0x00,
0x00, // thiszone
0x00,
0x00,
0x00,
0x00, // sigfigs
0xff,
0xff,
0x00,
0x00, // snaplen
(DLT_USBPCAP & 0xff) as u8,
((DLT_USBPCAP >> 8) & 0xff) as u8,
((DLT_USBPCAP >> 16) & 0xff) as u8,
((DLT_USBPCAP >> 24) & 0xff) as u8,
])?;
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let ts_sec = now.as_secs() as u32;
let ts_usec = now.subsec_micros();
// USB SETUP packet for GET_DESCRIPTOR
let w_value = ((descriptor_type as u16) << 8) | (descriptor_index as u16);
let w_length = descriptor_bytes.len() as u16;
let setup_pkt = [
0x80, // bmRequestType: device->host, standard, device
0x06, // bRequest: GET_DESCRIPTOR
(w_value & 0xff) as u8,
(w_value >> 8) as u8,
0x00,
0x00, // wIndex
(w_length & 0xff) as u8,
(w_length >> 8) as u8,
];
// Helper to write one USBPcap packet record
fn write_usbpcap_record(
f: &mut File,
ts_sec: u32,
ts_usec: u32,
// USBPcap base header fields
irp_id: u64,
status: u32,
function: u16,
info: u8,
bus: u16,
device: u16,
endpoint: u8,
transfer: u8,
stage: u8,
payload: &[u8],
) -> io::Result<()> {
// USBPCAP_BUFFER_PACKET_HEADER is packed(1) and begins with USHORT headerLen.
// For control transfers, header is: base header + stage byte.
// base header size = 2+8+4+2+1+2+2+1+1+4 = 27 bytes
// + stage = 1 => 28 bytes total headerLen
let header_len: u16 = 28;
let data_len: u32 = payload.len() as u32;
let mut pkt = Vec::with_capacity(header_len as usize + payload.len());
// --- USBPCAP_BUFFER_PACKET_HEADER ---
pkt.extend_from_slice(&header_len.to_le_bytes()); // USHORT headerLen
pkt.extend_from_slice(&irp_id.to_le_bytes()); // UINT64 irpId
pkt.extend_from_slice(&status.to_le_bytes()); // USBD_STATUS status (UINT32)
pkt.extend_from_slice(&function.to_le_bytes()); // USHORT function
pkt.push(info); // UCHAR info
pkt.extend_from_slice(&bus.to_le_bytes()); // USHORT bus
pkt.extend_from_slice(&device.to_le_bytes()); // USHORT device
pkt.push(endpoint); // UCHAR endpoint (MSB = IN)
pkt.push(transfer); // UCHAR transfer (2 = control)
pkt.extend_from_slice(&data_len.to_le_bytes()); // UINT32 dataLength
// --- USBPCAP_BUFFER_CONTROL_HEADER extra field ---
pkt.push(stage); // UCHAR stage
debug_assert_eq!(pkt.len(), header_len as usize);
// --- transfer data ---
pkt.extend_from_slice(payload);
// PCAP per-record header
let incl_len = pkt.len() as u32;
f.write_all(&ts_sec.to_le_bytes())?;
f.write_all(&ts_usec.to_le_bytes())?;
f.write_all(&incl_len.to_le_bytes())?;
f.write_all(&incl_len.to_le_bytes())?;
f.write_all(&pkt)?;
Ok(())
}
// Use consistent IDs so Wireshark can correlate them.
let irp_id: u64 = 0x1111_2222_3333_4444;
let bus: u16 = 1;
let device: u16 = 1;
// 1) SETUP stage (host -> device), carries SETUP bytes as payload.
write_usbpcap_record(
&mut f,
ts_sec,
ts_usec,
irp_id,
0, // status
URB_FUNCTION_CONTROL_TRANSFER,
0x00, // info: FDO->PDO (request)
bus,
device,
0x00, // EP0 OUT
USBPCAP_TRANSFER_CONTROL,
USBPCAP_CONTROL_STAGE_SETUP,
&setup_pkt, // payload = 8-byte setup
)?;
let mut config_descriptor = Vec::with_capacity(9 + 8 + 9 + descriptor_bytes.len());
// Config Descriptor
config_descriptor.push(0x09); // bLength
config_descriptor.push(0x02); // bDescriptorType = CONFIGURATION
config_descriptor.write_all(&(9 + 8 + 9 + descriptor_bytes.len() as u16).to_le_bytes())?;
config_descriptor.push(0x01); // bNumInterfaces
config_descriptor.push(0x01); // bConfigurationValue
config_descriptor.push(0x00); // iConfiguration
config_descriptor.push(0xC0); // bmAttributes = self-powered
config_descriptor.push(0x32); // bMaxPower = 100mA
// Interface Association Descriptor
config_descriptor.push(0x08); // bLength
config_descriptor.push(0x0B); // bDescriptorType = INTERFACE ASSOCIATION
config_descriptor.push(0x00); // bFirstInterface
config_descriptor.push(0x01); // bInterfaceCount
config_descriptor.push(0x01); // bFunctionClass = AUDIO
config_descriptor.push(0x03); // bFunctionSubClass = AUDIO_STREAMING
config_descriptor.push(0x00); // bFunctionProtocol = NONE
config_descriptor.push(0x00); // iFunction
// 'Standard' Audio Class Interface Descriptor
config_descriptor.push(0x09); // bLength
config_descriptor.push(0x04); // bDescriptorType = INTERFACE
config_descriptor.push(0x00); // bInterfaceNumber
config_descriptor.push(0x00); // bAlternateSetting
config_descriptor.push(0x02); // bNumEndpoints
config_descriptor.push(0x01); // bInterfaceClass = AUDIO
config_descriptor.push(0x01); // bInterfaceSubClass = AUDIO_CONTROL
config_descriptor.push(0x20); // bInterfaceProtocol = NONE
config_descriptor.push(0x00); // iInterface
config_descriptor.write_all(descriptor_bytes)?;
// 2) COMPLETE stage (device -> host), carries descriptor bytes as payload.
write_usbpcap_record(
&mut f,
ts_sec,
ts_usec.wrapping_add(1), // tiny delta
irp_id,
0, // status
URB_FUNCTION_CONTROL_TRANSFER,
0x01, // info: PDO->FDO (response)
bus,
device,
0x80, // EP0 IN
USBPCAP_TRANSFER_CONTROL,
USBPCAP_CONTROL_STAGE_COMPLETE,
&config_descriptor, // payload = IN data (descriptors)
)?;
Ok(())
}
#[test]
fn test_ac_interface() {
let descriptors: [AudioClassDescriptor; _] = [
ClockSource {
id: 1,
clock_type: ClockType::InternalFixed,
sof_sync: false,
frequency_access: AccessControl::NotPresent,
validity_access: AccessControl::NotPresent,
assoc_terminal: 0,
string: None,
}
.into(),
InputTerminal {
id: 2,
terminal_type: TerminalType::UsbStreaming,
assoc_terminal: 0,
clock_source: 1,
num_channels: 2,
channel_config: ChannelConfig::default_chans(2),
channel_names: 0,
copy_protect_control: AccessControl::NotPresent,
connector_control: AccessControl::NotPresent,
overload_control: AccessControl::NotPresent,
cluster_control: AccessControl::NotPresent,
underflow_control: AccessControl::NotPresent,
overflow_control: AccessControl::NotPresent,
phantom_power_control: AccessControl::NotPresent,
string: None,
}
.into(),
OutputTerminal {
id: 3,
terminal_type: TerminalType::OutUndefined,
assoc_terminal: 0,
source_id: 2,
clock_source: 1,
copy_protect_control: AccessControl::NotPresent,
connector_control: AccessControl::NotPresent,
overload_control: AccessControl::NotPresent,
underflow_control: AccessControl::NotPresent,
overflow_control: AccessControl::NotPresent,
string: None,
}
.into(),
OutputTerminal {
id: 4,
source_id: 5,
terminal_type: TerminalType::UsbStreaming,
assoc_terminal: 0,
clock_source: 1,
copy_protect_control: AccessControl::NotPresent,
connector_control: AccessControl::NotPresent,
overload_control: AccessControl::NotPresent,
underflow_control: AccessControl::NotPresent,
overflow_control: AccessControl::NotPresent,
string: None,
}
.into(),
InputTerminal {
id: 5,
terminal_type: TerminalType::InUndefined,
assoc_terminal: 0,
clock_source: 1,
num_channels: 2,
channel_config: ChannelConfig::default_chans(2),
channel_names: 0,
copy_protect_control: AccessControl::NotPresent,
connector_control: AccessControl::NotPresent,
overload_control: AccessControl::NotPresent,
cluster_control: AccessControl::NotPresent,
underflow_control: AccessControl::NotPresent,
overflow_control: AccessControl::NotPresent,
phantom_power_control: AccessControl::NotPresent,
string: None,
}
.into(),
];
let ac = AudioClassInterfaceDescriptor::new(descriptors, FunctionCode::Undefined);
let mut buf = [0u8; 1024];
let len = {
let mut cur = Cursor::new(&mut buf[..]);
ac.write(&mut cur).unwrap();
cur.position()
};
let bytes = &buf[..len];
for (i, b) in bytes.iter().enumerate() {
if i.is_multiple_of(16) {
println!();
}
print!("{:02x} ", b);
}
println!();
write_usb_descriptor_pcap("./uac2.pcap", 0x02, 0, bytes).unwrap();
}
#[test]
fn test_format_type1() {
let format = FormatType1 {
bytes_per_sample: 4,
bit_resolution: 24,
};
let mut buf = [0u8; FormatType1::MAX_SIZE];
let len = {
let mut cur = Cursor::new(&mut buf[..]);
format.write(&mut cur).unwrap();
cur.position()
};
let descriptor = &buf[..len];
assert_eq!(
descriptor,
&[
6, //bLength
0x24, // CS_INTERFACE
0x02, // FORMAT_TYPE
0x01, // FORMAT_TYPE_I
4, // bSubframeSize
24, // bBitResolution
]
);
}
fn test_as_interface_desc() {
let intf = AudioStreamingInterface {
terminal_id: 2,
active_alt_setting: AccessControl::Programmable,
valid_alt_settings: AccessControl::ReadOnly,
format_type: FormatType::Type1,
format_bitmap: Type1FormatBitmap::Pcm,
num_channels: 2,
channel_config: ChannelConfig::default_chans(2),
string: None,
};
let mut buf = [0u8; AudioStreamingInterface::MAX_SIZE];
let len = {
let mut cur = Cursor::new(&mut buf[..]);
intf.write(&mut cur).unwrap();
cur.position()
};
let descriptor = &buf[..len];
assert_eq!(
descriptor,
&[
16,
0x24, // CS_INTERFACE
0x01, // AS_GENERAL
2, // bTerminalLink
3 | (1 << 2), // bmControls
1, // bFormatType
1, // bmFormats[0]
0, // bmFormats[1]
0, // bmFormats[2]
0, // bmFormats[3]
2, // bNrChannels
3, // bmChannelConfig[0]
0, // bmChannelConfig[1]
0, // bmChannelConfig[2]
0, // bmChannelConfig[3]
0 // iChannelNames
]
);
}
#[test]
fn test_as_endpoint_desc() {
let ep = AudioStreamingEndpoint {
max_packets_only: true,
pitch_control: AccessControl::NotPresent,
overrun_control: AccessControl::ReadOnly,
underrun_control: AccessControl::Programmable,
lock_delay: LockDelay::DecodedSamples(1024),
};
let mut buf = [0u8; AudioStreamingEndpoint::MAX_SIZE];
let len = {
let mut cur = Cursor::new(&mut buf[..]);
ep.write(&mut cur).unwrap();
cur.position()
};
let descriptor = &buf[..len];
assert_eq!(
descriptor,
&[
8,
0x25, // CS_ENDPOINT
0x01, // EP_GENERAL
0x80, // bmAttributes
0 | (1 << 2) | (3 << 4), // bmControls
2, // bLockDelayUnits
(1024u16 & 0xff) as u8, // bLockDelay[0]
(1024u16 >> 8) as u8, // bLockDelay[1]
]
);
}
}