//! USB audio class for asynchronous, bidirectional (microphone + speakers) 48kHz audio. //! //! See "Universal Serial Bus Device Class Definition for Audio Devices", release 1.0 (March 18, //! 1998) for a specification of the USB audio class. use usb_device::class_prelude::*; use usb_device::endpoint::{IsochronousSynchronizationType, IsochronousUsageType}; use usb_device::Result; const DEVICE_CLASS_AUDIO: u8 = 0x01; const AUDIO_SUBCLASS_CONTROL: u8 = 0x01; const AUDIO_SUBCLASS_STREAMING: u8 = 0x02; const AUDIO_PROTOCOL_NONE: u8 = 0x00; const AUDIO_INTERFACE_DESC_TYPE: u8 = 0x24; const AUDIO_ENDPOINT_DESC_TYPE: u8 = 0x25; const AUDIO_CONTROL_HEADER: u8 = 0x01; const AUDIO_CONTROL_INPUT_TERMINAL: u8 = 0x02; const AUDIO_CONTROL_OUTPUT_TERMINAL: u8 = 0x03; const AUDIO_CONTROL_FEATURE_UNIT: u8 = 0x06; const AUDIO_STREAMING_GENERAL: u8 = 0x01; const AUDIO_STREAMING_FORMAT_TYPE: u8 = 0x02; const AUDIO_ENDPOINT_GENERAL: u8 = 0x01; // TODO: Update bDelay. pub struct UsbAudioClass<'a, B: UsbBus, OUTBUF: AsRef<[u32]>> { audio_control: InterfaceNumber, audio_streaming_inactive: InterfaceNumber, audio_streaming: InterfaceNumber, audio_out: EndpointOut<'a, B>, audio_out_sync: EndpointIn<'a, B>, //audio_in: EndpointIn<'a, B>, samples_per_frame: [u8; 3], audio_out_buf: Option, } impl<'a, B: UsbBus, OUTBUF: AsRef<[u32]>> UsbAudioClass<'a, B, OUTBUF> { pub fn new(bus_alloc: &'a UsbBusAllocator) -> Self { UsbAudioClass { audio_control: bus_alloc.interface(), audio_streaming_inactive: bus_alloc.interface(), audio_streaming: bus_alloc.interface(), // 48kHz * 2 * 16bit = 192B per packet. We allocate a bit more in case the device clock // is faster than the host clock. /*audio_out: bus_alloc.isochronous( IsochronousSynchronizationType::Asynchronous, IsochronousUsageType::Data, 256, 1, ),*/ audio_out: bus_alloc.isochronous( IsochronousSynchronizationType::Synchronous, IsochronousUsageType::Data, 256, 1, ), audio_out_sync: bus_alloc.isochronous( IsochronousSynchronizationType::Asynchronous, IsochronousUsageType::Feedback, 3, 1, ), /*audio_in: bus_alloc.isochronous( IsochronousSynchronizationType::Asynchronous, IsochronousUsageType::Data, 256, 1, ),*/ samples_per_frame: [48 >> 2, 48 << 6, 0], // 48 kHz audio_out_buf: None, } } // TODO: Functions to read and write data? } impl> UsbClass for UsbAudioClass<'_, B, OUTBUF> { fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { writer.iad( self.audio_control, 2, // Two interfaces (control + streaming). 0x0, // Each interface specifies its own class. 0x0, // Each interface specifies its own subclass. 0x0, // No class-specific protocols for this device. )?; // Audio control interface. writer.interface( self.audio_control, DEVICE_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_PROTOCOL_NONE, )?; writer.write( AUDIO_INTERFACE_DESC_TYPE, &[ AUDIO_CONTROL_HEADER, // bDescriptorSubtype 0x00, // bcdADC 0x01, // 37, // wTotalLength 0, // 0x01, // bInCollection self.audio_streaming.into(), // baInterfaceNr ], )?; // Input terminal (USB streaming). writer.write( AUDIO_INTERFACE_DESC_TYPE, &[ AUDIO_CONTROL_INPUT_TERMINAL, // bDescriptorSubtype 0x01, // bTerminalID 0x01, // wTerminalType 0x01, // 0x00, // bAssocTerminal 0x02, // bNrChannels 0x03, // wChannelConfig 0x00, // 0x00, // iChannelNames 0x00, // iTerminal ], )?; // Feature unit (volume and mute). writer.write( AUDIO_INTERFACE_DESC_TYPE, &[ AUDIO_CONTROL_FEATURE_UNIT, // bDescriptorSubtype 0x02, // bUnitID 0x01, // bSourceID 0x01, // bControlSize 0x03, // bmaControls(0) 0x00, // iFeature ], )?; // Output terminal (speaker). writer.write( AUDIO_INTERFACE_DESC_TYPE, &[ AUDIO_CONTROL_OUTPUT_TERMINAL, // bDescriptorSubtype 0x03, // bTerminalID 0x01, // wTerminalType 0x03, // 0x00, // bAssocTerminal 0x02, // bSourceID 0x00, // iTerminal ], )?; // Audio streaming interface (zero-bandwidth). writer.interface( self.audio_streaming_inactive, DEVICE_CLASS_AUDIO, AUDIO_SUBCLASS_STREAMING, AUDIO_PROTOCOL_NONE, )?; // Audio streaming interface (operational). writer.interface_alt( self.audio_streaming, 1, DEVICE_CLASS_AUDIO, AUDIO_SUBCLASS_STREAMING, AUDIO_PROTOCOL_NONE, None, )?; writer.write( AUDIO_INTERFACE_DESC_TYPE, &[ AUDIO_STREAMING_GENERAL, // bDescriptorSubtype 0x01, // bTerminalLink 0x03, // bDelay 0x01, // wFormatTag 0x00, // ], )?; writer.write( AUDIO_INTERFACE_DESC_TYPE, &[ AUDIO_STREAMING_FORMAT_TYPE, // bDescriptorSubtype 0x01, // bFormatType 0x02, // bNrChannels 0x02, // bSubframeSize 0x10, // bBitResolution 0x01, // bSamFreqType ], )?; /*writer.endpoint_ex(&self.audio_out, |data| { // TODO: Faster refresh data[0] = 0x09; // bRefresh data[1] = self.audio_out_sync.address().into(); // bSynchAddress Ok(2) })?;*/ writer.endpoint(&self.audio_out)?; writer.write( AUDIO_ENDPOINT_DESC_TYPE, &[ AUDIO_ENDPOINT_GENERAL, // bDescriptorSubtype 0x00, // bmAttributes 0x01, // bLockDelayUnits 0x00, // wLockDelay 0x00, ], )?; /*writer.endpoint_ex(&self.audio_out_sync, |data| { data[0] = 0x00; // bRefresh data[1] = 0x00; // bSynchAddress Ok(2) })?;*/ Ok(()) } fn get_string(&self, _index: StringIndex, _lang_id: u16) -> Option<&str> { // TODO None } fn reset(&mut self) { // Start sending synchronization data. self.endpoint_in_complete(self.audio_out_sync.address()); } fn control_out(&mut self, xfer: ControlOut) { let req = xfer.request(); if !(req.request_type == control::RequestType::Class && req.recipient == control::Recipient::Interface && req.index == u8::from(self.audio_control) as u16) { return; } match req.request { // TODO _ => { xfer.reject().ok(); } }; } fn control_in(&mut self, xfer: ControlIn) { let req = xfer.request(); if !(req.request_type == control::RequestType::Class && req.recipient == control::Recipient::Interface && req.index == u8::from(self.audio_control) as u16) { return; } match req.request { // TODO _ => { xfer.reject().ok(); } }; } fn endpoint_out(&mut self, addr: EndpointAddress) { if addr == self.audio_out.address() { if self.audio_out_buf.is_some() { // TODO: Write data into buffer, move buffer somewhere else. } else { // TODO } // TODO: Process incoming audio data. let mut buffer = [0u8; 256]; self.audio_out.read(&mut buffer); } } fn endpoint_in_complete(&mut self, addr: EndpointAddress) { if addr == self.audio_out_sync.address() { // Immediately write the next sync value. //self.audio_out_sync.write(&self.samples_per_frame).unwrap(); } } } /*use usb_device::class_prelude::*; use usb_device::endpoint::{IsochronousSynchronizationType, IsochronousUsageType}; use usb_device::Result; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_INTERFACE_CLASS_AUDIO: u8 = 0x01; const USB_AUDIO_SUBCLASS_UNDEFINED: u8 = 0x0; const USB_AUDIO_SUBCLASS_AUDIOCONTROL: u8 = 0x01; const USB_AUDIO_SUBCLASS_AUDIOSTREAMING: u8 = 0x02; const USB_AUDIO_SUBCLASS_MIDISTREAMING: u8 = 0x03; const USB_CLASS_CDC_DATA: u8 = 0x0a; const CDC_SUBCLASS_ACM: u8 = 0x02; const USB_AUDIO_PROTOCOL_NONE: u8 = 0x00; const CS_DEVICE: u8 = 0x21; const CS_CONFIGURATION: u8 = 0x22; const CS_STRING: u8 = 0x23; const CS_INTERFACE: u8 = 0x24; const CS_ENDPOINT: u8 = 0x25; const EP_GENERAL: u8 = 0x1; const AC_DESC_TYPE_HEADER: u8 = 0x1; const AC_DESC_TYPE_INPUT_TERMINAL: u8 = 0x2; const AC_DESC_TYPE_OUTPUT_TERMINAL: u8 = 0x3; const AC_DESC_TYPE_MIXER_UNIT: u8 = 0x4; const AC_DESC_TYPE_SELECTOR_UNIT: u8 = 0x5; const AC_DESC_TYPE_FEATURE_UNIT: u8 = 0x6; const AC_DESC_TYPE_PROCESSING_UNIT: u8 = 0x7; const AC_DESC_TYPE_EXTENSION_UNIT: u8 = 0x8; const AC_DESC_ST_GENERAL: u8 = 0x1; const AC_DESC_ST_FORMAT_TYPE: u8 = 0x2; const AC_DESC_ST_FORMAT_SPECIFIC: u8 = 0x3; const AUDIO_SUB_TYPE_HEADER: u8 = 0x01; const AUDIO_SUB_TYPE_INPUT_TERMINAL: u8 = 0x02; const AUDIO_SUB_TYPE_FEATURE_UNIT: u8 = 0x06; const AUDIO_SUB_TYPE_OUTPUT_TERMINAL: u8 = 0x03; const AUDIO_SUB_TYPE_AS_GENERAL: u8 = 0x01; const AUDIO_SUB_TYPE_FORMAT_TYPE: u8 = 0x02; pub struct UsbAudioClass<'a, B: UsbBus> { audio_control_if: InterfaceNumber, audio_stream: InterfaceNumber, alt_audio_stream: InterfaceNumber, audio_in_ep: EndpointIn<'a, B>, } impl UsbAudioClass<'_, B> { /// Creates a new UsbAudioClass with the provided UsbBus and max_packet_size in bytes. For /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. pub fn new(alloc: &UsbBusAllocator, max_packet_size: u16) -> UsbAudioClass<'_, B> { UsbAudioClass { audio_control_if: alloc.interface(), audio_stream: alloc.interface(), alt_audio_stream: alloc.interface(), audio_in_ep: alloc.isochronous( IsochronousSynchronizationType::Synchronous, IsochronousUsageType::Data, max_packet_size, 4, ), } } /// Gets the maximum packet size in bytes. pub fn max_packet_size(&self) -> u16 { self.audio_in_ep.max_packet_size() } /// Writes a single packet into the IN endpoint. pub fn write_packet(&mut self, data: &[u8]) -> Result { //defmt::info!("write_packet"); self.audio_in_ep.write(data) } /// Gets the address of the IN endpoint. pub(crate) fn write_ep_address(&self) -> EndpointAddress { self.audio_in_ep.address() } } impl UsbClass for UsbAudioClass<'_, B> { fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { writer.iad( self.audio_control_if, 3, 0x0, // device defined at interface level 0x0, // subclass unused USB_AUDIO_PROTOCOL_NONE, )?; writer.interface( self.audio_control_if, USB_INTERFACE_CLASS_AUDIO, USB_AUDIO_SUBCLASS_AUDIOCONTROL, USB_AUDIO_PROTOCOL_NONE, )?; writer.write( CS_INTERFACE, &[ AUDIO_SUB_TYPE_HEADER, // bDescriptorSubtype 0x00, 0x1, // bcdADC (1.0), 43, 0, // wTotalLength (Compute this!) 0x01, // bInCollection (1 streaming interface) 0x01, // baInterfaceNr (Interface 1 is stream) ], )?; writer.write( CS_INTERFACE, &[ AUDIO_SUB_TYPE_INPUT_TERMINAL, // bDescriptorSubtype 0x01, // bTerminalID 1, 0x10, 0x07, // wTerminalType (radio receiver) 0x00, // bAssocTerminal (none) 0x02, // bNrChannels - 2 for I and Q 0x03, 0x00, // wChannelConfig (left, right) 0x00, // iChannelNames (none) 0x00, // iTerminal (none) ], )?; writer.write( CS_INTERFACE, &[ AUDIO_SUB_TYPE_FEATURE_UNIT, // bDescriptorSubtype 0x02, // bUnitID, 0x01, // bSourceID (input terminal 1) 0x02, // bControlSize (2 bytes) 0x01, 0x00, // Master controls 0x00, 0x00, // Channel 0 controls 0x00, 0x00, // Channel 1 controls 0x00, // iFeature (none) ], )?; writer.write( CS_INTERFACE, &[ AUDIO_SUB_TYPE_OUTPUT_TERMINAL, // bDescriptorSubtype 0x03, // bTerminalID, 0x01, 0x01, // wTerminalType (USB Streaming) 0x00, // bAssocTerminal (none) 0x02, // bSourceID (feature unit 2) 0x00, // iTerminal (none) ], )?; writer.interface( self.audio_stream, USB_INTERFACE_CLASS_AUDIO, USB_AUDIO_SUBCLASS_AUDIOSTREAMING, USB_AUDIO_PROTOCOL_NONE, )?; //alternate audio stream /*writer.interface_alt( self.alt_audio_stream, 1, USB_INTERFACE_CLASS_AUDIO, USB_AUDIO_SUBCLASS_AUDIOSTREAMING, USB_AUDIO_PROTOCOL_NONE, None, )?;*/ // Audio Stream Audio Class Descriptor writer.write( CS_INTERFACE, &[ AUDIO_SUB_TYPE_AS_GENERAL, // bDescriptorSubtype 0x03, // bTerminalID, 0x00, // bDelay 0x01, 0x00, // wFormatTag (PCM Format) ], )?; // Format Type Audio Descriptor writer.write( CS_INTERFACE, &[ AUDIO_SUB_TYPE_FORMAT_TYPE, // bDescriptorSubtype 0x01, // bFormatType (TYPE_I) 0x02, // bNrChannels (2) 0x02, // bSubFrameSize (2) 0x10, // bBitResolution (16 bits) 0x01, // bSamFreqType (1 sample frequency) 0x80, // 8*2 = 16 KHz byte 0 0x3E, // 8*2 = 16 KHz byte 1 0x00, // 8*2 = 16 KHz byte 2 ], )?; // TODO: Set the necessary flags for Isochronous writer.endpoint(&self.audio_in_ep)?; // Isochronous endpoint Audio Class descriptor writer.write( CS_ENDPOINT, &[ EP_GENERAL, // bDescriptorSubtype 0x00, // bmAttributes (none) 0x02, // bLockDelayUnits (PCM Samples) 0x00, 0x00, // wLockDelay (0) - should be zero for asynchronous ], )?; Ok(()) } fn reset(&mut self) {} fn control_in(&mut self, xfer: ControlIn) { let req = xfer.request(); if !(req.request_type == control::RequestType::Class && req.recipient == control::Recipient::Interface && req.index == u8::from(self.audio_control_if) as u16) { return; } //defmt::info!("control_in - req : {:?}", req.request); match req.request { /* REQ_GET_LINE_CODING if req.length == 7 => { xfer.accept(|data| { data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); data[4] = self.line_coding.stop_bits as u8; data[5] = self.line_coding.parity_type as u8; data[6] = self.line_coding.data_bits; Ok(7) }).ok(); }, */ _ => { xfer.reject().ok(); } } } fn control_out(&mut self, xfer: ControlOut) { let req = xfer.request(); if !(req.request_type == control::RequestType::Class && req.recipient == control::Recipient::Interface && req.index == u8::from(self.audio_control_if) as u16) { return; } match req.request { /* REQ_SEND_ENCAPSULATED_COMMAND => { // We don't actually support encapsulated commands but pretend we do for standards // compatibility. xfer.accept().ok(); },*/ _ => { xfer.reject().ok(); } }; } }*/