diff options
| author | elagil <[email protected]> | 2024-11-02 20:01:20 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2024-11-24 00:33:08 +0100 |
| commit | ffc7b732e90af9715a31526c7f179e76c2a96062 (patch) | |
| tree | 4a7e8565c554ae5b72c2b26cb988d91e4e930ff1 | |
| parent | bc7372d7011c36157e4d55e05d0a3c5a82ba6f1c (diff) | |
feat(usb): add USB Audio Class 1
| -rw-r--r-- | embassy-usb/src/class/mod.rs | 1 | ||||
| -rw-r--r-- | embassy-usb/src/class/uac1/class_codes.rs | 151 | ||||
| -rw-r--r-- | embassy-usb/src/class/uac1/mod.rs | 134 | ||||
| -rw-r--r-- | embassy-usb/src/class/uac1/speaker.rs | 778 | ||||
| -rw-r--r-- | embassy-usb/src/class/uac1/terminal_type.rs | 50 |
5 files changed, 1114 insertions, 0 deletions
diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs index b883ed4e5..4bd89eb66 100644 --- a/embassy-usb/src/class/mod.rs +++ b/embassy-usb/src/class/mod.rs | |||
| @@ -3,4 +3,5 @@ pub mod cdc_acm; | |||
| 3 | pub mod cdc_ncm; | 3 | pub mod cdc_ncm; |
| 4 | pub mod hid; | 4 | pub mod hid; |
| 5 | pub mod midi; | 5 | pub mod midi; |
| 6 | pub mod uac1; | ||
| 6 | pub mod web_usb; | 7 | pub mod web_usb; |
diff --git a/embassy-usb/src/class/uac1/class_codes.rs b/embassy-usb/src/class/uac1/class_codes.rs new file mode 100644 index 000000000..3f6956771 --- /dev/null +++ b/embassy-usb/src/class/uac1/class_codes.rs | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | //! Audio Device Class Codes as defined in Universal Serial Bus Device Class | ||
| 2 | //! Definition for Audio Devices, Release 1.0, Appendix A and Universal Serial | ||
| 3 | //! Bus Device Class Definition for Audio Data Formats, Release 1.0, Appendix | ||
| 4 | //! A.1.1 (Audio Data Format Type I Codes) | ||
| 5 | #![allow(dead_code)] | ||
| 6 | |||
| 7 | /// The current version of the ADC specification (1.0) | ||
| 8 | pub const ADC_VERSION: u16 = 0x0100; | ||
| 9 | |||
| 10 | /// The current version of the USB device (1.0) | ||
| 11 | pub const DEVICE_VERSION: u16 = 0x0100; | ||
| 12 | |||
| 13 | /// Audio Interface Class Code | ||
| 14 | pub const USB_AUDIO_CLASS: u8 = 0x01; | ||
| 15 | |||
| 16 | // Audio Interface Subclass Codes | ||
| 17 | pub const USB_UNDEFINED_SUBCLASS: u8 = 0x00; | ||
| 18 | pub const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01; | ||
| 19 | pub const USB_AUDIOSTREAMING_SUBCLASS: u8 = 0x02; | ||
| 20 | pub const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03; | ||
| 21 | |||
| 22 | // Audio Protocol Code | ||
| 23 | pub const PROTOCOL_NONE: u8 = 0x00; | ||
| 24 | |||
| 25 | // Audio Class-Specific Descriptor Types | ||
| 26 | pub const CS_UNDEFINED: u8 = 0x20; | ||
| 27 | pub const CS_DEVICE: u8 = 0x21; | ||
| 28 | pub const CS_CONFIGURATION: u8 = 0x22; | ||
| 29 | pub const CS_STRING: u8 = 0x23; | ||
| 30 | pub const CS_INTERFACE: u8 = 0x24; | ||
| 31 | pub const CS_ENDPOINT: u8 = 0x25; | ||
| 32 | |||
| 33 | // Descriptor Subtype | ||
| 34 | pub const AC_DESCRIPTOR_UNDEFINED: u8 = 0x00; | ||
| 35 | pub const HEADER_SUBTYPE: u8 = 0x01; | ||
| 36 | pub const INPUT_TERMINAL: u8 = 0x02; | ||
| 37 | pub const OUTPUT_TERMINAL: u8 = 0x03; | ||
| 38 | pub const MIXER_UNIT: u8 = 0x04; | ||
| 39 | pub const SELECTOR_UNIT: u8 = 0x05; | ||
| 40 | pub const FEATURE_UNIT: u8 = 0x06; | ||
| 41 | pub const PROCESSING_UNIT: u8 = 0x07; | ||
| 42 | pub const EXTENSION_UNIT: u8 = 0x08; | ||
| 43 | |||
| 44 | // Audio Class-Specific AS Interface Descriptor Subtypes | ||
| 45 | pub const AS_DESCRIPTOR_UNDEFINED: u8 = 0x00; | ||
| 46 | pub const AS_GENERAL: u8 = 0x01; | ||
| 47 | pub const FORMAT_TYPE: u8 = 0x02; | ||
| 48 | pub const FORMAT_SPECIFIC: u8 = 0x03; | ||
| 49 | |||
| 50 | // Processing Unit Process Types | ||
| 51 | pub const PROCESS_UNDEFINED: u16 = 0x00; | ||
| 52 | pub const UP_DOWNMIX_PROCESS: u16 = 0x01; | ||
| 53 | pub const DOLBY_PROLOGIC_PROCESS: u16 = 0x02; | ||
| 54 | pub const DDD_STEREO_EXTENDER_PROCESS: u16 = 0x03; | ||
| 55 | pub const REVERBERATION_PROCESS: u16 = 0x04; | ||
| 56 | pub const CHORUS_PROCESS: u16 = 0x05; | ||
| 57 | pub const DYN_RANGE_COMP_PROCESS: u16 = 0x06; | ||
| 58 | |||
| 59 | // Audio Class-Specific Endpoint Descriptor Subtypes | ||
| 60 | pub const EP_DESCRIPTOR_UNDEFINED: u8 = 0x00; | ||
| 61 | pub const EP_GENERAL: u8 = 0x01; | ||
| 62 | |||
| 63 | // Audio Class-Specific Request Codes | ||
| 64 | pub const REQUEST_CODE_UNDEFINED: u8 = 0x00; | ||
| 65 | pub const SET_CUR: u8 = 0x01; | ||
| 66 | pub const GET_CUR: u8 = 0x81; | ||
| 67 | pub const SET_MIN: u8 = 0x02; | ||
| 68 | pub const GET_MIN: u8 = 0x82; | ||
| 69 | pub const SET_MAX: u8 = 0x03; | ||
| 70 | pub const GET_MAX: u8 = 0x83; | ||
| 71 | pub const SET_RES: u8 = 0x04; | ||
| 72 | pub const GET_RES: u8 = 0x84; | ||
| 73 | pub const SET_MEM: u8 = 0x05; | ||
| 74 | pub const GET_MEM: u8 = 0x85; | ||
| 75 | pub const GET_STAT: u8 = 0xFF; | ||
| 76 | |||
| 77 | // Terminal Control Selectors | ||
| 78 | pub const TE_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 79 | pub const COPY_PROTECT_CONTROL: u8 = 0x01; | ||
| 80 | |||
| 81 | // Feature Unit Control Selectors | ||
| 82 | pub const FU_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 83 | pub const MUTE_CONTROL: u8 = 0x01; | ||
| 84 | pub const VOLUME_CONTROL: u8 = 0x02; | ||
| 85 | pub const BASS_CONTROL: u8 = 0x03; | ||
| 86 | pub const MID_CONTROL: u8 = 0x04; | ||
| 87 | pub const TREBLE_CONTROL: u8 = 0x05; | ||
| 88 | pub const GRAPHIC_EQUALIZER_CONTROL: u8 = 0x06; | ||
| 89 | pub const AUTOMATIC_GAIN_CONTROL: u8 = 0x07; | ||
| 90 | pub const DELAY_CONTROL: u8 = 0x08; | ||
| 91 | pub const BASS_BOOST_CONTROL: u8 = 0x09; | ||
| 92 | pub const LOUDNESS_CONTROL: u8 = 0x0A; | ||
| 93 | |||
| 94 | // Up/Down-mix Processing Unit Control Selectors | ||
| 95 | pub const UD_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 96 | pub const UD_ENABLE_CONTROL: u8 = 0x01; | ||
| 97 | pub const UD_MODE_SELECT_CONTROL: u8 = 0x02; | ||
| 98 | |||
| 99 | // Dolby Prologic Processing Unit Control Selectors | ||
| 100 | pub const DP_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 101 | pub const DP_ENABLE_CONTROL: u8 = 0x01; | ||
| 102 | pub const DP_MODE_SELECT_CONTROL: u8 = 0x2; | ||
| 103 | |||
| 104 | // 3D Stereo Extender Processing Unit Control Selectors | ||
| 105 | pub const DDD_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 106 | pub const DDD_ENABLE_CONTROL: u8 = 0x01; | ||
| 107 | pub const DDD_SPACIOUSNESS_CONTROL: u8 = 0x03; | ||
| 108 | |||
| 109 | // Reverberation Processing Unit Control Selectors | ||
| 110 | pub const RV_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 111 | pub const RV_ENABLE_CONTROL: u8 = 0x01; | ||
| 112 | pub const REVERB_LEVEL_CONTROL: u8 = 0x02; | ||
| 113 | pub const REVERB_TIME_CONTROL: u8 = 0x03; | ||
| 114 | pub const REVERB_FEEDBACK_CONTROL: u8 = 0x04; | ||
| 115 | |||
| 116 | // Chorus Processing Unit Control Selectors | ||
| 117 | pub const CH_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 118 | pub const CH_ENABLE_CONTROL: u8 = 0x01; | ||
| 119 | pub const CHORUS_LEVEL_CONTROL: u8 = 0x02; | ||
| 120 | pub const CHORUS_RATE_CONTROL: u8 = 0x03; | ||
| 121 | pub const CHORUS_DEPTH_CONTROL: u8 = 0x04; | ||
| 122 | |||
| 123 | // Dynamic Range Compressor Processing Unit Control Selectors | ||
| 124 | pub const DR_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 125 | pub const DR_ENABLE_CONTROL: u8 = 0x01; | ||
| 126 | pub const COMPRESSION_RATE_CONTROL: u8 = 0x02; | ||
| 127 | pub const MAXAMPL_CONTROL: u8 = 0x03; | ||
| 128 | pub const THRESHOLD_CONTROL: u8 = 0x04; | ||
| 129 | pub const ATTACK_TIME: u8 = 0x05; | ||
| 130 | pub const RELEASE_TIME: u8 = 0x06; | ||
| 131 | |||
| 132 | // Extension Unit Control Selectors | ||
| 133 | pub const XU_CONTROL_UNDEFINED: u16 = 0x00; | ||
| 134 | pub const XU_ENABLE_CONTROL: u16 = 0x01; | ||
| 135 | |||
| 136 | // Endpoint Control Selectors | ||
| 137 | pub const EP_CONTROL_UNDEFINED: u8 = 0x00; | ||
| 138 | pub const SAMPLING_FREQ_CONTROL: u8 = 0x01; | ||
| 139 | pub const PITCH_CONTROL: u8 = 0x02; | ||
| 140 | |||
| 141 | // Format Type Codes | ||
| 142 | pub const FORMAT_TYPE_UNDEFINED: u8 = 0x00; | ||
| 143 | pub const FORMAT_TYPE_I: u8 = 0x01; | ||
| 144 | |||
| 145 | // Audio Data Format Type I Codes | ||
| 146 | pub const TYPE_I_UNDEFINED: u16 = 0x0000; | ||
| 147 | pub const PCM: u16 = 0x0001; | ||
| 148 | pub const PCM8: u16 = 0x0002; | ||
| 149 | pub const IEEE_FLOAT: u16 = 0x0003; | ||
| 150 | pub const ALAW: u16 = 0x0004; | ||
| 151 | pub const MULAW: u16 = 0x0005; | ||
diff --git a/embassy-usb/src/class/uac1/mod.rs b/embassy-usb/src/class/uac1/mod.rs new file mode 100644 index 000000000..3d5f4e524 --- /dev/null +++ b/embassy-usb/src/class/uac1/mod.rs | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | //! USB Audio Class 1.0 implementations for different applications. | ||
| 2 | //! | ||
| 3 | //! Contains: | ||
| 4 | //! - The `speaker` class with a single audio streaming interface (host to device) | ||
| 5 | |||
| 6 | pub mod speaker; | ||
| 7 | |||
| 8 | mod class_codes; | ||
| 9 | mod terminal_type; | ||
| 10 | |||
| 11 | /// The maximum supported audio channel index (corresponds to `Top`). | ||
| 12 | /// FIXME: Use `core::mem::variant_count(...)` when stabilized. | ||
| 13 | const MAX_AUDIO_CHANNEL_INDEX: usize = 12; | ||
| 14 | |||
| 15 | /// The maximum number of supported audio channels. | ||
| 16 | /// | ||
| 17 | /// Includes all twelve channels from `Channel`, plus the Master channel. | ||
| 18 | const MAX_AUDIO_CHANNEL_COUNT: usize = MAX_AUDIO_CHANNEL_INDEX + 1; | ||
| 19 | |||
| 20 | /// USB Audio Channel | ||
| 21 | #[derive(Debug, Clone, Copy, PartialEq)] | ||
| 22 | #[allow(missing_docs)] | ||
| 23 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 24 | pub enum Channel { | ||
| 25 | LeftFront, | ||
| 26 | RightFront, | ||
| 27 | CenterFront, | ||
| 28 | Lfe, | ||
| 29 | LeftSurround, | ||
| 30 | RightSurround, | ||
| 31 | LeftOfCenter, | ||
| 32 | RightOfCenter, | ||
| 33 | Surround, | ||
| 34 | SideLeft, | ||
| 35 | SideRight, | ||
| 36 | Top, | ||
| 37 | } | ||
| 38 | |||
| 39 | impl Channel { | ||
| 40 | /// Map a `Channel` to its corresponding USB Audio `ChannelConfig`. | ||
| 41 | fn get_channel_config(&self) -> ChannelConfig { | ||
| 42 | match self { | ||
| 43 | Channel::LeftFront => ChannelConfig::LeftFront, | ||
| 44 | Channel::RightFront => ChannelConfig::RightFront, | ||
| 45 | Channel::CenterFront => ChannelConfig::CenterFront, | ||
| 46 | Channel::Lfe => ChannelConfig::Lfe, | ||
| 47 | Channel::LeftSurround => ChannelConfig::LeftSurround, | ||
| 48 | Channel::RightSurround => ChannelConfig::RightSurround, | ||
| 49 | Channel::LeftOfCenter => ChannelConfig::LeftOfCenter, | ||
| 50 | Channel::RightOfCenter => ChannelConfig::RightOfCenter, | ||
| 51 | Channel::Surround => ChannelConfig::Surround, | ||
| 52 | Channel::SideLeft => ChannelConfig::SideLeft, | ||
| 53 | Channel::SideRight => ChannelConfig::SideRight, | ||
| 54 | Channel::Top => ChannelConfig::Top, | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | /// USB Audio Channel configuration | ||
| 60 | #[repr(u16)] | ||
| 61 | #[non_exhaustive] | ||
| 62 | // #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 63 | enum ChannelConfig { | ||
| 64 | None = 0x0000, | ||
| 65 | LeftFront = 0x0001, | ||
| 66 | RightFront = 0x0002, | ||
| 67 | CenterFront = 0x0004, | ||
| 68 | Lfe = 0x0008, | ||
| 69 | LeftSurround = 0x0010, | ||
| 70 | RightSurround = 0x0020, | ||
| 71 | LeftOfCenter = 0x0040, | ||
| 72 | RightOfCenter = 0x0080, | ||
| 73 | Surround = 0x0100, | ||
| 74 | SideLeft = 0x0200, | ||
| 75 | SideRight = 0x0400, | ||
| 76 | Top = 0x0800, | ||
| 77 | } | ||
| 78 | |||
| 79 | impl From<ChannelConfig> for u16 { | ||
| 80 | fn from(t: ChannelConfig) -> u16 { | ||
| 81 | t as u16 | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Feedback period adjustment `bRefresh` [UAC 3.7.2.2] | ||
| 86 | /// | ||
| 87 | /// From the specification: "A new Ff value is available every 2^(10 – P) frames with P ranging from 1 to 9. The | ||
| 88 | /// bRefresh field of the synch standard endpoint descriptor is used to report the exponent (10-P) to the Host." | ||
| 89 | /// | ||
| 90 | /// This means: | ||
| 91 | /// - 512 ms (2^9 frames) to 2 ms (2^1 frames) for USB full-speed | ||
| 92 | /// - 64 ms (2^9 microframes) to 0.25 ms (2^1 microframes) for USB high-speed | ||
| 93 | #[repr(u8)] | ||
| 94 | #[allow(missing_docs)] | ||
| 95 | #[derive(Clone, Copy)] | ||
| 96 | pub enum FeedbackRefresh { | ||
| 97 | Period2Frames = 1, | ||
| 98 | Period4Frames = 2, | ||
| 99 | Period8Frames = 3, | ||
| 100 | Period16Frames = 4, | ||
| 101 | Period32Frames = 5, | ||
| 102 | Period64Frames = 6, | ||
| 103 | Period128Frames = 7, | ||
| 104 | Period256Frames = 8, | ||
| 105 | Period512Frames = 9, | ||
| 106 | } | ||
| 107 | |||
| 108 | impl FeedbackRefresh { | ||
| 109 | /// Gets the number of frames, after which a new feedback frame is returned. | ||
| 110 | pub const fn frame_count(&self) -> usize { | ||
| 111 | 1 << (*self as usize) | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Audio sample width. | ||
| 116 | /// | ||
| 117 | /// Stored in number of bytes per sample. | ||
| 118 | #[repr(u8)] | ||
| 119 | #[derive(Clone, Copy)] | ||
| 120 | pub enum SampleWidth { | ||
| 121 | /// 16 bit audio | ||
| 122 | Width2Byte = 2, | ||
| 123 | /// 24 bit audio | ||
| 124 | Width3Byte = 3, | ||
| 125 | /// 32 bit audio | ||
| 126 | Width4Byte = 4, | ||
| 127 | } | ||
| 128 | |||
| 129 | impl SampleWidth { | ||
| 130 | /// Get the audio sample resolution in number of bit. | ||
| 131 | pub const fn in_bit(self) -> usize { | ||
| 132 | 8 * self as usize | ||
| 133 | } | ||
| 134 | } | ||
diff --git a/embassy-usb/src/class/uac1/speaker.rs b/embassy-usb/src/class/uac1/speaker.rs new file mode 100644 index 000000000..96456d94a --- /dev/null +++ b/embassy-usb/src/class/uac1/speaker.rs | |||
| @@ -0,0 +1,778 @@ | |||
| 1 | //! USB Audio Class 1.0 - Speaker device | ||
| 2 | //! | ||
| 3 | //! Provides a class with a single audio streaming interface (host to device), | ||
| 4 | //! that advertises itself as a speaker. Includes explicit sample rate feedback. | ||
| 5 | //! | ||
| 6 | //! Various aspects of the audio stream can be configured, for example: | ||
| 7 | //! - sample rate | ||
| 8 | //! - sample resolution | ||
| 9 | //! - audio channel count and assignment | ||
| 10 | //! | ||
| 11 | //! The class provides volume and mute controls for each channel. | ||
| 12 | |||
| 13 | use core::cell::{Cell, RefCell}; | ||
| 14 | use core::future::poll_fn; | ||
| 15 | use core::marker::PhantomData; | ||
| 16 | use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; | ||
| 17 | use core::task::Poll; | ||
| 18 | |||
| 19 | use embassy_sync::blocking_mutex::CriticalSectionMutex; | ||
| 20 | use embassy_sync::waitqueue::WakerRegistration; | ||
| 21 | use heapless::Vec; | ||
| 22 | |||
| 23 | use super::class_codes::*; | ||
| 24 | use super::terminal_type::TerminalType; | ||
| 25 | use super::{Channel, ChannelConfig, FeedbackRefresh, SampleWidth, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX}; | ||
| 26 | use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; | ||
| 27 | use crate::descriptor::{SynchronizationType, UsageType}; | ||
| 28 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut, EndpointType}; | ||
| 29 | use crate::types::InterfaceNumber; | ||
| 30 | use crate::{Builder, Handler}; | ||
| 31 | |||
| 32 | /// Maximum allowed sampling rate (3 bytes) in Hz. | ||
| 33 | const MAX_SAMPLE_RATE_HZ: u32 = 0x7FFFFF; | ||
| 34 | |||
| 35 | /// Arbitrary unique identifier for the input unit. | ||
| 36 | const INPUT_UNIT_ID: u8 = 0x01; | ||
| 37 | |||
| 38 | /// Arbitrary unique identifier for the feature unit. | ||
| 39 | const FEATURE_UNIT_ID: u8 = 0x02; | ||
| 40 | |||
| 41 | /// Arbitrary unique identifier for the output unit. | ||
| 42 | const OUTPUT_UNIT_ID: u8 = 0x03; | ||
| 43 | |||
| 44 | // Volume settings go from -25600 to 0, in steps of 256. | ||
| 45 | // Therefore, the volume settings are 8q8 values in units of dB. | ||
| 46 | const VOLUME_STEPS_PER_DB: i16 = 256; | ||
| 47 | const MIN_VOLUME_DB: i16 = -100; | ||
| 48 | const MAX_VOLUME_DB: i16 = 0; | ||
| 49 | |||
| 50 | // Maximum number of supported discrete sample rates. | ||
| 51 | const MAX_SAMPLE_RATE_COUNT: usize = 10; | ||
| 52 | |||
| 53 | /// The volume of an audio channel. | ||
| 54 | #[derive(Debug, Clone, Copy)] | ||
| 55 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 56 | pub enum Volume { | ||
| 57 | /// The channel is muted. | ||
| 58 | Muted, | ||
| 59 | /// The channel volume in dB. Ranges from `MIN_VOLUME_DB` (quietest) to `MAX_VOLUME_DB` (loudest). | ||
| 60 | DeciBel(f32), | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Internal state for the USB Audio Class. | ||
| 64 | pub struct State<'d> { | ||
| 65 | control: Option<Control<'d>>, | ||
| 66 | shared: SharedControl<'d>, | ||
| 67 | } | ||
| 68 | |||
| 69 | impl<'d> Default for State<'d> { | ||
| 70 | fn default() -> Self { | ||
| 71 | Self::new() | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl<'d> State<'d> { | ||
| 76 | /// Create a new `State`. | ||
| 77 | pub fn new() -> Self { | ||
| 78 | Self { | ||
| 79 | control: None, | ||
| 80 | shared: SharedControl::default(), | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Implementation of the USB audio class 1.0. | ||
| 86 | pub struct Speaker<'d, D: Driver<'d>> { | ||
| 87 | phantom: PhantomData<&'d D>, | ||
| 88 | } | ||
| 89 | |||
| 90 | impl<'d, D: Driver<'d>> Speaker<'d, D> { | ||
| 91 | /// Creates a new [`Speaker`] device, split into a stream, feedback, and a control change notifier. | ||
| 92 | /// | ||
| 93 | /// The packet size should be chosen, based on the expected transfer size of samples per (micro)frame. | ||
| 94 | /// For example, a stereo stream at 32 bit resolution and 48 kHz sample rate yields packets of 384 byte for | ||
| 95 | /// full-speed USB (1 ms frame interval) or 48 byte for high-speed USB (125 us microframe interval). | ||
| 96 | /// When using feedback, the packet size varies and thus, the `max_packet_size` should be increased (e.g. to double). | ||
| 97 | /// | ||
| 98 | /// # Arguments | ||
| 99 | /// | ||
| 100 | /// * `builder` - The builder for the class. | ||
| 101 | /// * `state` - The internal state of the class. | ||
| 102 | /// * `max_packet_size` - The maximum packet size per (micro)frame. | ||
| 103 | /// * `resolution` - The audio sample resolution. | ||
| 104 | /// * `sample_rates_hz` - The supported sample rates in Hz. | ||
| 105 | /// * `channels` - The advertised audio channels (up to 12). Entries must be unique, or this function panics. | ||
| 106 | /// * `feedback_refresh_period` - The refresh period for the feedback value. | ||
| 107 | pub fn new( | ||
| 108 | builder: &mut Builder<'d, D>, | ||
| 109 | state: &'d mut State<'d>, | ||
| 110 | max_packet_size: u16, | ||
| 111 | resolution: SampleWidth, | ||
| 112 | sample_rates_hz: &[u32], | ||
| 113 | channels: &'d [Channel], | ||
| 114 | feedback_refresh_period: FeedbackRefresh, | ||
| 115 | ) -> (Stream<'d, D>, Feedback<'d, D>, ControlMonitor<'d>) { | ||
| 116 | // The class and subclass fields of the IAD aren't required to match the class and subclass fields of | ||
| 117 | // the interfaces in the interface collection that the IAD describes. Microsoft recommends that | ||
| 118 | // the first interface of the collection has class and subclass fields that match the class and | ||
| 119 | // subclass fields of the IAD. | ||
| 120 | let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE); | ||
| 121 | |||
| 122 | // Audio control interface (mandatory) [UAC 4.3.1] | ||
| 123 | let mut interface = func.interface(); | ||
| 124 | let control_interface = interface.interface_number().into(); | ||
| 125 | let streaming_interface = u8::from(control_interface) + 1; | ||
| 126 | let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None); | ||
| 127 | |||
| 128 | // Terminal topology: | ||
| 129 | // Input terminal (receives audio stream) -> Feature Unit (mute and volume) -> Output terminal (e.g. towards speaker) | ||
| 130 | |||
| 131 | // ======================================= | ||
| 132 | // Input Terminal Descriptor [UAC 3.3.2.1] | ||
| 133 | // Audio input | ||
| 134 | let terminal_type: u16 = TerminalType::UsbStreaming.into(); | ||
| 135 | |||
| 136 | // Assemble channel configuration field | ||
| 137 | let mut channel_config: u16 = ChannelConfig::None.into(); | ||
| 138 | for channel in channels { | ||
| 139 | let channel: u16 = channel.get_channel_config().into(); | ||
| 140 | |||
| 141 | if channel_config & channel != 0 { | ||
| 142 | panic!("Invalid channel config, duplicate channel {}.", channel); | ||
| 143 | } | ||
| 144 | channel_config |= channel; | ||
| 145 | } | ||
| 146 | |||
| 147 | let input_terminal_descriptor = [ | ||
| 148 | INPUT_TERMINAL, // bDescriptorSubtype | ||
| 149 | INPUT_UNIT_ID, // bTerminalID | ||
| 150 | terminal_type as u8, | ||
| 151 | (terminal_type >> 8) as u8, // wTerminalType | ||
| 152 | 0x00, // bAssocTerminal (none) | ||
| 153 | channels.len() as u8, // bNrChannels | ||
| 154 | channel_config as u8, | ||
| 155 | (channel_config >> 8) as u8, // wChannelConfig | ||
| 156 | 0x00, // iChannelNames (none) | ||
| 157 | 0x00, // iTerminal (none) | ||
| 158 | ]; | ||
| 159 | |||
| 160 | // ======================================== | ||
| 161 | // Output Terminal Descriptor [UAC 4.3.2.2] | ||
| 162 | // Speaker output | ||
| 163 | let terminal_type: u16 = TerminalType::OutSpeaker.into(); | ||
| 164 | let output_terminal_descriptor = [ | ||
| 165 | OUTPUT_TERMINAL, // bDescriptorSubtype | ||
| 166 | OUTPUT_UNIT_ID, // bTerminalID | ||
| 167 | terminal_type as u8, | ||
| 168 | (terminal_type >> 8) as u8, // wTerminalType | ||
| 169 | 0x00, // bAssocTerminal (none) | ||
| 170 | FEATURE_UNIT_ID, // bSourceID (the feature unit) | ||
| 171 | 0x00, // iTerminal (none) | ||
| 172 | ]; | ||
| 173 | |||
| 174 | // ===================================== | ||
| 175 | // Feature Unit Descriptor [UAC 4.3.2.5] | ||
| 176 | // Mute and volume control | ||
| 177 | let controls = MUTE_CONTROL | VOLUME_CONTROL; | ||
| 178 | |||
| 179 | const FEATURE_UNIT_DESCRIPTOR_SIZE: usize = 5; | ||
| 180 | let mut feature_unit_descriptor: Vec<u8, { FEATURE_UNIT_DESCRIPTOR_SIZE + MAX_AUDIO_CHANNEL_COUNT + 1 }> = | ||
| 181 | Vec::from_slice(&[ | ||
| 182 | FEATURE_UNIT, // bDescriptorSubtype (Feature Unit) | ||
| 183 | FEATURE_UNIT_ID, // bUnitID | ||
| 184 | INPUT_UNIT_ID, // bSourceID | ||
| 185 | 1, // bControlSize (one byte per control) | ||
| 186 | FU_CONTROL_UNDEFINED, // Master controls (disabled, use only per-channel control) | ||
| 187 | ]) | ||
| 188 | .unwrap(); | ||
| 189 | |||
| 190 | // Add per-channel controls | ||
| 191 | for _channel in channels { | ||
| 192 | feature_unit_descriptor.push(controls).unwrap(); | ||
| 193 | } | ||
| 194 | feature_unit_descriptor.push(0x00).unwrap(); // iFeature (none) | ||
| 195 | |||
| 196 | // =============================================== | ||
| 197 | // Format desciptor [UAC 4.5.3] | ||
| 198 | // Used later, for operational streaming interface | ||
| 199 | let mut format_descriptor: Vec<u8, { 6 + 3 * MAX_SAMPLE_RATE_COUNT }> = Vec::from_slice(&[ | ||
| 200 | FORMAT_TYPE, // bDescriptorSubtype | ||
| 201 | FORMAT_TYPE_I, // bFormatType | ||
| 202 | channels.len() as u8, // bNrChannels | ||
| 203 | resolution as u8, // bSubframeSize | ||
| 204 | resolution.in_bit() as u8, // bBitResolution | ||
| 205 | ]) | ||
| 206 | .unwrap(); | ||
| 207 | |||
| 208 | format_descriptor.push(sample_rates_hz.len() as u8).unwrap(); | ||
| 209 | |||
| 210 | for sample_rate_hz in sample_rates_hz { | ||
| 211 | assert!(*sample_rate_hz <= MAX_SAMPLE_RATE_HZ); | ||
| 212 | format_descriptor.push((sample_rate_hz & 0xFF) as u8).unwrap(); | ||
| 213 | format_descriptor.push(((sample_rate_hz >> 8) & 0xFF) as u8).unwrap(); | ||
| 214 | format_descriptor.push(((sample_rate_hz >> 16) & 0xFF) as u8).unwrap(); | ||
| 215 | } | ||
| 216 | |||
| 217 | // ================================================== | ||
| 218 | // Class-specific AC Interface Descriptor [UAC 4.3.2] | ||
| 219 | const DESCRIPTOR_HEADER_SIZE: usize = 2; | ||
| 220 | const INTERFACE_DESCRIPTOR_SIZE: usize = 7; | ||
| 221 | |||
| 222 | let mut total_descriptor_length = 0; | ||
| 223 | |||
| 224 | for size in [ | ||
| 225 | INTERFACE_DESCRIPTOR_SIZE, | ||
| 226 | input_terminal_descriptor.len(), | ||
| 227 | feature_unit_descriptor.len(), | ||
| 228 | output_terminal_descriptor.len(), | ||
| 229 | ] { | ||
| 230 | total_descriptor_length += size + DESCRIPTOR_HEADER_SIZE; | ||
| 231 | } | ||
| 232 | |||
| 233 | let interface_descriptor: [u8; INTERFACE_DESCRIPTOR_SIZE] = [ | ||
| 234 | HEADER_SUBTYPE, // bDescriptorSubtype (Header) | ||
| 235 | ADC_VERSION as u8, | ||
| 236 | (ADC_VERSION >> 8) as u8, // bcdADC | ||
| 237 | total_descriptor_length as u8, | ||
| 238 | (total_descriptor_length >> 8) as u8, // wTotalLength | ||
| 239 | 0x01, // bInCollection (1 streaming interface) | ||
| 240 | streaming_interface, // baInterfaceNr | ||
| 241 | ]; | ||
| 242 | |||
| 243 | alt.descriptor(CS_INTERFACE, &interface_descriptor); | ||
| 244 | alt.descriptor(CS_INTERFACE, &input_terminal_descriptor); | ||
| 245 | alt.descriptor(CS_INTERFACE, &feature_unit_descriptor); | ||
| 246 | alt.descriptor(CS_INTERFACE, &output_terminal_descriptor); | ||
| 247 | |||
| 248 | // ===================================================== | ||
| 249 | // Audio streaming interface, zero-bandwidth [UAC 4.5.1] | ||
| 250 | let mut interface = func.interface(); | ||
| 251 | let alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None); | ||
| 252 | drop(alt); | ||
| 253 | |||
| 254 | // ================================================== | ||
| 255 | // Audio streaming interface, operational [UAC 4.5.1] | ||
| 256 | let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None); | ||
| 257 | |||
| 258 | alt.descriptor( | ||
| 259 | CS_INTERFACE, | ||
| 260 | &[ | ||
| 261 | AS_GENERAL, // bDescriptorSubtype | ||
| 262 | INPUT_UNIT_ID, // bTerminalLink | ||
| 263 | 0x00, // bDelay (none) | ||
| 264 | PCM as u8, | ||
| 265 | (PCM >> 8) as u8, // wFormatTag (PCM format) | ||
| 266 | ], | ||
| 267 | ); | ||
| 268 | |||
| 269 | alt.descriptor(CS_INTERFACE, &format_descriptor); | ||
| 270 | |||
| 271 | let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, max_packet_size, 1); | ||
| 272 | let feedback_endpoint = alt.alloc_endpoint_in( | ||
| 273 | EndpointType::Isochronous, | ||
| 274 | 4, // Feedback packets are 24 bit (10.14 format). | ||
| 275 | 1, | ||
| 276 | ); | ||
| 277 | |||
| 278 | // Write the descriptor for the streaming endpoint, after knowing the address of the feedback endpoint. | ||
| 279 | alt.endpoint_descriptor( | ||
| 280 | streaming_endpoint.info(), | ||
| 281 | SynchronizationType::Asynchronous, | ||
| 282 | UsageType::DataEndpoint, | ||
| 283 | &[ | ||
| 284 | 0x00, // bRefresh (0) | ||
| 285 | feedback_endpoint.info().addr.into(), // bSynchAddress (the feedback endpoint) | ||
| 286 | ], | ||
| 287 | ); | ||
| 288 | |||
| 289 | alt.descriptor( | ||
| 290 | CS_ENDPOINT, | ||
| 291 | &[ | ||
| 292 | AS_GENERAL, // bDescriptorSubtype (General) | ||
| 293 | SAMPLING_FREQ_CONTROL, // bmAttributes (support sampling frequency control) | ||
| 294 | 0x02, // bLockDelayUnits (PCM) | ||
| 295 | 0x0000 as u8, | ||
| 296 | (0x0000 >> 8) as u8, // wLockDelay (0) | ||
| 297 | ], | ||
| 298 | ); | ||
| 299 | |||
| 300 | // Write the feedback endpoint descriptor after the streaming endpoint descriptor | ||
| 301 | // This is demanded by the USB audio class specification. | ||
| 302 | alt.endpoint_descriptor( | ||
| 303 | feedback_endpoint.info(), | ||
| 304 | SynchronizationType::NoSynchronization, | ||
| 305 | UsageType::FeedbackEndpoint, | ||
| 306 | &[ | ||
| 307 | feedback_refresh_period as u8, // bRefresh | ||
| 308 | 0x00, // bSynchAddress (none) | ||
| 309 | ], | ||
| 310 | ); | ||
| 311 | |||
| 312 | // Free up the builder. | ||
| 313 | drop(func); | ||
| 314 | |||
| 315 | // Store channel information | ||
| 316 | state.shared.channels = channels; | ||
| 317 | |||
| 318 | state.control = Some(Control { | ||
| 319 | shared: &state.shared, | ||
| 320 | streaming_endpoint_address: streaming_endpoint.info().addr.into(), | ||
| 321 | control_interface_number: control_interface, | ||
| 322 | }); | ||
| 323 | |||
| 324 | builder.handler(state.control.as_mut().unwrap()); | ||
| 325 | |||
| 326 | let control = &state.shared; | ||
| 327 | |||
| 328 | ( | ||
| 329 | Stream { streaming_endpoint }, | ||
| 330 | Feedback { feedback_endpoint }, | ||
| 331 | ControlMonitor { shared: control }, | ||
| 332 | ) | ||
| 333 | } | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Audio settings for the feature unit. | ||
| 337 | /// | ||
| 338 | /// Contains volume and mute control. | ||
| 339 | #[derive(Clone, Copy, Debug)] | ||
| 340 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 341 | pub struct AudioSettings { | ||
| 342 | /// Channel mute states. | ||
| 343 | muted: [bool; MAX_AUDIO_CHANNEL_COUNT], | ||
| 344 | /// Channel volume levels in 8.8 format (in dB). | ||
| 345 | volume_8q8_db: [i16; MAX_AUDIO_CHANNEL_COUNT], | ||
| 346 | } | ||
| 347 | |||
| 348 | impl Default for AudioSettings { | ||
| 349 | fn default() -> Self { | ||
| 350 | AudioSettings { | ||
| 351 | muted: [true; MAX_AUDIO_CHANNEL_COUNT], | ||
| 352 | volume_8q8_db: [MAX_VOLUME_DB * VOLUME_STEPS_PER_DB; MAX_AUDIO_CHANNEL_COUNT], | ||
| 353 | } | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | struct Control<'d> { | ||
| 358 | control_interface_number: InterfaceNumber, | ||
| 359 | streaming_endpoint_address: u8, | ||
| 360 | shared: &'d SharedControl<'d>, | ||
| 361 | } | ||
| 362 | |||
| 363 | /// Shared data between [`Control`] and the [`Speaker`] class. | ||
| 364 | struct SharedControl<'d> { | ||
| 365 | /// The collection of audio settings (volumes, mute states). | ||
| 366 | audio_settings: CriticalSectionMutex<Cell<AudioSettings>>, | ||
| 367 | |||
| 368 | /// Channel assignments. | ||
| 369 | channels: &'d [Channel], | ||
| 370 | |||
| 371 | /// The audio sample rate in Hz. | ||
| 372 | sample_rate_hz: AtomicU32, | ||
| 373 | |||
| 374 | // Notification mechanism. | ||
| 375 | waker: RefCell<WakerRegistration>, | ||
| 376 | changed: AtomicBool, | ||
| 377 | } | ||
| 378 | |||
| 379 | impl<'d> Default for SharedControl<'d> { | ||
| 380 | fn default() -> Self { | ||
| 381 | SharedControl { | ||
| 382 | audio_settings: CriticalSectionMutex::new(Cell::new(AudioSettings::default())), | ||
| 383 | channels: &[], | ||
| 384 | sample_rate_hz: AtomicU32::new(0), | ||
| 385 | waker: RefCell::new(WakerRegistration::new()), | ||
| 386 | changed: AtomicBool::new(false), | ||
| 387 | } | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | impl<'d> SharedControl<'d> { | ||
| 392 | async fn changed(&self) { | ||
| 393 | poll_fn(|context| { | ||
| 394 | if self.changed.load(Ordering::Relaxed) { | ||
| 395 | self.changed.store(false, Ordering::Relaxed); | ||
| 396 | Poll::Ready(()) | ||
| 397 | } else { | ||
| 398 | self.waker.borrow_mut().register(context.waker()); | ||
| 399 | Poll::Pending | ||
| 400 | } | ||
| 401 | }) | ||
| 402 | .await; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | /// Used for reading audio frames. | ||
| 407 | pub struct Stream<'d, D: Driver<'d>> { | ||
| 408 | streaming_endpoint: D::EndpointOut, | ||
| 409 | } | ||
| 410 | |||
| 411 | impl<'d, D: Driver<'d>> Stream<'d, D> { | ||
| 412 | /// Reads a single packet from the OUT endpoint | ||
| 413 | pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> { | ||
| 414 | self.streaming_endpoint.read(data).await | ||
| 415 | } | ||
| 416 | |||
| 417 | /// Waits for the USB host to enable this interface | ||
| 418 | pub async fn wait_connection(&mut self) { | ||
| 419 | self.streaming_endpoint.wait_enabled().await; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | /// Used for writing sample rate information over the feedback endpoint. | ||
| 424 | pub struct Feedback<'d, D: Driver<'d>> { | ||
| 425 | feedback_endpoint: D::EndpointIn, | ||
| 426 | } | ||
| 427 | |||
| 428 | impl<'d, D: Driver<'d>> Feedback<'d, D> { | ||
| 429 | /// Writes a single packet into the IN endpoint. | ||
| 430 | pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { | ||
| 431 | self.feedback_endpoint.write(data).await | ||
| 432 | } | ||
| 433 | |||
| 434 | /// Waits for the USB host to enable this interface. | ||
| 435 | pub async fn wait_connection(&mut self) { | ||
| 436 | self.feedback_endpoint.wait_enabled().await; | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | /// Control status change monitor | ||
| 441 | /// | ||
| 442 | /// Await [`ControlMonitor::changed`] for being notified of configuration changes. Afterwards, the updated | ||
| 443 | /// configuration settings can be read with [`ControlMonitor::volume`] and [`ControlMonitor::sample_rate_hz`]. | ||
| 444 | pub struct ControlMonitor<'d> { | ||
| 445 | shared: &'d SharedControl<'d>, | ||
| 446 | } | ||
| 447 | |||
| 448 | impl<'d> ControlMonitor<'d> { | ||
| 449 | fn audio_settings(&self) -> AudioSettings { | ||
| 450 | let audio_settings = self.shared.audio_settings.lock(|x| x.get()); | ||
| 451 | |||
| 452 | audio_settings | ||
| 453 | } | ||
| 454 | |||
| 455 | fn get_logical_channel(&self, search_channel: Channel) -> Option<usize> { | ||
| 456 | let index = self.shared.channels.iter().position(|&c| c == search_channel)?; | ||
| 457 | |||
| 458 | // The logical channels start at one (zero is the master channel). | ||
| 459 | Some(index + 1) | ||
| 460 | } | ||
| 461 | |||
| 462 | /// Get the volume of a selected channel. | ||
| 463 | pub fn volume(&self, channel: Channel) -> Option<Volume> { | ||
| 464 | let channel_index = self.get_logical_channel(channel)?; | ||
| 465 | |||
| 466 | if self.audio_settings().muted[channel_index] { | ||
| 467 | return Some(Volume::Muted); | ||
| 468 | } | ||
| 469 | |||
| 470 | Some(Volume::DeciBel( | ||
| 471 | (self.audio_settings().volume_8q8_db[channel_index] as f32) / 256.0f32, | ||
| 472 | )) | ||
| 473 | } | ||
| 474 | |||
| 475 | /// Get the streaming endpoint's sample rate in Hz. | ||
| 476 | pub fn sample_rate_hz(&self) -> u32 { | ||
| 477 | self.shared.sample_rate_hz.load(Ordering::Relaxed) | ||
| 478 | } | ||
| 479 | |||
| 480 | /// Return a future for when the control settings change. | ||
| 481 | pub async fn changed(&self) { | ||
| 482 | self.shared.changed().await; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | impl<'d> Control<'d> { | ||
| 487 | fn changed(&mut self) { | ||
| 488 | self.shared.changed.store(true, Ordering::Relaxed); | ||
| 489 | self.shared.waker.borrow_mut().wake(); | ||
| 490 | } | ||
| 491 | |||
| 492 | fn interface_set_mute_state( | ||
| 493 | &mut self, | ||
| 494 | audio_settings: &mut AudioSettings, | ||
| 495 | channel_index: u8, | ||
| 496 | data: &[u8], | ||
| 497 | ) -> OutResponse { | ||
| 498 | let mute_state = data[0] != 0; | ||
| 499 | |||
| 500 | match channel_index as usize { | ||
| 501 | ..=MAX_AUDIO_CHANNEL_INDEX => { | ||
| 502 | audio_settings.muted[channel_index as usize] = mute_state; | ||
| 503 | } | ||
| 504 | _ => { | ||
| 505 | debug!("Failed to set channel {} mute state: {}", channel_index, mute_state); | ||
| 506 | return OutResponse::Rejected; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | debug!("Set channel {} mute state: {}", channel_index, mute_state); | ||
| 511 | OutResponse::Accepted | ||
| 512 | } | ||
| 513 | |||
| 514 | fn interface_set_volume( | ||
| 515 | &mut self, | ||
| 516 | audio_settings: &mut AudioSettings, | ||
| 517 | channel_index: u8, | ||
| 518 | data: &[u8], | ||
| 519 | ) -> OutResponse { | ||
| 520 | let volume = i16::from_ne_bytes(data[..2].try_into().expect("Failed to read volume.")); | ||
| 521 | |||
| 522 | match channel_index as usize { | ||
| 523 | ..=MAX_AUDIO_CHANNEL_INDEX => { | ||
| 524 | audio_settings.volume_8q8_db[channel_index as usize] = volume; | ||
| 525 | } | ||
| 526 | _ => { | ||
| 527 | debug!("Failed to set channel {} volume: {}", channel_index, volume); | ||
| 528 | return OutResponse::Rejected; | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | debug!("Set channel {} volume: {}", channel_index, volume); | ||
| 533 | OutResponse::Accepted | ||
| 534 | } | ||
| 535 | |||
| 536 | fn interface_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> { | ||
| 537 | let interface_number = req.index as u8; | ||
| 538 | let entity_index = (req.index >> 8) as u8; | ||
| 539 | let channel_index = req.value as u8; | ||
| 540 | let control_unit = (req.value >> 8) as u8; | ||
| 541 | |||
| 542 | if interface_number != self.control_interface_number.into() { | ||
| 543 | debug!("Unhandled interface set request for interface {}", interface_number); | ||
| 544 | return None; | ||
| 545 | } | ||
| 546 | |||
| 547 | if entity_index != FEATURE_UNIT_ID { | ||
| 548 | debug!("Unsupported interface set request for entity {}", entity_index); | ||
| 549 | return Some(OutResponse::Rejected); | ||
| 550 | } | ||
| 551 | |||
| 552 | if req.request != SET_CUR { | ||
| 553 | debug!("Unsupported interface set request type {}", req.request); | ||
| 554 | return Some(OutResponse::Rejected); | ||
| 555 | } | ||
| 556 | |||
| 557 | let mut audio_settings = self.shared.audio_settings.lock(|x| x.get()); | ||
| 558 | let response = match control_unit { | ||
| 559 | MUTE_CONTROL => self.interface_set_mute_state(&mut audio_settings, channel_index, data), | ||
| 560 | VOLUME_CONTROL => self.interface_set_volume(&mut audio_settings, channel_index, data), | ||
| 561 | _ => OutResponse::Rejected, | ||
| 562 | }; | ||
| 563 | |||
| 564 | if response == OutResponse::Rejected { | ||
| 565 | return Some(response); | ||
| 566 | } | ||
| 567 | |||
| 568 | // Store updated settings | ||
| 569 | self.shared.audio_settings.lock(|x| x.set(audio_settings)); | ||
| 570 | |||
| 571 | self.changed(); | ||
| 572 | |||
| 573 | Some(OutResponse::Accepted) | ||
| 574 | } | ||
| 575 | |||
| 576 | fn endpoint_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> { | ||
| 577 | let control_selector = (req.value >> 8) as u8; | ||
| 578 | let endpoint_address = req.index as u8; | ||
| 579 | |||
| 580 | if endpoint_address != self.streaming_endpoint_address { | ||
| 581 | debug!( | ||
| 582 | "Unhandled endpoint set request for endpoint {} and control {} with data {}", | ||
| 583 | endpoint_address, control_selector, data | ||
| 584 | ); | ||
| 585 | return None; | ||
| 586 | } | ||
| 587 | |||
| 588 | if control_selector != SAMPLING_FREQ_CONTROL { | ||
| 589 | debug!( | ||
| 590 | "Unsupported endpoint set request for control selector {}", | ||
| 591 | control_selector | ||
| 592 | ); | ||
| 593 | return Some(OutResponse::Rejected); | ||
| 594 | } | ||
| 595 | |||
| 596 | let sample_rate_hz: u32 = (data[0] as u32) | (data[1] as u32) << 8 | (data[2] as u32) << 16; | ||
| 597 | self.shared.sample_rate_hz.store(sample_rate_hz, Ordering::Relaxed); | ||
| 598 | |||
| 599 | debug!("Set endpoint {} sample rate to {} Hz", endpoint_address, sample_rate_hz); | ||
| 600 | |||
| 601 | self.changed(); | ||
| 602 | |||
| 603 | Some(OutResponse::Accepted) | ||
| 604 | } | ||
| 605 | |||
| 606 | fn interface_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option<InResponse<'r>> { | ||
| 607 | let interface_number = req.index as u8; | ||
| 608 | let entity_index = (req.index >> 8) as u8; | ||
| 609 | let channel_index = req.value as u8; | ||
| 610 | let control_unit = (req.value >> 8) as u8; | ||
| 611 | |||
| 612 | if interface_number != self.control_interface_number.into() { | ||
| 613 | debug!("Unhandled interface get request for interface {}.", interface_number); | ||
| 614 | return None; | ||
| 615 | } | ||
| 616 | |||
| 617 | if entity_index != FEATURE_UNIT_ID { | ||
| 618 | // Only this function unit can be handled at the moment. | ||
| 619 | debug!("Unsupported interface get request for entity {}.", entity_index); | ||
| 620 | return Some(InResponse::Rejected); | ||
| 621 | } | ||
| 622 | |||
| 623 | let audio_settings = self.shared.audio_settings.lock(|x| x.get()); | ||
| 624 | |||
| 625 | match req.request { | ||
| 626 | GET_CUR => match control_unit { | ||
| 627 | VOLUME_CONTROL => { | ||
| 628 | let volume: i16; | ||
| 629 | |||
| 630 | match channel_index as usize { | ||
| 631 | ..=MAX_AUDIO_CHANNEL_INDEX => volume = audio_settings.volume_8q8_db[channel_index as usize], | ||
| 632 | _ => return Some(InResponse::Rejected), | ||
| 633 | } | ||
| 634 | |||
| 635 | buf[0] = volume as u8; | ||
| 636 | buf[1] = (volume >> 8) as u8; | ||
| 637 | |||
| 638 | debug!("Got channel {} volume: {}.", channel_index, volume); | ||
| 639 | return Some(InResponse::Accepted(&buf[..2])); | ||
| 640 | } | ||
| 641 | MUTE_CONTROL => { | ||
| 642 | let mute_state: bool; | ||
| 643 | |||
| 644 | match channel_index as usize { | ||
| 645 | ..=MAX_AUDIO_CHANNEL_INDEX => mute_state = audio_settings.muted[channel_index as usize], | ||
| 646 | _ => return Some(InResponse::Rejected), | ||
| 647 | } | ||
| 648 | |||
| 649 | buf[0] = mute_state.into(); | ||
| 650 | debug!("Got channel {} mute state: {}.", channel_index, mute_state); | ||
| 651 | return Some(InResponse::Accepted(&buf[..1])); | ||
| 652 | } | ||
| 653 | _ => return Some(InResponse::Rejected), | ||
| 654 | }, | ||
| 655 | GET_MIN => match control_unit { | ||
| 656 | VOLUME_CONTROL => { | ||
| 657 | let min_volume = MIN_VOLUME_DB * VOLUME_STEPS_PER_DB; | ||
| 658 | buf[0] = min_volume as u8; | ||
| 659 | buf[1] = (min_volume >> 8) as u8; | ||
| 660 | return Some(InResponse::Accepted(&buf[..2])); | ||
| 661 | } | ||
| 662 | _ => return Some(InResponse::Rejected), | ||
| 663 | }, | ||
| 664 | GET_MAX => match control_unit { | ||
| 665 | VOLUME_CONTROL => { | ||
| 666 | let max_volume = MAX_VOLUME_DB * VOLUME_STEPS_PER_DB; | ||
| 667 | buf[0] = max_volume as u8; | ||
| 668 | buf[1] = (max_volume >> 8) as u8; | ||
| 669 | return Some(InResponse::Accepted(&buf[..2])); | ||
| 670 | } | ||
| 671 | _ => return Some(InResponse::Rejected), | ||
| 672 | }, | ||
| 673 | GET_RES => match control_unit { | ||
| 674 | VOLUME_CONTROL => { | ||
| 675 | buf[0] = VOLUME_STEPS_PER_DB as u8; | ||
| 676 | buf[1] = (VOLUME_STEPS_PER_DB >> 8) as u8; | ||
| 677 | return Some(InResponse::Accepted(&buf[..2])); | ||
| 678 | } | ||
| 679 | _ => return Some(InResponse::Rejected), | ||
| 680 | }, | ||
| 681 | _ => return Some(InResponse::Rejected), | ||
| 682 | } | ||
| 683 | } | ||
| 684 | |||
| 685 | fn endpoint_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option<InResponse<'r>> { | ||
| 686 | let control_selector = (req.value >> 8) as u8; | ||
| 687 | let endpoint_address = req.index as u8; | ||
| 688 | |||
| 689 | if endpoint_address != self.streaming_endpoint_address { | ||
| 690 | debug!("Unhandled endpoint get request for endpoint {}.", endpoint_address); | ||
| 691 | return None; | ||
| 692 | } | ||
| 693 | |||
| 694 | if control_selector != SAMPLING_FREQ_CONTROL as u8 { | ||
| 695 | debug!( | ||
| 696 | "Unsupported endpoint get request for control selector {}.", | ||
| 697 | control_selector | ||
| 698 | ); | ||
| 699 | return Some(InResponse::Rejected); | ||
| 700 | } | ||
| 701 | |||
| 702 | let sample_rate_hz = self.shared.sample_rate_hz.load(Ordering::Relaxed); | ||
| 703 | |||
| 704 | buf[0] = (sample_rate_hz & 0xFF) as u8; | ||
| 705 | buf[1] = ((sample_rate_hz >> 8) & 0xFF) as u8; | ||
| 706 | buf[2] = ((sample_rate_hz >> 16) & 0xFF) as u8; | ||
| 707 | |||
| 708 | Some(InResponse::Accepted(&buf[..3])) | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | impl<'d> Handler for Control<'d> { | ||
| 713 | /// Called when the USB device has been enabled or disabled. | ||
| 714 | fn enabled(&mut self, enabled: bool) { | ||
| 715 | debug!("USB device enabled: {}", enabled); | ||
| 716 | } | ||
| 717 | |||
| 718 | /// Called when the host has set the address of the device to `addr`. | ||
| 719 | fn addressed(&mut self, addr: u8) { | ||
| 720 | debug!("Host set address to: {}", addr); | ||
| 721 | } | ||
| 722 | |||
| 723 | /// Called when the host has enabled or disabled the configuration of the device. | ||
| 724 | fn configured(&mut self, configured: bool) { | ||
| 725 | debug!("USB device configured: {}", configured); | ||
| 726 | } | ||
| 727 | |||
| 728 | /// Called when remote wakeup feature is enabled or disabled. | ||
| 729 | fn remote_wakeup_enabled(&mut self, enabled: bool) { | ||
| 730 | debug!("USB remote wakeup enabled: {}", enabled); | ||
| 731 | } | ||
| 732 | |||
| 733 | /// Called when a "set alternate setting" control request is done on the interface. | ||
| 734 | fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { | ||
| 735 | debug!( | ||
| 736 | "USB set interface number {} to alt setting {}.", | ||
| 737 | iface, alternate_setting | ||
| 738 | ); | ||
| 739 | } | ||
| 740 | |||
| 741 | /// Called after a USB reset after the bus reset sequence is complete. | ||
| 742 | fn reset(&mut self) { | ||
| 743 | let shared = self.shared; | ||
| 744 | shared.audio_settings.lock(|x| x.set(AudioSettings::default())); | ||
| 745 | |||
| 746 | shared.changed.store(true, Ordering::Relaxed); | ||
| 747 | shared.waker.borrow_mut().wake(); | ||
| 748 | } | ||
| 749 | |||
| 750 | /// Called when the bus has entered or exited the suspend state. | ||
| 751 | fn suspended(&mut self, suspended: bool) { | ||
| 752 | debug!("USB device suspended: {}", suspended); | ||
| 753 | } | ||
| 754 | |||
| 755 | // Handle control set requests. | ||
| 756 | fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> { | ||
| 757 | match req.request_type { | ||
| 758 | RequestType::Class => match req.recipient { | ||
| 759 | Recipient::Interface => self.interface_set_request(req, data), | ||
| 760 | Recipient::Endpoint => self.endpoint_set_request(req, data), | ||
| 761 | _ => Some(OutResponse::Rejected), | ||
| 762 | }, | ||
| 763 | _ => None, | ||
| 764 | } | ||
| 765 | } | ||
| 766 | |||
| 767 | // Handle control get requests. | ||
| 768 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> { | ||
| 769 | match req.request_type { | ||
| 770 | RequestType::Class => match req.recipient { | ||
| 771 | Recipient::Interface => self.interface_get_request(req, buf), | ||
| 772 | Recipient::Endpoint => self.endpoint_get_request(req, buf), | ||
| 773 | _ => None, | ||
| 774 | }, | ||
| 775 | _ => None, | ||
| 776 | } | ||
| 777 | } | ||
| 778 | } | ||
diff --git a/embassy-usb/src/class/uac1/terminal_type.rs b/embassy-usb/src/class/uac1/terminal_type.rs new file mode 100644 index 000000000..65474a714 --- /dev/null +++ b/embassy-usb/src/class/uac1/terminal_type.rs | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | //! USB Audio Terminal Types from Universal Serial Bus Device Class Definition | ||
| 2 | //! for Terminal Types, Release 1.0 | ||
| 3 | |||
| 4 | /// USB Audio Terminal Types from "Universal Serial Bus Device Class Definition | ||
| 5 | /// for Terminal Types, Release 1.0" | ||
| 6 | #[repr(u16)] | ||
| 7 | #[non_exhaustive] | ||
| 8 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 9 | #[allow(missing_docs)] | ||
| 10 | pub enum TerminalType { | ||
| 11 | // USB Terminal Types | ||
| 12 | UsbUndefined = 0x0100, | ||
| 13 | UsbStreaming = 0x0101, | ||
| 14 | UsbVendor = 0x01ff, | ||
| 15 | |||
| 16 | // Input Terminal Types | ||
| 17 | InUndefined = 0x0200, | ||
| 18 | InMicrophone = 0x0201, | ||
| 19 | InDesktopMicrophone = 0x0202, | ||
| 20 | InPersonalMicrophone = 0x0203, | ||
| 21 | InOmniDirectionalMicrophone = 0x0204, | ||
| 22 | InMicrophoneArray = 0x0205, | ||
| 23 | InProcessingMicrophoneArray = 0x0206, | ||
| 24 | |||
| 25 | // Output Terminal Types | ||
| 26 | OutUndefined = 0x0300, | ||
| 27 | OutSpeaker = 0x0301, | ||
| 28 | OutHeadphones = 0x0302, | ||
| 29 | OutHeadMountedDisplayAudio = 0x0303, | ||
| 30 | OutDesktopSpeaker = 0x0304, | ||
| 31 | OutRoomSpeaker = 0x0305, | ||
| 32 | OutCommunicationSpeaker = 0x0306, | ||
| 33 | OutLowFrequencyEffectsSpeaker = 0x0307, | ||
| 34 | |||
| 35 | // External Terminal Types | ||
| 36 | ExtUndefined = 0x0600, | ||
| 37 | ExtAnalogConnector = 0x0601, | ||
| 38 | ExtDigitalAudioInterface = 0x0602, | ||
| 39 | ExtLineConnector = 0x0603, | ||
| 40 | ExtLegacyAudioConnector = 0x0604, | ||
| 41 | ExtSpdifConnector = 0x0605, | ||
| 42 | Ext1394DaStream = 0x0606, | ||
| 43 | Ext1394DvStreamSoundtrack = 0x0607, | ||
| 44 | } | ||
| 45 | |||
| 46 | impl From<TerminalType> for u16 { | ||
| 47 | fn from(t: TerminalType) -> u16 { | ||
| 48 | t as u16 | ||
| 49 | } | ||
| 50 | } | ||
