usbd_audio/
lib.rs

1//! USB Audio class
2//!
3//! This crate provides a USB device class based on "Universal Serial Bus Device
4//! Class Definition for Audio Devices", Release 1.0 (experimental
5//! implementation without the aim of standard compliance).
6//!
7//! Since the USB descriptor can be quite large, it may be required to activate the feature
8//! `control-buffer-256` of the `usb-device` crate.
9//!
10//! Example
11//!
12//! ```ignore
13//! let mut usb_bus = ... // create a UsbBusAllocator in a platform specific way
14//!
15//! let mut usb_audio = AudioClassBuilder::new()
16//!     .input(
17//!         StreamConfig::new_discrete(
18//!             Format::S16le,
19//!             1,
20//!             &[48000],
21//!             TerminalType::InMicrophone).unwrap())
22//!     .output(
23//!         StreamConfig::new_discrete(
24//!             Format::S24le,
25//!             2,
26//!             &[44100, 48000, 96000],
27//!             TerminalType::OutSpeaker).unwrap())
28//!     .build(&usb_bus)
29//!     .unwrap();
30//! ```
31//!
32//! This example creates an audio device having a one channel (Mono) microphone
33//! with a fixed sampling frequency of 48 KHz and a two channel (Stereo) speaker
34//! output that supports three different sampling rates.
35#![no_std]
36
37use class_codes::*;
38use core::convert::From;
39use usb_device::control::{Recipient, Request, RequestType};
40use usb_device::device::DEFAULT_ALTERNATE_SETTING;
41use usb_device::endpoint::{Endpoint, EndpointDirection, In, Out};
42use usb_device::{class_prelude::*, UsbDirection};
43
44mod terminal_type;
45pub use terminal_type::TerminalType;
46mod class_codes;
47
48const ID_INPUT_TERMINAL: u8 = 0x01;
49const ID_OUTPUT_TERMINAL: u8 = 0x02;
50
51const MAX_ISO_EP_SIZE: u32 = 1023;
52
53#[derive(Clone, Copy, Debug)]
54pub enum Format {
55    /// Signed, 16 bits per subframe, little endian
56    S16le,
57    /// Signed, 24 bits per subframe, little endian
58    S24le,
59}
60
61/// Sampling rates that shall be supported by an steaming endpoint
62#[derive(Debug)]
63pub enum Rates<'a> {
64    /// A continuous range of sampling rates in samples/second defined by a
65    /// tuple including a minimum value and a maximum value. The maximum value
66    /// must be greater than the minimum value.
67    Continuous(u32, u32),
68    /// A set of discrete sampling rates in samples/second
69    Discrete(&'a [u32]),
70}
71
72#[derive(Debug)]
73pub struct StreamConfig<'a> {
74    format: Format,
75    channels: u8,
76    rates: Rates<'a>,
77    terminal_type: TerminalType,
78    /// ISO endpoint size calculated from format, channels and rates (may be
79    /// removed in future)
80    ep_size: u16,
81}
82
83impl StreamConfig<'_> {
84    /// Create a stream configuration with one or more discrete sampling rates
85    /// indicated in samples/second. An input stream or an output stream will
86    /// have an Input Terminal or Output Terminal of Terminal Type
87    /// `terminal_type`, respectively.
88    pub fn new_discrete(
89        format: Format,
90        channels: u8,
91        rates: &'_ [u32],
92        terminal_type: TerminalType,
93    ) -> Result<StreamConfig<'_>> {
94        let max_rate = rates.iter().max().unwrap();
95        let ep_size = Self::ep_size(format, channels, *max_rate)?;
96        let rates = Rates::Discrete(rates);
97        Ok(StreamConfig {
98            format,
99            channels,
100            rates,
101            terminal_type,
102            ep_size,
103        })
104    }
105
106    /// Create a stream configuration with a continuous range of supported
107    /// sampling rates indicated in samples/second. An input stream or an output
108    /// stream will have an Input Terminal or Output Terminal of Terminal Type
109    /// `terminal_type`, respectively.
110    pub fn new_continuous(
111        format: Format,
112        channels: u8,
113        min_rate: u32,
114        max_rate: u32,
115        terminal_type: TerminalType,
116    ) -> Result<StreamConfig<'static>> {
117        if min_rate >= max_rate {
118            return Err(Error::InvalidValue);
119        }
120        let ep_size = Self::ep_size(format, channels, max_rate)?;
121        let rates = Rates::Continuous(min_rate, max_rate);
122        Ok(StreamConfig {
123            format,
124            channels,
125            rates,
126            terminal_type,
127            ep_size,
128        })
129    }
130
131    /// calculate ISO endpoint size from format, channels and rates
132    fn ep_size(format: Format, channels: u8, max_rate: u32) -> Result<u16> {
133        let octets_per_frame = channels as u32
134            * match format {
135                Format::S16le => 2,
136                Format::S24le => 3,
137            };
138        let ep_size = octets_per_frame * max_rate / 1000;
139        if ep_size > MAX_ISO_EP_SIZE {
140            return Err(Error::BandwidthExceeded);
141        }
142        Ok(ep_size as u16)
143    }
144}
145
146/// USB audio errors, including possible USB Stack errors
147#[derive(Debug)]
148pub enum Error {
149    InvalidValue,
150    BandwidthExceeded,
151    StreamNotInitialized,
152    UsbError(usb_device::UsbError),
153}
154
155impl From<UsbError> for Error {
156    fn from(err: UsbError) -> Self {
157        Error::UsbError(err)
158    }
159}
160
161/// Result type alias for the USB Audio Class
162type Result<T> = core::result::Result<T, Error>;
163
164/// Internal state related to audio streaming in a certain direction
165struct AudioStream<'a, B: UsbBus, D: EndpointDirection> {
166    stream_config: StreamConfig<'a>,
167    interface: InterfaceNumber,
168    endpoint: Endpoint<'a, B, D>,
169    alt_setting: u8,
170}
171
172macro_rules! append {
173    ($iter:ident, $value:expr) => {
174        *($iter.next().ok_or(UsbError::BufferOverflow)?.1) = $value;
175    };
176}
177
178macro_rules! append_u24le {
179    ($iter:ident, $value:expr) => {
180        append!($iter, $value as u8);
181        append!($iter, ($value >> 8) as u8);
182        append!($iter, ($value >> 16) as u8);
183    };
184}
185
186impl<B: UsbBus, D: EndpointDirection> AudioStream<'_, B, D> {
187    fn write_ac_descriptors(&self, writer: &mut DescriptorWriter) -> usb_device::Result<()> {
188        let is_input = self.endpoint.address().direction() == UsbDirection::In;
189        let terminal_type: u16 = self.stream_config.terminal_type.into();
190        let id_offset = if is_input { 0 } else { 4 };
191
192        // write Input Terminal Descriptor (12 bytes)
193        let tt = if is_input {
194            terminal_type
195        } else {
196            TerminalType::UsbStreaming.into()
197        }
198        .to_le_bytes();
199
200        // Calculate wChannelConfig based on channel count
201        let channel_config = match self.stream_config.channels {
202            1 => 0x0001u16, // L
203            2 => 0x0003u16, // L+R
204            4 => 0x0033u16, // L+R+LS+RS
205            6 => 0x003Fu16, // L+R+C+LFE+LS+RS
206            8 => 0x00FFu16, // L+R+C+LFE+LS+RS+LC+RC
207            _ => 0x0003u16, // Default to stereo for unsupported counts
208        };
209
210        writer.write(
211            CS_INTERFACE,
212            &[
213                INPUT_TERMINAL,                // bDescriptorSubtype
214                ID_INPUT_TERMINAL + id_offset, // bTerminalID
215                tt[0],                         // wTerminalType
216                tt[1],
217                0x00,                        // bAssocTerminal
218                self.stream_config.channels, // bNrChannels
219                (channel_config & 0xFF) as u8,
220                (channel_config >> 8) as u8, // wChannelConfig
221                0x00, // iChannelNames
222                0x00, // iTerminal
223            ],
224        )?;
225
226        // write Output Terminal Descriptor (9 bytes)
227        let tt = if is_input {
228            TerminalType::UsbStreaming.into()
229        } else {
230            terminal_type
231        }
232        .to_le_bytes();
233        writer.write(
234            CS_INTERFACE,
235            &[
236                OUTPUT_TERMINAL,                // bDescriptorSubtype
237                ID_OUTPUT_TERMINAL + id_offset, // bTerminalID
238                tt[0],                          // wTerminalType
239                tt[1],
240                0x00,                          // bAssocTerminal
241                ID_INPUT_TERMINAL + id_offset, // bSourceID
242                0x00,                          // iTerminal
243            ],
244        )
245    }
246
247    fn write_as_and_ep_descriptors(&self, writer: &mut DescriptorWriter) -> usb_device::Result<()> {
248        let is_input = self.endpoint.address().direction() == UsbDirection::In;
249        let id_offset = if is_input { 0 } else { 4 };
250        // Standard AS Interface Descriptor (Alt. Set. 0)
251        writer.interface(self.interface, AUDIO, AUDIOSTREAMING, 0x00)?;
252
253        // Standard AS Interface Descriptor (Alt. Set. 1)
254        writer.interface_alt(self.interface, 0x01, AUDIO, AUDIOSTREAMING, 0x00, None)?;
255
256        // Class-specific AS General Interface Descriptor
257        let terminal_link = id_offset
258            + if is_input {
259                ID_OUTPUT_TERMINAL
260            } else {
261                ID_INPUT_TERMINAL
262            };
263        writer.write(
264            CS_INTERFACE,
265            &[
266                AS_GENERAL,    // bDescriptorSubtype:
267                terminal_link, // bTerminalLink
268                0x01,          // bDelay
269                PCM as u8,
270                (PCM >> 8) as u8, // wFormatTag
271            ],
272        )?;
273
274        // Type 1 Format Type Descriptor
275        let mut format_desc = [0x00u8; 128];
276        let mut iter = format_desc.iter_mut().enumerate();
277        append!(iter, FORMAT_TYPE); // bDescriptorSubtype;
278        append!(iter, FORMAT_TYPE_I); // bFormatType
279        append!(iter, self.stream_config.channels); // bNrChannels
280        append!(
281            iter,
282            match self.stream_config.format {
283                // bSubFrameSize
284                Format::S16le => 2,
285                Format::S24le => 3,
286            }
287        );
288        append!(
289            iter,
290            match self.stream_config.format {
291                // bBitResolution
292                Format::S16le => 16,
293                Format::S24le => 24,
294            }
295        );
296        match self.stream_config.rates {
297            Rates::Continuous(min, max) => {
298                append!(iter, 0x00); // bSamFreqType
299                append_u24le!(iter, min);
300                append_u24le!(iter, max);
301            }
302            Rates::Discrete(rates) => {
303                append!(iter, rates.len() as u8); // bSamFreqType
304                for rate in rates {
305                    append_u24le!(iter, *rate);
306                }
307            }
308        }
309        let length = iter.next().unwrap().0;
310        writer.write(CS_INTERFACE, &format_desc[..length])?;
311
312        // Standard Endpoint Descriptor
313        writer.endpoint(&self.endpoint)?;
314
315        // Class-specific Isoc. Audio Data Endpoint Descriptor
316        writer.write(
317            0x25,
318            &[
319                // bDescriptorType: CS_ENDPOINT
320                0x01, // bDescriptorSubtype: GENERAL
321                0x00, // bmAttributes
322                0x00, // bLockDelayUnits
323                0x00, 0x00, // wLockDelay
324            ],
325        )
326    }
327}
328
329/// Builder class to create an `AudioClass` structure.
330pub struct AudioClassBuilder<'a> {
331    input: Option<StreamConfig<'a>>,
332    output: Option<StreamConfig<'a>>,
333}
334
335impl<'a> AudioClassBuilder<'a> {
336    /// Create a new AudioClassBuilder
337    pub fn new() -> AudioClassBuilder<'static> {
338        AudioClassBuilder {
339            input: None,
340            output: None,
341        }
342    }
343
344    /// Configure the input audio stream according to a `StreamConfig`.
345    /// At most one input stream can be configured. When calling this method
346    /// multiple times, the last call matters.
347    pub fn input(self, input: StreamConfig<'a>) -> AudioClassBuilder<'a> {
348        AudioClassBuilder {
349            input: Some(input),
350            output: self.output,
351        }
352    }
353
354    /// Configure the output audio stream according to a `StreamConfig`.
355    /// At most one output stream can be configured. When calling this method
356    /// multiple times, the last call matters.
357    pub fn output(self, output: StreamConfig<'a>) -> AudioClassBuilder<'a> {
358        AudioClassBuilder {
359            input: self.input,
360            output: Some(output),
361        }
362    }
363
364    /// Create the `AudioClass` structure
365    pub fn build<B: UsbBus>(self, alloc: &'a UsbBusAllocator<B>) -> Result<AudioClass<'a, B>> {
366        let control_iface = alloc.interface();
367        let mut ac = AudioClass {
368            control_iface,
369            input: None,
370            output: None,
371        };
372        if let Some(stream_config) = self.input {
373            let interface = alloc.interface();
374            let endpoint = alloc.alloc(
375                None,
376                EndpointType::Isochronous {
377                    synchronization: IsochronousSynchronizationType::Asynchronous,
378                    usage: IsochronousUsageType::Data,
379                },
380                stream_config.ep_size,
381                1,
382            )?;
383            let alt_setting = DEFAULT_ALTERNATE_SETTING;
384            ac.input = Some(AudioStream {
385                stream_config,
386                interface,
387                endpoint,
388                alt_setting,
389            })
390        }
391
392        if let Some(stream_config) = self.output {
393            let interface = alloc.interface();
394            let endpoint = alloc.alloc(
395                None,
396                EndpointType::Isochronous {
397                    synchronization: IsochronousSynchronizationType::Adaptive,
398                    usage: IsochronousUsageType::Data,
399                },
400                stream_config.ep_size,
401                1,
402            )?;
403            let alt_setting = DEFAULT_ALTERNATE_SETTING;
404            ac.output = Some(AudioStream {
405                stream_config,
406                interface,
407                endpoint,
408                alt_setting,
409            })
410        }
411
412        Ok(ac)
413    }
414}
415
416/// USB device class for audio devices.
417///
418/// This device class based on the "Universal Serial Bus Device Class Definition
419/// for Audio Devices", Release 1.0. It supports one input stream and/or one
420/// output stream.
421pub struct AudioClass<'a, B: UsbBus> {
422    control_iface: InterfaceNumber,
423    input: Option<AudioStream<'a, B, In>>,
424    output: Option<AudioStream<'a, B, Out>>,
425}
426
427impl<B: UsbBus> AudioClass<'_, B> {
428    /// Read audio frames as output by the host. Returns an Error if no output
429    /// stream has been configured.
430    pub fn read(&self, data: &mut [u8]) -> Result<usize> {
431        if let Some(ref info) = self.output {
432            info.endpoint.read(data).map_err(Error::UsbError)
433        } else {
434            Err(Error::StreamNotInitialized)
435        }
436    }
437
438    /// Write audio frames to be input by the host. Returns an Error when no
439    /// input stream has been configured.
440    pub fn write(&self, data: &[u8]) -> Result<usize> {
441        if let Some(ref info) = self.input {
442            info.endpoint.write(data).map_err(Error::UsbError)
443        } else {
444            Err(Error::StreamNotInitialized)
445        }
446    }
447
448    /// Get current Alternate Setting of the input stream. Returns an error if
449    /// the stream is not configured.
450    pub fn input_alt_setting(&self) -> Result<u8> {
451        self.input
452            .as_ref()
453            .ok_or(Error::StreamNotInitialized)
454            .map(|si| si.alt_setting)
455    }
456
457    /// Get current Alternate Setting of the output stream. Returns an error if
458    /// the stream is not configured.
459    pub fn output_alt_setting(&self) -> Result<u8> {
460        self.output
461            .as_ref()
462            .ok_or(Error::StreamNotInitialized)
463            .map(|si| si.alt_setting)
464    }
465}
466
467impl<B: UsbBus> UsbClass<B> for AudioClass<'_, B> {
468    fn get_configuration_descriptors(
469        &self,
470        writer: &mut DescriptorWriter,
471    ) -> usb_device::Result<()> {
472        let mut in_collection = 0u8;
473        if self.input.is_some() {
474            in_collection += 1;
475        }
476        if self.output.is_some() {
477            in_collection += 1;
478        }
479
480        writer.iad(
481            self.control_iface,
482            in_collection + 1, // Number of interfaces: control + streaming
483            AUDIO,             // bFunctionClass
484            AUDIOCONTROL,
485            0x00, // bFunctionProtocol
486            None, // iFunction
487        )?;
488
489        // write Class-specific Audio Control (AC) Interface Descriptors
490        writer.interface(self.control_iface, AUDIO, AUDIOCONTROL, 0x00)?;
491
492        let total_length = 8u16 + (1 + 21) * in_collection as u16;
493
494        let mut ac_header = [
495            HEADER, // bDescriptorSubtype
496            0x00,
497            0x01, // bcdADC
498            total_length as u8,
499            (total_length >> 8) as u8, // wTotalLength
500            in_collection,             // number of AS interfaces
501            0x00,
502            0x00, // placeholders for baInterfaceNr
503        ];
504        let mut ndx = 6;
505        if let Some(ref input) = self.input {
506            ac_header[ndx] = input.interface.into();
507            ndx += 1;
508        }
509        if let Some(ref output) = self.output {
510            ac_header[ndx] = output.interface.into();
511            ndx += 1;
512        }
513        writer.write(CS_INTERFACE, &ac_header[..ndx])?;
514        if let Some(ref a) = self.input {
515            a.write_ac_descriptors(writer)?;
516        }
517        if let Some(ref a) = self.output {
518            a.write_ac_descriptors(writer)?;
519        }
520
521        // write Audio Streaming (AS) and endpoint (EP) descriptors
522        if let Some(ref a) = self.input {
523            a.write_as_and_ep_descriptors(writer)?;
524        }
525        if let Some(ref a) = self.output {
526            a.write_as_and_ep_descriptors(writer)?;
527        }
528        Ok(())
529    }
530
531    fn control_in(&mut self, xfer: ControlIn<B>) {
532        let req = xfer.request();
533        if req.request_type == RequestType::Standard
534            && req.recipient == Recipient::Interface
535            && req.request == Request::GET_INTERFACE
536            && req.length == 1
537        {
538            let iface = req.index as u8;
539            if let Some(info) = self.input.as_ref() {
540                if iface == info.interface.into() {
541                    xfer.accept_with(&[info.alt_setting]).ok();
542                    return;
543                }
544            }
545            if let Some(info) = self.output.as_ref() {
546                if iface == info.interface.into() {
547                    xfer.accept_with(&[info.alt_setting]).ok();
548                }
549            }
550        }
551    }
552
553    fn control_out(&mut self, xfer: ControlOut<B>) {
554        let req = xfer.request();
555        if req.request_type == RequestType::Standard
556            && req.recipient == Recipient::Interface
557            && req.request == Request::SET_INTERFACE
558        {
559            let iface = req.index as u8;
560            let alt_setting = req.value;
561
562            if let Some(info) = self.input.as_mut() {
563                if iface == info.interface.into() {
564                    info.alt_setting = alt_setting as u8;
565                    xfer.accept().ok();
566                    return;
567                }
568            }
569            if let Some(info) = self.output.as_mut() {
570                if iface == info.interface.into() {
571                    info.alt_setting = alt_setting as u8;
572                    xfer.accept().ok();
573                }
574            }
575        }
576    }
577}