usbd_uac2: fix clippies, add builder, support IN data

This commit is contained in:
2026-05-16 16:07:05 -07:00
parent 24e0083090
commit fb050c4a40
3 changed files with 94 additions and 65 deletions
+4 -4
View File
@@ -195,7 +195,7 @@ impl Descriptor for ClockSource {
writer.write_u8(self.bm_attributes())?; // bmAttributes writer.write_u8(self.bm_attributes())?; // bmAttributes
writer.write_u8(self.bm_controls())?; // bmControls writer.write_u8(self.bm_controls())?; // bmControls
writer.write_u8(self.assoc_terminal)?; // bAssocTerminal writer.write_u8(self.assoc_terminal)?; // bAssocTerminal
writer.write_u8(self.string.map_or(0, |n| u8::from(n)))?; // iClockSource writer.write_u8(self.string.map_or(0, u8::from))?; // iClockSource
Ok(()) Ok(())
} }
} }
@@ -247,7 +247,7 @@ impl Descriptor for InputTerminal {
| ((self.overflow_control as u8) << 2) | ((self.overflow_control as u8) << 2)
| ((self.phantom_power_control as u8) << 4), | ((self.phantom_power_control as u8) << 4),
)?; )?;
writer.write_u8(self.string.map_or(0, |s| u8::from(s)))?; writer.write_u8(self.string.map_or(0, u8::from))?;
Ok(()) Ok(())
} }
} }
@@ -289,7 +289,7 @@ impl Descriptor for OutputTerminal {
| ((self.underflow_control as u8) << 6), | ((self.underflow_control as u8) << 6),
)?; )?;
writer.write_u8(self.overflow_control as u8)?; writer.write_u8(self.overflow_control as u8)?;
writer.write_u8(self.string.map_or(0, |n| u8::from(n)))?; // iTerminal writer.write_u8(self.string.map_or(0, u8::from))?; // iTerminal
Ok(()) Ok(())
} }
} }
@@ -839,7 +839,7 @@ impl Descriptor for AudioStreamingInterface {
writer.write_u32::<LittleEndian>(self.format_bitmap as u32)?; writer.write_u32::<LittleEndian>(self.format_bitmap as u32)?;
writer.write_u8(self.num_channels)?; writer.write_u8(self.num_channels)?;
writer.write(&self.channel_config.bytes)?; writer.write(&self.channel_config.bytes)?;
writer.write_u8(self.string.map_or(0, |s| u8::from(s)))?; writer.write_u8(self.string.map_or(0, u8::from))?;
Ok(()) Ok(())
} }
} }
+89 -61
View File
@@ -16,7 +16,7 @@ use log::*;
use num_traits::{ConstZero, ToPrimitive}; use num_traits::{ConstZero, ToPrimitive};
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, UsbDeviceBuilder, UsbVidPid};
use usb_device::endpoint::{self, Endpoint, EndpointDirection, In, Out}; use usb_device::endpoint::{self, Endpoint, EndpointDirection, In, Out};
use usb_device::{UsbDirection, class_prelude::*}; use usb_device::{UsbDirection, class_prelude::*};
@@ -63,7 +63,7 @@ impl RangeType for u32 {}
/// Represent a range request/response. /// Represent a range request/response.
/// ///
/// ref: UAC2 5.2.2 /// ref: UAC2 5.2.2
#[derive(PartialEq, Eq, Ord)] #[derive(PartialEq, Eq)]
pub struct RangeEntry<T: RangeType> { pub struct RangeEntry<T: RangeType> {
pub min: T, pub min: T,
pub max: T, pub max: T,
@@ -93,10 +93,14 @@ impl<T: RangeType> RangeEntry<T> {
// The spec guarantees that ranges do not overlap, so compare by min is correct. // The spec guarantees that ranges do not overlap, so compare by min is correct.
impl<T: RangeType + PartialOrd> PartialOrd for RangeEntry<T> { impl<T: RangeType + PartialOrd> PartialOrd for RangeEntry<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.min.partial_cmp(&other.min) Some(self.cmp(other))
}
}
impl<T: RangeType + PartialOrd> Ord for RangeEntry<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.min.cmp(&other.min)
} }
} }
/// Represent Isochronous feedback as integer and fractional parts, internally as 16.16. /// Represent Isochronous feedback as integer and fractional parts, internally as 16.16.
/// ///
/// Provides conversions to and from the different USB formats. /// Provides conversions to and from the different USB formats.
@@ -146,6 +150,10 @@ pub trait AudioHandler<'a, B: UsbBus> {
/// `ep.read()`. /// `ep.read()`.
fn audio_data_rx(&mut self, ep: &Endpoint<'a, B, endpoint::Out>); fn audio_data_rx(&mut self, ep: &Endpoint<'a, B, endpoint::Out>);
/// Called when we must produce audio data for the host. `ep` is ready for
/// `ep.write()`.
fn audio_data_tx(&mut self, ep: &Endpoint<'a, B, endpoint::In>);
/// 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. Feedback always runs at 1ms (in /// return the correct feedback payload. Feedback always runs at 1ms (in
/// this implementation), and will be passed the nominal frame size. /// this implementation), and will be passed the nominal frame size.
@@ -183,6 +191,7 @@ pub trait ClockSource {
fn sample_rate(&self) -> u32; fn sample_rate(&self) -> u32;
/// Called when the host requests to set the sample rate. Not necessarily called at all startups, /// Called when the host requests to set the sample rate. Not necessarily called at all startups,
/// so alt_setting should start/stop the clock. Not required for 'fixed' clocks. /// so alt_setting should start/stop the clock. Not required for 'fixed' clocks.
#[allow(unused_variables)]
fn set_sample_rate( fn set_sample_rate(
&mut self, &mut self,
sample_rate: u32, sample_rate: u32,
@@ -239,7 +248,7 @@ pub trait ClockSource {
}; };
let cs = descriptors::ClockSource { let cs = descriptors::ClockSource {
id: id, id,
clock_type: Self::CLOCK_TYPE, clock_type: Self::CLOCK_TYPE,
sof_sync: Self::SOF_SYNC, sof_sync: Self::SOF_SYNC,
frequency_access, frequency_access,
@@ -367,6 +376,7 @@ pub struct TerminalConfig<D: EndpointDirection> {
} }
impl<D: EndpointDirection> TerminalConfig<D> { impl<D: EndpointDirection> TerminalConfig<D> {
#[allow(clippy::too_many_arguments)]
fn new( fn new(
base_id: u8, base_id: u8,
clock_source_id: u8, clock_source_id: u8,
@@ -400,7 +410,7 @@ impl<D: EndpointDirection> TerminalConfig<D> {
TerminalConfigBuilder::new() TerminalConfigBuilder::new()
} }
} }
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<Out> { impl 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,
@@ -437,7 +447,7 @@ impl<'a> TerminalConfigurationDescriptors for TerminalConfig<Out> {
// fn get_interface_descriptor(&self, id: InterfaceIndex) ) // fn get_interface_descriptor(&self, id: InterfaceIndex) )
} }
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<In> { impl 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,
@@ -838,7 +848,7 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbClass<B>
// UAC2 4.7 // UAC2 4.7
if let Some(descs) = additional_descs { if let Some(descs) = additional_descs {
for desc in descs.into_iter() { for desc in descs.iter() {
desc.write_descriptor(writer)?; desc.write_descriptor(writer)?;
} }
} }
@@ -893,10 +903,14 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbClass<B>
fn endpoint_in_complete(&mut self, addr: EndpointAddress) { fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
debug!("EP {} IN complete", addr); debug!("EP {} IN complete", addr);
if let Some(fb_ep) = self.feedback.as_ref() if let Some(_fb_ep) = self.feedback.as_ref()
&& addr.index() == self.fb_ep && addr.index() == self.fb_ep
{ {
self.emit_feedback(); self.emit_feedback();
} else if let Some(in_ep) = self.input.as_ref()
&& addr.index() == self.in_ep
{
self.audio_impl.audio_data_tx(&in_ep.endpoint);
} else { } else {
debug!(" unexpected IN on {}", addr); debug!(" unexpected IN on {}", addr);
} }
@@ -978,11 +992,11 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
fn class_request_out(&mut self, xfer: ControlOut<B>) { fn class_request_out(&mut self, xfer: ControlOut<B>) {
let req = xfer.request(); let req = xfer.request();
match (req.recipient, req.request.try_into()) { match (req.recipient, req.request.into()) {
(Recipient::Interface, Ok(ClassSpecificRequest::Cur)) => self.set_interface_cur(xfer), (Recipient::Interface, ClassSpecificRequest::Cur) => self.set_interface_cur(xfer),
(Recipient::Interface, Ok(ClassSpecificRequest::Range)) => {} (Recipient::Interface, ClassSpecificRequest::Range) => {}
(Recipient::Endpoint, Ok(ClassSpecificRequest::Cur)) => self.set_endpoint_cur(xfer), (Recipient::Endpoint, ClassSpecificRequest::Cur) => self.set_endpoint_cur(xfer),
(Recipient::Endpoint, Ok(ClassSpecificRequest::Range)) => {} (Recipient::Endpoint, ClassSpecificRequest::Range) => {}
_ => { _ => {
debug!(" Unimplemented."); debug!(" Unimplemented.");
} }
@@ -992,13 +1006,11 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
fn class_request_in(&mut self, xfer: ControlIn<B>) { fn class_request_in(&mut self, xfer: ControlIn<B>) {
let req = xfer.request(); let req = xfer.request();
match (req.recipient, req.request.try_into()) { match (req.recipient, req.request.into()) {
(Recipient::Interface, Ok(ClassSpecificRequest::Cur)) => self.get_interface_cur(xfer), (Recipient::Interface, ClassSpecificRequest::Cur) => self.get_interface_cur(xfer),
(Recipient::Interface, Ok(ClassSpecificRequest::Range)) => { (Recipient::Interface, ClassSpecificRequest::Range) => self.get_interface_range(xfer),
self.get_interface_range(xfer) (Recipient::Endpoint, ClassSpecificRequest::Cur) => self.get_endpoint_cur(xfer),
} (Recipient::Endpoint, ClassSpecificRequest::Range) => self.get_endpoint_range(xfer),
(Recipient::Endpoint, Ok(ClassSpecificRequest::Cur)) => self.get_endpoint_cur(xfer),
(Recipient::Endpoint, Ok(ClassSpecificRequest::Range)) => self.get_endpoint_range(xfer),
_ => { _ => {
debug!(" Unimplemented."); debug!(" Unimplemented.");
} }
@@ -1012,12 +1024,16 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
debug!(" SET_ALT_INTERFACE {} {}", iface, alt_setting); debug!(" SET_ALT_INTERFACE {} {}", iface, alt_setting);
if self.input.is_some() && iface == self.in_iface { if let Some(input) = &mut self.input
let old_alt = self.input.as_ref().unwrap().alt_setting; && iface == self.in_iface
{
let old_alt = input.alt_setting;
if old_alt != alt_setting { if old_alt != alt_setting {
self.audio_impl self.audio_impl
.alternate_setting_changed(UsbDirection::In, alt_setting); .alternate_setting_changed(UsbDirection::In, alt_setting);
self.input.as_mut().unwrap().alt_setting = alt_setting; input.alt_setting = alt_setting;
// Start the IN cycle
self.audio_impl.audio_data_tx(&input.endpoint);
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 {
@@ -1041,13 +1057,15 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
let req = xfer.request(); let req = xfer.request();
let iface = req.index as u8; let iface = req.index as u8;
debug!(" GET_ALT_INTERFACE {}", iface); debug!(" GET_ALT_INTERFACE {}", iface);
if self.input.is_some() && iface == self.in_iface { if let Some(input) = self.input.as_ref()
xfer.accept_with(&[self.input.as_ref().unwrap().alt_setting]) && iface == self.in_iface
.ok(); {
xfer.accept_with(&[input.alt_setting]).ok();
return; return;
} else if self.output.is_some() && iface == self.out_iface { } else if let Some(output) = self.output.as_ref()
xfer.accept_with(&[self.output.as_ref().unwrap().alt_setting]) && iface == self.out_iface
.ok(); {
xfer.accept_with(&[output.alt_setting]).ok();
return; return;
} }
debug!(" Unimplemented."); debug!(" Unimplemented.");
@@ -1109,9 +1127,8 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
channel: u8, channel: u8,
control: u8, control: u8,
) { ) {
match entity { if entity == 1 {
1 => return self.get_clock_cur(xfer, channel, control), return self.get_clock_cur(xfer, channel, control);
_ => {}
} }
debug!(" Unimplemented."); debug!(" Unimplemented.");
} }
@@ -1123,49 +1140,48 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
channel: u8, channel: u8,
control: u8, control: u8,
) { ) {
match entity { if entity == 1 {
1 => return self.set_clock_cur(xfer, channel, control), return self.set_clock_cur(xfer, channel, control);
_ => {}
} }
debug!(" Unimplemented."); debug!(" Unimplemented.");
} }
fn set_streaming_interface_cur( fn set_streaming_interface_cur(
&mut self, &mut self,
xfer: ControlOut<B>, _xfer: ControlOut<B>,
direction: UsbDirection, _direction: UsbDirection,
entity: u8, _entity: u8,
channel: u8, _channel: u8,
control: u8, _control: u8,
) { ) {
debug!(" Unimplemented."); debug!(" Unimplemented.");
} }
fn get_endpoint_cur(&mut self, xfer: ControlIn<B>) { fn get_endpoint_cur(&mut self, xfer: ControlIn<B>) {
let req = xfer.request(); let req = xfer.request();
let entity = (req.index >> 8) as u8; let _entity = (req.index >> 8) as u8;
let interface = (req.index & 0xff) as u8; let _interface = (req.index & 0xff) as u8;
let control = (req.value >> 8) as u8; let _control = (req.value >> 8) as u8;
let channel = (req.value & 0xff) as u8; let _channel = (req.value & 0xff) as u8;
debug!(" Unimplemented."); debug!(" Unimplemented.");
} }
fn get_endpoint_range(&mut self, xfer: ControlIn<B>) { fn get_endpoint_range(&mut self, xfer: ControlIn<B>) {
let req = xfer.request(); let req = xfer.request();
let entity = (req.index >> 8) as u8; let _entity = (req.index >> 8) as u8;
let interface = (req.index & 0xff) as u8; let _interface = (req.index & 0xff) as u8;
let control = (req.value >> 8) as u8; let _control = (req.value >> 8) as u8;
let channel = (req.value & 0xff) as u8; let _channel = (req.value & 0xff) as u8;
debug!(" Unimplemented."); debug!(" Unimplemented.");
} }
fn set_endpoint_cur(&mut self, xfer: ControlOut<B>) { fn set_endpoint_cur(&mut self, xfer: ControlOut<B>) {
let req = xfer.request(); let req = xfer.request();
let entity = (req.index >> 8) as u8; let _entity = (req.index >> 8) as u8;
let interface = (req.index & 0xff) as u8; let _interface = (req.index & 0xff) as u8;
let control = (req.value >> 8) as u8; let _control = (req.value >> 8) as u8;
let channel = (req.value & 0xff) as u8; let _channel = (req.value & 0xff) as u8;
debug!(" Unimplemented."); debug!(" Unimplemented.");
} }
@@ -1203,8 +1219,8 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
} }
} }
fn get_clock_cur(&mut self, xfer: ControlIn<B>, channel: u8, control: u8) { fn get_clock_cur(&mut self, xfer: ControlIn<B>, channel: u8, control: u8) {
match control.try_into() { match control.into() {
Ok(ClockSourceControlSelector::SamFreqControl) => { ClockSourceControlSelector::SamFreqControl => {
debug!(" SamplingFreqControl"); debug!(" SamplingFreqControl");
if channel != 0 { if channel != 0 {
error!( error!(
@@ -1222,7 +1238,7 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
}) })
.ok(); .ok();
} }
Ok(ClockSourceControlSelector::ClockValidControl) => { ClockSourceControlSelector::ClockValidControl => {
debug!(" ClockValidControl"); debug!(" ClockValidControl");
if channel != 0 { if channel != 0 {
error!( error!(
@@ -1248,8 +1264,8 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
} }
} }
fn set_clock_cur(&mut self, xfer: ControlOut<B>, channel: u8, control: u8) { fn set_clock_cur(&mut self, xfer: ControlOut<B>, channel: u8, control: u8) {
match control.try_into() { match control.into() {
Ok(ClockSourceControlSelector::SamFreqControl) => { ClockSourceControlSelector::SamFreqControl => {
debug!(" SamplingFreqControl"); debug!(" SamplingFreqControl");
if channel != 0 { if channel != 0 {
error!( error!(
@@ -1266,7 +1282,7 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
); );
xfer.accept().ok(); xfer.accept().ok();
} }
Err(e) => { Err(_e) => {
error!(" SET SamplingFreqControl CUR ERROR BAD DATA"); error!(" SET SamplingFreqControl CUR ERROR BAD DATA");
} }
} }
@@ -1277,8 +1293,8 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
} }
} }
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.into() {
Ok(ClockSourceControlSelector::SamFreqControl) => { ClockSourceControlSelector::SamFreqControl => {
debug!(" SamplingFreqControl"); debug!(" SamplingFreqControl");
if channel != 0 { if channel != 0 {
error!( error!(
@@ -1321,3 +1337,15 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B,
} }
} }
} }
/// Set up a `UsbDeviceBuilder` with the device IDs for Usb Audio Class 2.0
pub fn builder<'a, B: UsbBus>(
alloc: &'a UsbBusAllocator<B>,
vid_pid: UsbVidPid,
) -> UsbDeviceBuilder<'a, B> {
UsbDeviceBuilder::new(alloc, vid_pid)
.composite_with_iads() // required by UAC2 4.6
.device_class(0xef) // required by UAC2 4.2
.device_sub_class(0x02) // required by UAC2 4.2
.device_protocol(0x01) // required by UAC2 4.2
}
+1
View File
@@ -1,4 +1,5 @@
// src/log.rs (or log/mod.rs) // src/log.rs (or log/mod.rs)
#[allow(unused_imports)]
#[cfg(feature = "defmt")] #[cfg(feature = "defmt")]
pub use defmt::{debug, error, info, trace, warn}; pub use defmt::{debug, error, info, trace, warn};