refactoring and allow &mut calls of AudioClass
This commit is contained in:
+87
-50
@@ -6,18 +6,16 @@ mod cursor;
|
|||||||
pub mod descriptors;
|
pub mod descriptors;
|
||||||
mod log;
|
mod log;
|
||||||
|
|
||||||
use core::cell::OnceCell;
|
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::AtomicUsize;
|
use core::sync::atomic::AtomicUsize;
|
||||||
|
|
||||||
use byteorder_embedded_io::{LittleEndian, WriteBytesExt};
|
use byteorder_embedded_io::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use constants::*;
|
use constants::*;
|
||||||
use descriptors::*;
|
use descriptors::*;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
use modular_bitfield::prelude::*;
|
use num_traits::ConstZero;
|
||||||
use num_traits::{ConstZero, Zero};
|
|
||||||
use usb_device::control::{Recipient, Request, RequestType};
|
use usb_device::control::{Recipient, Request, RequestType};
|
||||||
use usb_device::device::DEFAULT_ALTERNATE_SETTING;
|
use usb_device::device::DEFAULT_ALTERNATE_SETTING;
|
||||||
use usb_device::endpoint::{self, Endpoint, EndpointDirection, In, Out};
|
use usb_device::endpoint::{self, Endpoint, EndpointDirection, In, Out};
|
||||||
@@ -163,7 +161,7 @@ impl UsbIsochronousFeedback {
|
|||||||
pub trait UsbAudioClass<'a, B: UsbBus> {
|
pub trait UsbAudioClass<'a, B: UsbBus> {
|
||||||
/// Called when audio data is received from the host. `ep` is ready for
|
/// Called when audio data is received from the host. `ep` is ready for
|
||||||
/// `ep.read()`.
|
/// `ep.read()`.
|
||||||
fn audio_data_rx(&self, ep: &Endpoint<'a, B, endpoint::Out>) {}
|
fn audio_data_rx(&mut self, ep: &Endpoint<'a, B, endpoint::Out>) {}
|
||||||
|
|
||||||
/// Called when it's time to send an isochronous feedback update. Should
|
/// Called when it's time to send an isochronous feedback update. Should
|
||||||
/// return the correct feedback payload. Should not be considered a great
|
/// return the correct feedback payload. Should not be considered a great
|
||||||
@@ -172,20 +170,14 @@ pub trait UsbAudioClass<'a, B: UsbBus> {
|
|||||||
///
|
///
|
||||||
/// Required for isochronous asynchronous mode to work properly. If None is
|
/// Required for isochronous asynchronous mode to work properly. If None is
|
||||||
/// returned, no IN packet will be emitted at feedback time.
|
/// returned, no IN packet will be emitted at feedback time.
|
||||||
fn feedback(&self) -> Option<UsbIsochronousFeedback> {
|
fn feedback(&mut self) -> Option<UsbIsochronousFeedback> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when the alternate setting of `terminal`'s interface is changed,
|
/// Called when the alternate setting of `terminal`'s interface is changed,
|
||||||
/// before the `AudioStream` is updated. Currently not very useful since we
|
/// before the `AudioStream` is updated. Currently not very useful since we
|
||||||
/// don't implement alternate settings.
|
/// don't implement alternate settings.
|
||||||
fn alternate_setting_changed<CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>(
|
fn alternate_setting_changed(&mut self, terminal: UsbDirection, alt_setting: u8) {}
|
||||||
&self,
|
|
||||||
ac: &mut AudioClass<'a, B, CS, AU>,
|
|
||||||
terminal: UsbDirection,
|
|
||||||
alt_setting: u8,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for implementing Sampling Frequency Control for USB Audio Clock Sources
|
/// A trait for implementing Sampling Frequency Control for USB Audio Clock Sources
|
||||||
@@ -201,8 +193,8 @@ pub trait UsbAudioClockImpl {
|
|||||||
const SOF_SYNC: bool;
|
const SOF_SYNC: bool;
|
||||||
/// Called when the host requests the current sample rate. Returns the sample rate in Hz.
|
/// Called when the host requests the current sample rate. Returns the sample rate in Hz.
|
||||||
fn get_sample_rate(&self) -> core::result::Result<u32, UsbAudioClassError>;
|
fn get_sample_rate(&self) -> core::result::Result<u32, UsbAudioClassError>;
|
||||||
/// Called when the host requests to set the sample rate. Should reconfigure the clock source
|
/// Called when the host requests to set the sample rate. Not necessarily called at all startups,
|
||||||
/// if necessary.
|
/// so alt_setting should start/stop the clock. Not required for 'fixed' clocks.
|
||||||
fn set_sample_rate(
|
fn set_sample_rate(
|
||||||
&mut self,
|
&mut self,
|
||||||
sample_rate: u32,
|
sample_rate: u32,
|
||||||
@@ -238,6 +230,13 @@ pub trait UsbAudioClockImpl {
|
|||||||
/// ref: USB Audio Class Specification 2.0 5.2.1 & 5.2.3.3
|
/// ref: USB Audio Class Specification 2.0 5.2.1 & 5.2.3.3
|
||||||
fn get_rates(&self) -> core::result::Result<&[RangeEntry<u32>], UsbAudioClassError>;
|
fn get_rates(&self) -> core::result::Result<&[RangeEntry<u32>], UsbAudioClassError>;
|
||||||
|
|
||||||
|
/// Called when the audio device's AltSetting is changed. Usually 0 signals shutdown of the
|
||||||
|
/// streaming audio and 1 signals start of streaming. This should be used to start the clock
|
||||||
|
/// (and stop it if desired). If unimplemented, does nothing - keep the clock running at all times.
|
||||||
|
fn alt_setting(&mut self, alt_setting: u8) -> core::result::Result<(), UsbAudioClassError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Build the ClockSource descriptor. It is not intended to override this method.
|
/// Build the ClockSource descriptor. It is not intended to override this method.
|
||||||
///
|
///
|
||||||
/// Assumes access control based on clock type. Internal fixed/variable are read only,
|
/// Assumes access control based on clock type. Internal fixed/variable are read only,
|
||||||
@@ -320,7 +319,7 @@ impl<D: EndpointDirection> TerminalConfig<D> {
|
|||||||
self.format.bytes_per_sample as u32 * self.num_channels as u32
|
self.format.bytes_per_sample as u32 * self.num_channels as u32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<In> {
|
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<Out> {
|
||||||
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal) {
|
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal) {
|
||||||
let input_terminal = InputTerminal {
|
let input_terminal = InputTerminal {
|
||||||
id: self.base_id,
|
id: self.base_id,
|
||||||
@@ -357,7 +356,7 @@ impl<'a> TerminalConfigurationDescriptors for TerminalConfig<In> {
|
|||||||
// fn get_interface_descriptor(&self, id: InterfaceIndex) )
|
// fn get_interface_descriptor(&self, id: InterfaceIndex) )
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<Out> {
|
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<In> {
|
||||||
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal) {
|
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal) {
|
||||||
let output_terminal = OutputTerminal {
|
let output_terminal = OutputTerminal {
|
||||||
id: self.base_id,
|
id: self.base_id,
|
||||||
@@ -422,10 +421,10 @@ pub enum UsbSpeed {
|
|||||||
pub struct AudioClassConfig<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> {
|
pub struct AudioClassConfig<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> {
|
||||||
pub speed: UsbSpeed,
|
pub speed: UsbSpeed,
|
||||||
pub device_category: FunctionCode,
|
pub device_category: FunctionCode,
|
||||||
pub clock_impl: &'a CS,
|
pub clock_impl: &'a mut CS,
|
||||||
pub audio_impl: &'a AU,
|
pub audio_impl: &'a mut AU,
|
||||||
pub input_config: Option<TerminalConfig<Out>>,
|
pub input_config: Option<TerminalConfig<In>>,
|
||||||
pub output_config: Option<TerminalConfig<In>>,
|
pub output_config: Option<TerminalConfig<Out>>,
|
||||||
pub additional_descriptors: Option<&'a [AudioClassDescriptor]>,
|
pub additional_descriptors: Option<&'a [AudioClassDescriptor]>,
|
||||||
_bus: PhantomData<B>,
|
_bus: PhantomData<B>,
|
||||||
}
|
}
|
||||||
@@ -436,8 +435,8 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
speed: UsbSpeed,
|
speed: UsbSpeed,
|
||||||
device_category: FunctionCode,
|
device_category: FunctionCode,
|
||||||
clock_impl: &'a CS,
|
clock_impl: &'a mut CS,
|
||||||
audio_impl: &'a AU,
|
audio_impl: &'a mut AU,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
speed,
|
speed,
|
||||||
@@ -450,11 +449,11 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
_bus: PhantomData,
|
_bus: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn with_input_config(mut self, input_config: TerminalConfig<Out>) -> Self {
|
pub fn with_input_config(mut self, input_config: TerminalConfig<In>) -> Self {
|
||||||
self.input_config = Some(input_config);
|
self.input_config = Some(input_config);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn with_output_config(mut self, output_config: TerminalConfig<In>) -> Self {
|
pub fn with_output_config(mut self, output_config: TerminalConfig<Out>) -> Self {
|
||||||
self.output_config = Some(output_config);
|
self.output_config = Some(output_config);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -516,10 +515,10 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
interval,
|
interval,
|
||||||
);
|
);
|
||||||
let alt_setting = DEFAULT_ALTERNATE_SETTING;
|
let alt_setting = DEFAULT_ALTERNATE_SETTING;
|
||||||
ac.in_iface = interface.into();
|
ac.out_iface = interface.into();
|
||||||
ac.in_ep = endpoint.address().index();
|
ac.out_ep = endpoint.address().index();
|
||||||
ac.fb_ep = feedback_ep.address().index();
|
ac.fb_ep = feedback_ep.address().index();
|
||||||
ac.input = Some(AudioStream {
|
ac.output = Some(AudioStream {
|
||||||
config,
|
config,
|
||||||
interface,
|
interface,
|
||||||
endpoint,
|
endpoint,
|
||||||
@@ -537,9 +536,10 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
interval,
|
interval,
|
||||||
);
|
);
|
||||||
let alt_setting = DEFAULT_ALTERNATE_SETTING;
|
let alt_setting = DEFAULT_ALTERNATE_SETTING;
|
||||||
ac.out_iface = interface.into();
|
ac.in_iface = interface.into();
|
||||||
ac.out_ep = endpoint.address().index();
|
ac.in_ep = endpoint.address().index();
|
||||||
ac.output = Some(AudioStream {
|
|
||||||
|
ac.input = Some(AudioStream {
|
||||||
config,
|
config,
|
||||||
interface,
|
interface,
|
||||||
endpoint,
|
endpoint,
|
||||||
@@ -651,8 +651,8 @@ impl<'a, B: UsbBus, D: EndpointDirection> AudioStream<'a, B, D> {
|
|||||||
|
|
||||||
pub struct AudioClass<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> {
|
pub struct AudioClass<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> {
|
||||||
control_iface: InterfaceNumber,
|
control_iface: InterfaceNumber,
|
||||||
clock_impl: &'a CS,
|
clock_impl: &'a mut CS,
|
||||||
audio_impl: &'a AU,
|
audio_impl: &'a mut AU,
|
||||||
output: Option<AudioStream<'a, B, Out>>,
|
output: Option<AudioStream<'a, B, Out>>,
|
||||||
input: Option<AudioStream<'a, B, In>>,
|
input: Option<AudioStream<'a, B, In>>,
|
||||||
feedback: Option<Endpoint<'a, B, In>>,
|
feedback: Option<Endpoint<'a, B, In>>,
|
||||||
@@ -675,7 +675,7 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass<B>
|
|||||||
) -> usb_device::Result<()> {
|
) -> usb_device::Result<()> {
|
||||||
info!(" AudioClass::get_configuration_descriptors");
|
info!(" AudioClass::get_configuration_descriptors");
|
||||||
// Control + 0-2 streaming
|
// Control + 0-2 streaming
|
||||||
let n_interfaces = 1 + (self.input.is_some() as u8) + (self.input.is_some() as u8);
|
let n_interfaces = 1 + (self.input.is_some() as u8) + (self.output.is_some() as u8);
|
||||||
|
|
||||||
debug!("writer.iad()");
|
debug!("writer.iad()");
|
||||||
// UAC2 4.6 Interface Association Descriptor
|
// UAC2 4.6 Interface Association Descriptor
|
||||||
@@ -835,22 +835,26 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass<B>
|
|||||||
fn poll(&mut self) {
|
fn poll(&mut self) {
|
||||||
debug!("poll");
|
debug!("poll");
|
||||||
// no streaming in alt 0
|
// no streaming in alt 0
|
||||||
if self.output.as_ref().unwrap().alt_setting != 1 {
|
if self.output.as_ref().is_none_or(|o| o.alt_setting != 0)
|
||||||
|
|| self.input.as_ref().is_none_or(|i| i.alt_setting != 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let mut buf = [0; 1024];
|
if let Some(o) = self.output.as_ref() {
|
||||||
match self.output.as_ref().unwrap().endpoint.read(&mut buf) {
|
let mut buf = [0; 1024];
|
||||||
Ok(len) if len > 0 => {
|
match o.endpoint.read(&mut buf) {
|
||||||
debug!("EP OUT data {:?}", len);
|
Ok(len) if len > 0 => {
|
||||||
}
|
debug!("EP OUT data {:?}", len);
|
||||||
Ok(_) => {
|
}
|
||||||
debug!("EP OUT empty");
|
Ok(_) => {
|
||||||
break;
|
debug!("EP OUT empty");
|
||||||
}
|
break;
|
||||||
Err(UsbError::WouldBlock) => break,
|
}
|
||||||
Err(err) => {
|
Err(UsbError::WouldBlock) => break,
|
||||||
debug!("EP OUT error {:?}", err);
|
Err(err) => {
|
||||||
|
debug!("EP OUT error {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -917,16 +921,18 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
if self.input.is_some() && iface == self.in_iface {
|
if self.input.is_some() && iface == self.in_iface {
|
||||||
let old_alt = self.input.as_ref().unwrap().alt_setting;
|
let old_alt = self.input.as_ref().unwrap().alt_setting;
|
||||||
if old_alt != alt_setting {
|
if old_alt != alt_setting {
|
||||||
|
self.clock_impl.alt_setting(alt_setting).ok();
|
||||||
self.audio_impl
|
self.audio_impl
|
||||||
.alternate_setting_changed(self, UsbDirection::In, alt_setting);
|
.alternate_setting_changed(UsbDirection::In, alt_setting);
|
||||||
self.input.as_mut().unwrap().alt_setting = alt_setting;
|
self.input.as_mut().unwrap().alt_setting = alt_setting;
|
||||||
xfer.accept().ok();
|
xfer.accept().ok();
|
||||||
}
|
}
|
||||||
} else if self.output.is_some() && iface == self.out_iface {
|
} else if self.output.is_some() && iface == self.out_iface {
|
||||||
let old_alt = self.output.as_ref().unwrap().alt_setting;
|
let old_alt = self.output.as_ref().unwrap().alt_setting;
|
||||||
if old_alt != alt_setting {
|
if old_alt != alt_setting {
|
||||||
|
self.clock_impl.alt_setting(alt_setting).ok();
|
||||||
self.audio_impl
|
self.audio_impl
|
||||||
.alternate_setting_changed(self, UsbDirection::Out, alt_setting);
|
.alternate_setting_changed(UsbDirection::Out, alt_setting);
|
||||||
self.output.as_mut().unwrap().alt_setting = alt_setting;
|
self.output.as_mut().unwrap().alt_setting = alt_setting;
|
||||||
xfer.accept().ok();
|
xfer.accept().ok();
|
||||||
}
|
}
|
||||||
@@ -1023,6 +1029,10 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
channel: u8,
|
channel: u8,
|
||||||
control: u8,
|
control: u8,
|
||||||
) {
|
) {
|
||||||
|
match entity {
|
||||||
|
1 => return self.set_clock_cur(xfer, channel, control),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
debug!(" Unimplemented.");
|
debug!(" Unimplemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1131,7 +1141,8 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
Ok(valid) => {
|
Ok(valid) => {
|
||||||
debug!(" {}", valid);
|
debug!(" {}", valid);
|
||||||
buf.write_u8(valid as u8)
|
buf.write_u8(valid as u8)
|
||||||
.map_err(|_e| UsbError::BufferOverflow)?;
|
.map_err(|_e| UsbError::BufferOverflow)
|
||||||
|
.ok();
|
||||||
Ok(1)
|
Ok(1)
|
||||||
}
|
}
|
||||||
Err(_e) => Err(UsbError::InvalidState),
|
Err(_e) => Err(UsbError::InvalidState),
|
||||||
@@ -1143,6 +1154,32 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn set_clock_cur(&mut self, xfer: ControlOut<B>, channel: u8, control: u8) {
|
||||||
|
match control.try_into() {
|
||||||
|
Ok(ClockSourceControlSelector::SamFreqControl) => {
|
||||||
|
debug!(" SamplingFreqControl");
|
||||||
|
if channel != 0 {
|
||||||
|
error!(
|
||||||
|
" Invalid channel {} for SamplingFreqControl GET CUR. Ignoring.",
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
match xfer.data().read_u32::<LittleEndian>() {
|
||||||
|
Ok(rate) => {
|
||||||
|
debug!(" SET SamplingFreqControl CUR {}", rate);
|
||||||
|
self.clock_impl.set_sample_rate(rate).ok();
|
||||||
|
xfer.accept().ok();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(" SET SamplingFreqControl CUR ERROR BAD DATA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
debug!(" Unimplemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fn get_clock_range(&mut self, xfer: ControlIn<B>, channel: u8, control: u8) {
|
fn get_clock_range(&mut self, xfer: ControlIn<B>, channel: u8, control: u8) {
|
||||||
match control.try_into() {
|
match control.try_into() {
|
||||||
Ok(ClockSourceControlSelector::SamFreqControl) => {
|
Ok(ClockSourceControlSelector::SamFreqControl) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user