initial commit: beginnings of descriptor definition

This commit is contained in:
2026-04-19 00:58:39 -07:00
commit 1d18eb72cf
7 changed files with 2603 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/target
Generated
+219
View File
@@ -0,0 +1,219 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-embedded-io"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed6bb9472871706c9b1f648ca527031e33d647a95706d6ab5659f22ca28d419"
dependencies = [
"byteorder",
"embedded-io",
]
[[package]]
name = "defmt"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
dependencies = [
"bitflags",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
dependencies = [
"defmt-parser",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror",
]
[[package]]
name = "embedded-io"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7"
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]]
name = "modular-bitfield"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2956e537fc68236d2aa048f55704f231cc93f1c4de42fe1ecb5bd7938061fc4a"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b43b4fd69e3437618106f7754f34021b831a514f9e1a98ae863cabcd8d8dad"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "usb-device"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6"
dependencies = [
"heapless",
"portable-atomic",
]
[[package]]
name = "usbd-uac2"
version = "0.1.0"
dependencies = [
"byteorder-embedded-io",
"defmt",
"embedded-io",
"modular-bitfield",
"usb-device",
]
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "usbd-uac2"
description = "USB Audio Class 2.0 for usb-device"
authors = ["Keenan Tims <ktims@gotroot.ca>"]
version = "0.1.0"
edition = "2024"
keywords = ["no-std", "usb-device"]
[features]
defmt = ["dep:defmt"]
[dependencies]
byteorder-embedded-io = { version = "0.1.1", features = ["embedded-io"] }
defmt = { version = "1.0.1", optional = true }
embedded-io = "0.7.1"
modular-bitfield = "0.13.1"
usb-device = "0.3"
+490
View File
@@ -0,0 +1,490 @@
pub const AUDIO: u8 = 0x1;
pub const HEADER: u8 = 0x1;
/// A.2 Audio Function Subclass Codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum FunctionSubclass {
Undefined = 0,
}
/// A.3 Audio Function Protocol Codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum FunctionProtocol {
Undefined = 0,
Version2 = 0x20,
}
/// A.5 Audio Interface Subclass Codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum InterfaceSubclass {
Undefined = 0,
AudioControl = 1,
AudioStreaming = 2,
MidiStreaming = 3,
}
/// A.6 Audio Interface Protocol Codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum InterfaceProtocol {
Undefined = 0,
Version2 = 0x20,
}
/// A.7 Audio Function Category Codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum FunctionCode {
Undefined = 0,
DesktopSpeaker = 1,
HomeTheater = 2,
Microphone = 3,
Headset = 4,
Telephone = 5,
Converter = 6,
SoundRecorder = 7,
IoBox = 8,
MusicalInstrument = 9,
ProAudio = 0xa,
AudioVideo = 0xb,
ControlPanel = 0xc,
Other = 0xff,
}
/// A.8 Audio Class-Specific Descriptor Types
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClassSpecificDescriptorType {
Undefined = 0x20,
Device = 0x21,
Configuration = 0x22,
String = 0x23,
Interface = 0x24,
Endpoint = 0x25,
}
/// A.9 Audio Class-Specific AC Interface Descriptor Subtypes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClassSpecificACInterfaceDescriptorSubtype {
Undefined = 0,
Header = 0x01,
InputTerminal = 0x02,
OutputTerminal = 0x03,
MixerUnit = 0x04,
SelectorUnit = 0x05,
FeatureUnit = 0x06,
EffectUnit = 0x07,
ProcessingUnit = 0x08,
ExtensionUnit = 0x09,
ClockSource = 0x0A,
ClockSelector = 0x0B,
ClockMultiplier = 0x0C,
SampleRateConverter = 0x0D,
}
/// A.10 Audio Class-Specific AS Interface Descriptor Subtypes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClassSpecificASInterfaceDescriptorSubtype {
Undefined = 0,
General = 1,
FormatType = 2,
Encoder = 3,
Decoder = 4,
}
/// A.11 Effect Unit Effect Types
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EffectUnitEffectType {
Undefined = 0,
ParamEqSection = 1,
Reverb = 2,
ModDelay = 3,
DynRangeComp = 4,
}
/// A.12 Processing Unit Process Types
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ProcessingUnitProcessType {
Undefined = 0,
UpDownMix = 1,
DolbyPrologic = 2,
StereoExtender = 3,
}
/// A.13 Audio Class-Specific Endpoint Descriptor Subtypes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClassSpecificEndpointDescriptorSubtype {
Undefined = 0,
General = 1,
}
/// A.14 Audio Class-Specific Request Codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClassSpecificRequest {
Undefined = 0,
Cur = 1,
Range = 2,
Mem = 3,
}
/// A.15 Encoder Type Codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EncoderType {
Undefined = 0,
Other = 1,
Mpeg = 2,
Ac3 = 3,
Wma = 4,
Dts = 5,
}
/// A.16 Decoder Type Codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DecoderType {
Undefined = 0,
Other = 1,
Mpeg = 2,
Ac3 = 3,
Wma = 4,
Dts = 5,
}
/// A.17.1 Clock Source Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClockSourceControlSelector {
Undefined = 0,
SamFreqControl = 1,
ClockValidControl = 2,
}
/// A.17.2 Clock Selector Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClockSelectorControlSelector {
ControlUndefined = 0,
ClockSelectorControl = 1,
}
/// A.17.3 Clock Multiplier Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClockMultiplierControlSelector {
Undefined = 0,
NumeratorControl = 1,
DenominatorControl = 2,
}
/// A.17.4 Terminal Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TerminalControlSelector {
Undefined = 0,
CopyProtect = 1,
Connector = 2,
Overload = 3,
Cluster = 4,
Underflow = 5,
Overflow = 6,
Latency = 7,
PhantomPower = 8,
}
/// A.17.5 Mixer Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MixerControlSelector {
Undefined = 0,
Mixer = 1,
Cluster = 2,
Underflow = 3,
Overflow = 4,
Latency = 5,
}
/// A.17.6 Selector Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SelectorControlSelector {
Undefined = 0,
Selector = 1,
Latency = 2,
}
/// A.17.7 Feature Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum FeatureUnitControlSelector {
Undefined = 0,
Mute = 0x01,
Volume = 0x02,
Bass = 0x03,
Mid = 0x04,
Treble = 0x05,
GraphicEqualizer = 0x06,
AutomaticGain = 0x07,
Delay = 0x08,
BassBoost = 0x09,
Loudness = 0x0A,
InputGain = 0x0B,
InputGainPad = 0x0C,
PhaseInverter = 0x0D,
Underflow = 0x0E,
Overflow = 0x0F,
Latency = 0x10,
HighpassFilter = 0x11,
}
/// A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ParametricEqSectionEffectUnitControlSelector {
Undefined = 0,
Enable = 1,
CenterFreq = 2,
QFactor = 3,
Gain = 4,
Underflow = 5,
Overflow = 6,
Latency = 7,
}
/// A.17.8.2 Reverberation Effect Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ReverbEffectUnitControlSelector {
Undefined = 0,
Enable = 1,
Type = 2,
Level = 3,
Time = 4,
Feedback = 5,
PreDelay = 6,
Density = 7,
HiFreqRolloff = 8,
Underflow = 9,
Overflow = 0xa,
Latency = 0xb,
}
/// A.17.8.3 Modulation Delay Effect Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ModDelayEffectUnitControlSelector {
Undefined = 0,
Enable = 1,
Balance = 2,
Rate = 3,
Depth = 4,
Time = 5,
Feedback = 6,
Underflow = 7,
Overflow = 8,
Latency = 9,
}
/// A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DynamicRangeCompressorEffectUnitControlSelector {
Undefined = 0,
Enable = 1,
CompressionRate = 2,
MaxAmplitude = 3,
Threshold = 4,
AttackTime = 5,
ReleaseTime = 6,
Underflow = 7,
Overflow = 8,
Latency = 9,
}
/// A.17.9.1 Up/Down-mix Processing Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum UpDownMixProcessingUnitControlSelector {
Undefined = 0,
Enable = 1,
ModeSelect = 2,
Cluster = 3,
Underflow = 4,
Overflow = 5,
Latency = 6,
}
/// A.17.9.2 Dolby Prologic Processing Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DolbyProcessingUnitControlSelectors {
Undefined = 0,
Enable = 1,
ModeSelect = 2,
Cluster = 3,
Underflow = 4,
Overflow = 5,
Latency = 6,
}
/// A.17.9.3 Stereo Extender Processing Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum StereoExtenderProcessingUnitControlSelector {
Undefined = 0,
Enable = 1,
Width = 2,
Underflow = 3,
Overflow = 4,
Latency = 5,
}
/// A.17.10 Extension Unit Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ExtensionUnitControlSelector {
Undefined = 0,
Enable = 1,
Cluster = 2,
Underflow = 3,
Overflow = 4,
Latency = 5,
}
/// A.17.11 AudioStreaming Interface Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AudioStreamingInterfaceControlSelector {
Undefined = 0,
ActAltSetting = 1,
ValAltSetting = 2,
AudioDataFormat = 3,
}
/// A.17.12 Encoder Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EncoderControlSelector {
Undefined = 0,
BitRate = 0x01,
Quality = 0x02,
Vbr = 0x03,
Type = 0x04,
Underflow = 0x05,
Overflow = 0x06,
EncoderError = 0x07,
Param1 = 0x08,
Param2 = 0x09,
Param3 = 0x0A,
Param4 = 0x0B,
Param5 = 0x0C,
Param6 = 0x0D,
Param7 = 0x0E,
Param8 = 0x0F,
}
/// A.17.13.1 MPEG Decoder Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MpegDecoderControlSelector {
Undefined = 0,
DualChannel = 1,
SecondStereo = 2,
Multilingual = 3,
DynRange = 4,
Scaling = 5,
HiloScaling = 6,
Underflow = 7,
Overflow = 8,
DecoderError = 9,
}
/// A.17.13.2 AC-3 Decoder Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Ac3DecoderControlSelector {
Undefined = 0,
Mode = 1,
DynRange = 2,
Scaling = 3,
HiloScaling = 4,
Underflow = 5,
Overflow = 6,
DecoderError = 7,
}
/// A.17.13.3 WMA Decoder Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum WmaDecoderControlSelector {
Undefined = 0,
Underflow = 1,
Overflow = 2,
DecoderError = 3,
}
/// A.17.13.4 DTS Decoder Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DtsDecoderControlSelector {
Undefined = 0,
Underflow = 1,
Overflow = 2,
DecoderError = 3,
}
/// A.17.14 Endpoint Control Selectors
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EndpointControlSelector {
Undefined = 0,
Pitch = 1,
DataOverrun = 2,
DataUnderrun = 3,
}
/// Universal Serial Bus Device Class Definition for Terminal Types
#[repr(u16)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TerminalType {
// USB Terminal Types
UsbUndefined = 0x0100,
UsbStreaming = 0x0101,
UsbVendor = 0x01ff,
// Input Terminal Types
InUndefined = 0x0200,
InMicrophone = 0x0201,
InDesktopMicrophone = 0x0202,
InPersonalMicrophone = 0x0203,
InOmniDirectionalMicrophone = 0x0204,
InMicrophoneArray = 0x0205,
InProcessingMicrophoneArray = 0x0206,
// Output Terminal Types
OutUndefined = 0x0300,
OutSpeaker = 0x0301,
OutHeadphones = 0x0302,
OutHeadMountedDisplayAudio = 0x0303,
OutDesktopSpeaker = 0x0304,
OutRoomSpeaker = 0x0305,
OutCommunicationSpeaker = 0x0306,
OutLowFrequencyEffectsSpeaker = 0x0307,
// External Terminal Types
ExtUndefined = 0x0600,
ExtAnalogConnector = 0x0601,
ExtDigitalAudioInterface = 0x0602,
ExtLineConnector = 0x0603,
ExtLegacyAudioConnector = 0x0604,
ExtSpdifConnector = 0x0605,
Ext1394DaStream = 0x0606,
Ext1394DvStreamSoundtrack = 0x0607,
}
+169
View File
@@ -0,0 +1,169 @@
// Copy most of embedded_io_cursor here to avoid multiple embedded-io versions in dep tree
use core::cmp;
use embedded_io::{BufRead, Error, ErrorKind, ErrorType, Read, Seek, SeekFrom, Write};
#[derive(Debug, Default, Eq, PartialEq)]
pub struct Cursor<T> {
inner: T,
pos: usize,
}
impl<T> Cursor<T> {
/// Creates a new cursor wrapping the provided underlying in-memory buffer.
///
/// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`)
/// is not empty. So writing to cursor starts with overwriting `Vec`
/// content, not with appending to it.
pub const fn new(inner: T) -> Cursor<T> {
Cursor { pos: 0, inner }
}
/// Consumes this cursor, returning the underlying value.
pub fn into_inner(self) -> T {
self.inner
}
/// Gets a reference to the underlying value in this cursor.
pub const fn get_ref(&self) -> &T {
&self.inner
}
/// Gets a mutable reference to the underlying value in this cursor.
///
/// Care should be taken to avoid modifying the internal I/O state of the
/// underlying value as it may corrupt this cursor's position.
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
/// Returns the current position of this cursor.
pub const fn position(&self) -> usize {
self.pos
}
/// Sets the position of this cursor.
pub fn set_position(&mut self, pos: usize) {
self.pos = pos;
}
}
impl<T> Cursor<T>
where
T: AsRef<[u8]>,
{
/// Returns the remaining slice from the current position.
///
/// This method returns the portion of the underlying buffer that
/// can still be read from the current cursor position.
pub fn remaining_slice(&self) -> &[u8] {
let pos = cmp::min(self.pos, self.inner.as_ref().len());
&self.inner.as_ref()[pos..]
}
/// Returns `true` if there are no more bytes to read from the cursor.
///
/// This is equivalent to checking if `remaining_slice().is_empty()`.
pub fn is_empty(&self) -> bool {
self.pos >= self.inner.as_ref().len()
}
}
impl<T> Clone for Cursor<T>
where
T: Clone,
{
#[inline]
fn clone(&self) -> Self {
Cursor {
inner: self.inner.clone(),
pos: self.pos,
}
}
#[inline]
fn clone_from(&mut self, other: &Self) {
self.inner.clone_from(&other.inner);
self.pos = other.pos;
}
}
impl<T> ErrorType for Cursor<T> {
type Error = ErrorKind;
}
// Read implementation for AsRef<[u8]> types
impl<T> Read for Cursor<T>
where
T: AsRef<[u8]>,
{
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let remaining = self.remaining_slice();
let n = cmp::min(buf.len(), remaining.len());
if n > 0 {
buf[..n].copy_from_slice(&remaining[..n]);
}
self.pos += n;
Ok(n)
}
}
// BufRead implementation for AsRef<[u8]> types
impl<T> BufRead for Cursor<T>
where
T: AsRef<[u8]>,
{
fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
Ok(self.remaining_slice())
}
fn consume(&mut self, amt: usize) {
self.pos += amt;
}
}
// Seek implementation for AsRef<[u8]> types
impl<T> Seek for Cursor<T>
where
T: AsRef<[u8]>,
{
fn seek(&mut self, style: SeekFrom) -> Result<u64, Self::Error> {
let (base_pos, offset) = match style {
SeekFrom::Start(n) => {
self.pos = n as usize;
return Ok(n);
}
SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n),
SeekFrom::Current(n) => (self.pos as u64, n),
};
match base_pos.checked_add_signed(offset) {
Some(n) => {
self.pos = n as usize;
Ok(self.pos as u64)
}
None => Err(ErrorKind::InvalidInput),
}
}
}
/// Helper function for writing to fixed-size slices
fn slice_write(pos_mut: &mut usize, slice: &mut [u8], buf: &[u8]) -> Result<usize, ErrorKind> {
let pos = cmp::min(*pos_mut, slice.len()) as usize;
let amt = (&mut slice[pos..]).write(buf).map_err(|err| err.kind())?;
*pos_mut += amt;
Ok(amt)
}
// Write implementation for &mut [u8]
impl Write for Cursor<&mut [u8]> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
slice_write(&mut self.pos, self.inner, buf)
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
+1422
View File
File diff suppressed because it is too large Load Diff
+285
View File
@@ -0,0 +1,285 @@
#![no_std]
#![allow(dead_code)]
mod constants;
mod cursor;
mod descriptors;
use constants::*;
use descriptors::*;
use usb_device::control::{Recipient, Request, RequestType};
use usb_device::device::DEFAULT_ALTERNATE_SETTING;
use usb_device::endpoint::{self, Endpoint, EndpointDirection, In, Out};
use usb_device::{UsbDirection, class_prelude::*};
#[derive(Clone, Copy, Debug)]
pub enum Format {
/// Signed, 16 bits per subframe, little endian
S16le,
/// Signed, 24 bits per subframe, little endian
S24le,
/// Signed, 32 bits per subframe, little endian
S32le,
}
/// Sampling rates that shall be supported by an steaming endpoint
#[derive(Debug)]
pub enum Rates<'a> {
/// A continuous range of sampling rates in samples/second defined by a
/// tuple including a minimum value and a maximum value. The maximum value
/// must be greater than the minimum value.
Continuous(u32, u32),
/// A set of discrete sampling rates in samples/second
Discrete(&'a [u32]),
}
#[derive(Debug)]
pub struct StreamConfig<'a> {
format: Format,
channels: u8,
rates: Rates<'a>,
terminal_type: TerminalType,
/// ISO endpoint size calculated from format, channels and rates (may be
/// removed in future)
ep_size: u16,
}
impl StreamConfig<'_> {
/// Create a stream configuration with one or more discrete sampling rates
/// indicated in samples/second. An input stream or an output stream will
/// have an Input Terminal or Output Terminal of Terminal Type
/// `terminal_type`, respectively.
pub fn new_discrete(
format: Format,
channels: u8,
rates: &'_ [u32],
terminal_type: TerminalType,
) -> Result<StreamConfig<'_>> {
let max_rate = rates.iter().max().unwrap();
let ep_size = Self::ep_size(format, channels, *max_rate)?;
let rates = Rates::Discrete(rates);
Ok(StreamConfig {
format,
channels,
rates,
terminal_type,
ep_size,
})
}
/// Create a stream configuration with a continuous range of supported
/// sampling rates indicated in samples/second. An input stream or an output
/// stream will have an Input Terminal or Output Terminal of Terminal Type
/// `terminal_type`, respectively.
pub fn new_continuous(
format: Format,
channels: u8,
min_rate: u32,
max_rate: u32,
terminal_type: TerminalType,
) -> Result<StreamConfig<'static>> {
if min_rate >= max_rate {
return Err(Error::InvalidValue);
}
let ep_size = Self::ep_size(format, channels, max_rate)?;
let rates = Rates::Continuous(min_rate, max_rate);
Ok(StreamConfig {
format,
channels,
rates,
terminal_type,
ep_size,
})
}
/// calculate ISO endpoint size from format, channels and rates
fn ep_size(format: Format, channels: u8, max_rate: u32) -> Result<u16> {
let octets_per_frame = channels as u32
* match format {
Format::S16le => 2,
Format::S24le => 3,
Format::S32le => 4,
};
let ep_size = octets_per_frame * max_rate / 1000;
// if ep_size > MAX_ISO_EP_SIZE {
// return Err(Error::BandwidthExceeded);
// }
Ok(ep_size as u16)
}
}
/// USB audio errors, including possible USB Stack errors
#[derive(Debug)]
pub enum Error {
InvalidValue,
BandwidthExceeded,
StreamNotInitialized,
UsbError(usb_device::UsbError),
}
impl From<UsbError> for Error {
fn from(err: UsbError) -> Self {
Error::UsbError(err)
}
}
type Result<T> = core::result::Result<T, Error>;
struct AudioStream<'a, B: UsbBus, D: EndpointDirection> {
stream_config: StreamConfig<'a>,
interface: InterfaceNumber,
endpoint: Endpoint<'a, B, D>,
alt_setting: u8,
}
impl<B: UsbBus> AudioStream<'_, B, endpoint::In> {
fn input_terminal_desc(&self, id: u8, clock_source: u8) -> InputTerminal {
let channel_config = ChannelConfig::default_chans(self.stream_config.channels);
InputTerminal {
id,
terminal_type: TerminalType::UsbStreaming,
assoc_terminal: 0,
clock_source,
num_channels: self.stream_config.channels,
channel_config,
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: 0,
}
}
}
impl<B: UsbBus> AudioStream<'_, B, endpoint::Out> {
fn output_terminal_desc(&self, id: u8, source_id: u8, clock_source: u8) -> OutputTerminal {
OutputTerminal {
id,
terminal_type: TerminalType::UsbStreaming,
assoc_terminal: 0,
source_id,
clock_source,
copy_protect_control: AccessControl::NotPresent,
connector_control: AccessControl::NotPresent,
overload_control: AccessControl::NotPresent,
underflow_control: AccessControl::NotPresent,
overflow_control: AccessControl::NotPresent,
string: 0,
}
}
}
pub struct AudioClass<'a, B: UsbBus> {
control_iface: InterfaceNumber,
input: Option<AudioStream<'a, B, In>>,
output: Option<AudioStream<'a, B, Out>>,
function: FunctionCode,
clock_type: ClockType,
input_type: Option<TerminalType>,
output_type: Option<TerminalType>,
}
impl<B: UsbBus> AudioClass<'_, B> {}
impl<B: UsbBus> UsbClass<B> for AudioClass<'_, B> {
fn get_configuration_descriptors(
&self,
writer: &mut DescriptorWriter,
) -> usb_device::Result<()> {
// Build the necessary descriptors
// Clock Source - id 1
// USB Input Terminal - id 2
// Audio Output Terminal - id 3
// USB Output Terminal - id 4
// Audio Input Terminal - id 5
let clock_source = ClockSource {
id: 1,
clock_type: self.clock_type,
sof_sync: false,
frequency_access: if self.clock_type == ClockType::InternalProgrammable {
AccessControl::Programmable
} else {
AccessControl::NotPresent
},
validity_access: AccessControl::ReadOnly,
assoc_terminal: 0,
string: 0,
};
let in_terminals = match &self.input {
Some(i) => Some((
i.input_terminal_desc(2, 1),
OutputTerminal {
id: 3,
terminal_type: self.output_type.unwrap_or(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: 0,
},
)),
None => None,
};
let out_terminals = match &self.output {
Some(i) => Some((
i.output_terminal_desc(4, 5, 1),
InputTerminal {
id: 5,
terminal_type: self.input_type.unwrap_or(TerminalType::InUndefined),
assoc_terminal: 0,
clock_source: 1,
num_channels: i.stream_config.channels,
channel_config: ChannelConfig::default_chans(i.stream_config.channels),
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: 0,
},
)),
None => None,
};
let n_interfaces = match (&self.input, &self.output) {
(Some(_), Some(_)) => 3, // two audio, one control
(Some(_), None) | (None, Some(_)) => 2, // one audio, one control
(None, None) => 1, // no audio (?!), one control
};
writer.iad(
self.control_iface,
n_interfaces,
AUDIO,
FunctionSubclass::Undefined as u8,
FunctionProtocol::Version2 as u8,
None,
)?;
writer.interface(
self.control_iface,
AUDIO,
InterfaceSubclass::AudioControl as u8,
InterfaceProtocol::Version2 as u8,
)?;
if let Some(terminals) = in_terminals {
terminals.0.write_descriptor(writer)?;
terminals.1.write_descriptor(writer)?;
}
if let Some(terminals) = out_terminals {
terminals.0.write_descriptor(writer)?;
terminals.1.write_descriptor(writer)?;
}
Ok(())
}
}