From d57d90cbaec1dc04dd885911aac1f7f437d3b16f Mon Sep 17 00:00:00 2001 From: Keenan Tims Date: Sun, 3 May 2026 02:06:36 -0700 Subject: [PATCH] kind of working usb->i2s, but major buffering / timing problems --- examples/lpc55s28-evk/.cargo/config.toml | 2 + examples/lpc55s28-evk/Cargo.lock | 22 ++ examples/lpc55s28-evk/Cargo.toml | 2 + examples/lpc55s28-evk/memory.x | 7 +- examples/lpc55s28-evk/src/main.rs | 446 ++++++++++++++++------- src/lib.rs | 85 ++++- 6 files changed, 425 insertions(+), 139 deletions(-) diff --git a/examples/lpc55s28-evk/.cargo/config.toml b/examples/lpc55s28-evk/.cargo/config.toml index 0117d72..8d8735a 100644 --- a/examples/lpc55s28-evk/.cargo/config.toml +++ b/examples/lpc55s28-evk/.cargo/config.toml @@ -2,12 +2,14 @@ rustflags = [ "-C", "link-arg=-Tlink.x", "-C", "link-arg=-Tdefmt.x", + "-C", "debug-assertions", ] [target.thumbv8m.main-none-eabihf] rustflags = [ "-C", "link-arg=-Tlink.x", "-C", "link-arg=-Tdefmt.x", + # "-C", "debug-assertions", ] [build] diff --git a/examples/lpc55s28-evk/Cargo.lock b/examples/lpc55s28-evk/Cargo.lock index 1dd6b37..a12b8b8 100644 --- a/examples/lpc55s28-evk/Cargo.lock +++ b/examples/lpc55s28-evk/Cargo.lock @@ -275,6 +275,16 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + [[package]] name = "inout" version = "0.1.4" @@ -350,10 +360,12 @@ dependencies = [ "defmt-rtt", "embedded-hal 1.0.0", "embedded-io", + "heapless 0.9.3", "log-to-defmt", "lpc55-hal", "nb 1.1.0", "panic-halt", + "panic-probe", "static_cell", "usb-device", "usbd-uac2", @@ -463,6 +475,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt 1.0.1", +] + [[package]] name = "portable-atomic" version = "1.13.1" diff --git a/examples/lpc55s28-evk/Cargo.toml b/examples/lpc55s28-evk/Cargo.toml index 969e5d4..b263668 100644 --- a/examples/lpc55s28-evk/Cargo.toml +++ b/examples/lpc55s28-evk/Cargo.toml @@ -10,10 +10,12 @@ defmt = "1.0.1" defmt-rtt = "1.1.0" embedded-hal = "1.0.0" embedded-io = "0.7.1" +heapless = "0.9.3" log-to-defmt = "0.1.0" lpc55-hal = { version = "0.5.0", path = "../lpc55-hal" } nb = "1.1.0" panic-halt = "1.0.0" +panic-probe = { version = "1.0.0", features = ["print-defmt"] } static_cell = "2.1.1" usb-device = "0.3" usbd-uac2 = { version = "0.1.0", path = "../..", features = ["defmt"]} diff --git a/examples/lpc55s28-evk/memory.x b/examples/lpc55s28-evk/memory.x index fdd289f..5a68a5f 100644 --- a/examples/lpc55s28-evk/memory.x +++ b/examples/lpc55s28-evk/memory.x @@ -3,7 +3,7 @@ MEMORY FLASH : ORIGIN = 0x00000000, LENGTH = 512K /* for use with standard link.x */ - RAM : ORIGIN = 0x20000000, LENGTH = 64K + RAM : ORIGIN = 0x20000000, LENGTH = 192K /* would be used with proper link.x */ /* needs changes to r0 (initialization code) */ @@ -18,9 +18,4 @@ MEMORY /* USB1 SRAM regin */ /* USB1_SRAM : ORIGIN = 0x40100000, LENGTH = 16K */ - - /* To define our own USB RAM section in one regular */ - /* RAM, probably easiest to shorten length of RAM */ - /* above, and use this freed RAM section */ - } diff --git a/examples/lpc55s28-evk/src/main.rs b/examples/lpc55s28-evk/src/main.rs index 5251776..b9f91b1 100644 --- a/examples/lpc55s28-evk/src/main.rs +++ b/examples/lpc55s28-evk/src/main.rs @@ -1,83 +1,82 @@ #![no_main] #![no_std] -// extern crate panic_semihosting; -extern crate panic_halt; -use core::cell::OnceCell; -use cortex_m::asm::delay; +extern crate panic_probe; +#[defmt::panic_handler] +fn panic() -> ! { + panic_probe::hard_fault() +} + +use core::cell::RefCell; + use cortex_m_rt::entry; -use embedded_io::{ErrorType, Write}; -use nb; - -#[allow(unused_imports)] -use hal::prelude::*; -#[allow(unused_imports)] -use lpc55_hal as hal; -use lpc55_hal::{ - drivers::{ - Serial, - pins::{PinId, Pio0_29, Pio0_30}, - serial::Tx, - }, - peripherals::flexcomm::Usart0, - typestates::pin::{ - flexcomm::{Usart, UsartPins}, - function::{FC0_RXD_SDA_MOSI_DATA, FC0_TXD_SCL_MISO_WS}, - state::Special, - }, -}; - -use core::convert::Infallible; use defmt; +use defmt::debug; use defmt_rtt as _; +use embedded_io::Write; +use hal::Syscon; use hal::drivers::{Timer, UsbBus, pins}; -use static_cell::StaticCell; +use hal::peripherals::flexcomm::Flexcomm7; +use hal::prelude::*; +use hal::raw as pac; +use hal::time::Hertz; +use heapless::spsc::Queue; +use lpc55_hal::drivers::clocks::Pll; +use lpc55_hal::peripherals::syscon::ClockControl; +use lpc55_hal::raw::{FLEXCOMM7, I2S7}; +use lpc55_hal::{self as hal}; + use usb_device::{ - bus, + bus::{self}, device::{StringDescriptors, UsbDeviceBuilder, UsbVidPid}, endpoint::IsochronousSynchronizationType, }; use usbd_uac2::{ - self, AudioClassConfig, RangeEntry, TerminalConfig, USB_CLASS_AUDIO, UsbAudioClass, - UsbAudioClockImpl, UsbSpeed, + self, AudioClassConfig, RangeEntry, TerminalConfig, UsbAudioClass, UsbAudioClockImpl, UsbSpeed, constants::{FunctionCode, TerminalType}, - descriptors::{ChannelConfig, ClockSource, ClockType, FormatType1, LockDelay}, + descriptors::{ChannelConfig, ClockType, FormatType1, LockDelay}, }; -type SERIAL_RX_PIN = hal::Pin>; -type SERIAL_TX_PIN = hal::Pin>; -type SERIAL_PINS = (SERIAL_TX_PIN, SERIAL_RX_PIN); +const CODEC_I2C_ADDR: u8 = 0b0011010; -static SERIAL: StaticCell> = StaticCell::new(); +const SINE_LUT: [i32; 32] = [ + 0, 1636536, 3210180, 4660460, 5931640, 6974871, 7750062, 8227422, 8388607, 8227422, 7750062, + 6974871, 5931640, 4660460, 3210180, 1636536, 0, -1636536, -3210180, -4660460, -5931640, + -6974871, -7750062, -8227422, -8388607, -8227422, -7750062, -6974871, -5931640, -4660460, + -3210180, -1636536, +]; -pub struct DefmtUart(pub Tx) -where - U: Usart; +pub fn i2s_sine_test(i2s: &pac::I2S7) -> ! { + let mut idx = 0; + let mut count = 0usize; -impl ErrorType for DefmtUart -where - U: Usart, -{ - type Error = Infallible; -} -impl Write for DefmtUart -where - U: Usart, -{ - fn write(&mut self, buf: &[u8]) -> Result { - buf.iter().map(|c| nb::block!(self.0.write(*c))).last(); - Ok(buf.len()) - } + defmt::debug!("starting sine test"); - fn flush(&mut self) -> Result<(), Self::Error> { - // Blocking write, so flush is a no-op - Ok(()) + loop { + if i2s.fifostat.read().txnotfull().bit_is_set() { + let sample = SINE_LUT[idx] * 32; + + // ✅ Left channel + i2s.fifowr.write(|w| unsafe { w.bits(sample as u32) }); + + // wait for space if needed + while !i2s.fifostat.read().txnotfull().bit_is_set() {} + + // ✅ Right channel + i2s.fifowr.write(|w| unsafe { w.bits(sample as u32) }); + + idx = (idx + 1) & (SINE_LUT.len() - 1); + count += 1; + if count.is_multiple_of(48000) { + defmt::debug!("frames sent: {}", count) + } + } } } struct Clock {} impl Clock { - const RATES: [RangeEntry; 1] = [RangeEntry::new_fixed(44100)]; + const RATES: [RangeEntry; 1] = [RangeEntry::new_fixed(48000)]; } impl UsbAudioClockImpl for Clock { const CLOCK_TYPE: usbd_uac2::descriptors::ClockType = ClockType::InternalFixed; @@ -90,10 +89,225 @@ impl UsbAudioClockImpl for Clock { ) -> core::result::Result<&[usbd_uac2::RangeEntry], usbd_uac2::UsbAudioClassError> { Ok(&Clock::RATES) } + fn get_clock_validity(&self) -> core::result::Result { + Ok(true) + } } -struct Audio {} -impl<'a, B: bus::UsbBus> UsbAudioClass<'a, B> for Audio {} +struct Audio { + running: RefCell, + i2s: I2sTx, + queue: RefCell>, +} +impl Audio { + fn poll(&self) { + if !*self.running.borrow() { + return; + } + let stat = self.i2s.i2s.fifostat.read(); + + if stat.txerr().bit_is_set() { + self.i2s.i2s.fifostat.modify(|_, w| w.txerr().set_bit()); + defmt::error!("fifo tx error, txlvl: {}", stat.txlvl().bits()); + } + if stat.txlvl().bits() <= 6 { + // fifo is 8 deep + if let Some(sample) = self.queue.borrow_mut().dequeue() { + self.i2s + .i2s + .fifowr + .write(|w| unsafe { w.bits(sample.0 as u32) }); + self.i2s + .i2s + .fifowr + .write(|w| unsafe { w.bits(sample.1 as u32) }); + } else { + defmt::error!("queue underflow"); + self.i2s.i2s.fifowr.write(|w| unsafe { w.bits(0 as u32) }); + } + } + } +} +impl<'a, B: bus::UsbBus> UsbAudioClass<'a, B> for Audio { + fn alternate_setting_changed>( + &self, + ac: &mut usbd_uac2::AudioClass<'a, B, CS, AU>, + terminal: usb_device::UsbDirection, + alt_setting: u8, + ) { + match alt_setting { + 0 => *self.running.borrow_mut() = false, + 1 => *self.running.borrow_mut() = true, + _ => defmt::error!("unexpected alt setting {}", alt_setting), + } + } + fn audio_data_rx(&self, ep: &usb_device::endpoint::Endpoint<'a, B, usb_device::endpoint::Out>) { + let mut buf = [0; 384]; + let len = match ep.read(&mut buf) { + Ok(len) => len, + Err(e) => { + defmt::error!("usb error in rx callback"); + return; + } + }; + let buf = &buf[..len]; + for sample in buf.chunks_exact(8).map(|b| { + ( + u32::from_le_bytes(b[..4].try_into().unwrap()), + u32::from_le_bytes(b[4..].try_into().unwrap()), + ) + }) { + self.queue.borrow_mut().enqueue(sample).ok(); // TODO: ok is not ok here, it means we have overflowed the + } + } +} + +// copied from NXP SDK WM8904_Init +fn init_codec(i2c: &mut T) +where + T: _embedded_hal_blocking_i2c_WriteRead + _embedded_hal_blocking_i2c_Write, +{ + let mut buf = [0u8; 2]; + match i2c.write_read(CODEC_I2C_ADDR, &[0], &mut buf) { + Ok(_) => { + let chip_id = ((buf[0] as u16) << 8) | buf[1] as u16; + defmt::debug!("Read chip ID: {:x}", chip_id) + } + Err(_) => defmt::error!("Error reading I2C"), + } + + i2c.write(CODEC_I2C_ADDR, &[0x16, 0x00, 0x0f]).ok(); // clock rates 2 = OPCLK_ENA | CLK_SYS_ENA | CLK_DSP_ENA | TOCLK_ENA + i2c.write(CODEC_I2C_ADDR, &[0x6c, 0x01, 0x00]).ok(); // write sequencer 0 ENA + i2c.write(CODEC_I2C_ADDR, &[0x6f, 0x01, 0x00]).ok(); // write sequencer 3 START, INDEX=0 + // wait on write sequencer + defmt::debug!("[codec] waiting on write seq"); + loop { + let mut buf = [0; 2]; + i2c.write_read(CODEC_I2C_ADDR, &[0x70], &mut buf).ok(); + if buf[1] & 1 == 0 { + break; + } + } + defmt::debug!("[codec] write seq done"); + i2c.write(CODEC_I2C_ADDR, &[0x14, 0x00, 0x00]).ok(); // clock rates 0 + i2c.write(CODEC_I2C_ADDR, &[0x0c, 0x00, 0x03]).ok(); // power management 0 = INL_ENA | INR_ENA + i2c.write(CODEC_I2C_ADDR, &[0x0e, 0x00, 0x03]).ok(); // power management 2 = HPL_PGA_ENA | HPR_PGA_ENA + i2c.write(CODEC_I2C_ADDR, &[0x0f, 0x00, 0x03]).ok(); // power management 3 = LINEOUTL_ENA | LINEOUTR_ENA + + i2c.write(CODEC_I2C_ADDR, &[0x12, 0x00, 0x0f]).ok(); // power management 6 = DACL_ENA | DACR_ENA | ADCL_ENA | ADCR_ENA + i2c.write(CODEC_I2C_ADDR, &[0x0a, 0x00, 0x01]).ok(); // analog adc 0 = ADC_OSR128 + i2c.write(CODEC_I2C_ADDR, &[0x18, 0x00, 0x50]).ok(); // audio if 0 = AIFADCR_SRC | AIFDACR_SRC + i2c.write(CODEC_I2C_ADDR, &[0x21, 0x00, 0x40]).ok(); // dac digital 1 = DAC_OSR128 + i2c.write(CODEC_I2C_ADDR, &[0x2c, 0x00, 0x05]).ok(); // analog lin 0 = 0dB (unmute) + i2c.write(CODEC_I2C_ADDR, &[0x2d, 0x00, 0x05]).ok(); // analog rin 0 = 0dB (unmute) + i2c.write(CODEC_I2C_ADDR, &[0x39, 0x00, 0x39]).ok(); // analog out1 left = vol=0dB + i2c.write(CODEC_I2C_ADDR, &[0x3a, 0x00, 0x39]).ok(); // analog out1 right = vol=0dB + i2c.write(CODEC_I2C_ADDR, &[0x3b, 0x00, 0x39]).ok(); // analog out2 left = vol=0dB + i2c.write(CODEC_I2C_ADDR, &[0x3c, 0x00, 0x39]).ok(); // analog out2 right = vol=0dB + i2c.write(CODEC_I2C_ADDR, &[0x43, 0x00, 0x03]).ok(); // dc server 0 = HPOUTL_ENA | HPOUTR_ENA + i2c.write(CODEC_I2C_ADDR, &[0x5a, 0x00, 0xff]).ok(); // analog hp 0 = remove all shorts etc + i2c.write(CODEC_I2C_ADDR, &[0x5e, 0x00, 0xff]).ok(); // analog lineout 0 = remove all shorts etc + i2c.write(CODEC_I2C_ADDR, &[0x68, 0x00, 0x01]).ok(); // enable class w charge pump + i2c.write(CODEC_I2C_ADDR, &[0x62, 0x00, 0x01]).ok(); // enable charge pump + i2c.write(CODEC_I2C_ADDR, &[0x19, 0x00, 0x0e]).ok(); // audio if 1 = i2s, 32 bits mode + i2c.write(CODEC_I2C_ADDR, &[0x15, (0x05 << 2), 0x05]).ok(); // sys clock rate 512fs, sample rate 48 + i2c.write(CODEC_I2C_ADDR, &[0x16, 0x00, 0x0f]).ok(); // clock rates 2 = CLK_SYS_ENA + i2c.write(CODEC_I2C_ADDR, &[0x1a, 0x00, 0x08]).ok(); // audio interface 2 = no gpio, sysclk / 8 + i2c.write(CODEC_I2C_ADDR, &[0x1b, 0x00, 0x00]).ok(); // audio interface 3 = input lrclock + i2c.write(CODEC_I2C_ADDR, &[0x3d, 0x00, 0x00]).ok(); // analog out12 zc = play source = dac + i2c.write(CODEC_I2C_ADDR, &[0x1e, 0x01, 0xff]).ok(); // dac vol left = update left/right = 0dB +} + +pub struct I2sTx { + pub i2s: I2S7, +} + +pub fn init_i2s(mut fc7: FLEXCOMM7, mut i2s7: I2S7, syscon: &mut Syscon) -> I2sTx { + defmt::debug!("init i2s"); + // Enable BOTH + syscon.reset(&mut fc7); + syscon.enable_clock(&mut fc7); + + unsafe { + pac::IOCON::ptr().as_ref().unwrap().pio1_31.modify(|_, w| { + w.func() + .alt1() + .mode() + .inactive() + .slew() + .fast() + .invert() + .disabled() + .digimode() + .digital() + .od() + .normal() + }); + pac::SYSCON::ptr() + .as_ref() + .unwrap() + .fcclksel7() + .modify(|_, w| w.sel().enum_0x5()); // MCLK + pac::SYSCON::ptr() + .as_ref() + .unwrap() + .mclkclksel + .modify(|_, w| w.sel().enum_0x0()); // FRO 96MHz + pac::SYSCON::ptr() + .as_ref() + .unwrap() + .mclkdiv + .modify(|_, w| w.div().bits(3).halt().run().reset().released()); // div by 4 = 24MHz + pac::SYSCON::ptr() + .as_ref() + .unwrap() + .mclkio + .modify(|_, w| w.mclkio().output()); + }; + + // Select I2S TX function + fc7.pselid.write(|w| w.persel().i2s_transmit()); + + let regs = i2s7; + + // Enable TX FIFO only + regs.fifocfg.modify(|_, w| { + w.enabletx() + .enabled() + .enablerx() + .disabled() + .dmatx() + .disabled() + .txi2se0() + .zero() + }); + + // Flush + regs.fifocfg.modify(|_, w| w.emptytx().set_bit()); + + // Config + regs.cfg1.modify(|_, w| unsafe { + w.mstslvcfg() + .normal_master() + .onechannel() + .dual_channel() + .datalen() + .bits(31) + .mainenable() + .enabled() + .mode() + .classic_mode() + .datapause() + .normal() + }); + + regs.cfg2 + .modify(|_, w| unsafe { w.position().bits(0).framelen().bits(63) }); + + regs.div.modify(|_, w| unsafe { w.div().bits(7) }); // Clock source is MCLK (24MHz on FRO96) / 8 = 3MHz + + I2sTx { i2s: regs } +} #[entry] fn main() -> ! { @@ -106,24 +320,32 @@ fn main() -> ! { let mut gpio = hal.gpio.enabled(&mut syscon); let mut iocon = hal.iocon.enabled(&mut syscon); + debug!("start"); + let mut red_led = pins::Pio1_6::take() .unwrap() .into_gpio_pin(&mut iocon, &mut gpio) .into_output(hal::drivers::pins::Level::Low); // start turned on + debug!("iocon"); let usb0_vbus_pin = pins::Pio0_22::take() .unwrap() .into_usb0_vbus_pin(&mut iocon); + let codec_i2c_pins = ( + pins::Pio1_20::take().unwrap().into_i2c4_scl_pin(&mut iocon), + pins::Pio1_21::take().unwrap().into_i2c4_sda_pin(&mut iocon), + ); + let codec_i2s_pins = ( + pins::Pio0_21::take().unwrap().into_spi7_sck_pin(&mut iocon), + pins::Pio0_20::take().unwrap().into_i2s7_sda_pin(&mut iocon), + pins::Pio0_19::take().unwrap().into_i2s7_ws_pin(&mut iocon), + pins::Pio1_31::take().unwrap(), // MCLK + ); - let serial_rx_pin = pins::Pio0_29::take() - .unwrap() - .into_usart0_rx_pin(&mut iocon); - let serial_tx_pin = pins::Pio0_30::take() - .unwrap() - .into_usart0_tx_pin(&mut iocon); - - iocon.disabled(&mut syscon).release(); // save the environment :) + // iocon.disabled(&mut syscon).release(); // save the environment :) + debug!("clocks"); + // TODO: figure out how to configure the PLL for a more suitable audio clock. let clocks = hal::ClockRequirements::default() // .system_frequency(24.mhz()) // .system_frequency(72.mhz()) @@ -136,16 +358,22 @@ fn main() -> ! { .enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()), ); - let usart = hal + debug!("peripherals"); + + let i2c_peripheral = hal .flexcomm - .0 - .enabled_as_usart(&mut syscon, &clocks.support_flexcomm_token().unwrap()); + .4 + .enabled_as_i2c(&mut syscon, &clocks.support_flexcomm_token().unwrap()); + let mut i2c_bus = I2cMaster::new( + i2c_peripheral, + codec_i2c_pins, + Hertz::try_from(400.kHz()).unwrap(), + ); - let serial_config = hal::drivers::serial::config::Config::default().speed(115_200.Hz()); - let serial = Serial::new(usart, (serial_tx_pin, serial_rx_pin), serial_config).split(); - let serial_tx = DefmtUart(serial.0); - - // defmt_serial::defmt_serial(SERIAL.init(serial_tx)); + let i2s_peripheral = { + let fc7 = hal.flexcomm.7.release(); + init_i2s(fc7.0, fc7.2, &mut syscon) + }; let usb_peripheral = hal.usbhs.enabled_as_device( &mut anactrl, @@ -157,7 +385,16 @@ fn main() -> ! { let usb_bus = UsbBus::new(usb_peripheral, usb0_vbus_pin); let clock = Clock {}; - let audio = Audio {}; + + defmt::debug!("codec init"); + init_codec(&mut i2c_bus); + + // i2s_sine_test(&i2s_peripheral.i2s); + let audio = Audio { + i2s: i2s_peripheral, + queue: RefCell::new(heapless::spsc::Queue::new()), + running: RefCell::new(false), + }; let config = AudioClassConfig::new(UsbSpeed::High, FunctionCode::Other, &clock, &audio) .with_input_config(TerminalConfig::new( @@ -165,13 +402,13 @@ fn main() -> ! { 1, 2, FormatType1 { - bit_resolution: 24, + bit_resolution: 32, bytes_per_sample: 4, }, TerminalType::ExtLineConnector, ChannelConfig::default_chans(2), - IsochronousSynchronizationType::Adaptive, - LockDelay::Milliseconds(10), + IsochronousSynchronizationType::Asynchronous, + LockDelay::Undefined(0), None, )) .with_output_config(TerminalConfig::new( @@ -179,12 +416,12 @@ fn main() -> ! { 1, 2, FormatType1 { - bit_resolution: 24, + bit_resolution: 32, bytes_per_sample: 4, }, TerminalType::ExtLineConnector, ChannelConfig::default_chans(2), - IsochronousSynchronizationType::Adaptive, + IsochronousSynchronizationType::Asynchronous, LockDelay::Milliseconds(10), None, )); @@ -206,53 +443,10 @@ fn main() -> ! { .build(); defmt::info!("main loop"); - let mut need_zlp = false; - let mut buf = [0u8; 8]; - let mut size = 0; - let mut buf_in_use = false; + loop { - // if !usb_dev.poll(&mut []) { - // if !usb_dev.poll(&mut [&mut serial]) { - if !usb_dev.poll(&mut [&mut uac2]) { - continue; - } - - // let mut buf = [0u8; 512]; - - // match serial.read(&mut buf) { - // Ok(count) if count > 0 => { - // assert!(count == 1); - // // hprintln!("received some data on the serial port: {:?}", &buf[..count]).ok(); - // // cortex_m_semihosting::hprintln!("received:\n{}", core::str::from_utf8(&buf[..count]).unwrap()).ok(); - // red_led.set_low().ok(); // Turn on - - // // cortex_m_semihosting::hprintln!("read {:?}", &buf[..count]).ok(); - // cortex_m_semihosting::hprintln!("read {:?}", count).ok(); - - // // Echo back in upper case - // for c in buf[0..count].iter_mut() { - // if (0x61 <= *c && *c <= 0x7a) || (0x41 <= *c && *c <= 0x5a) { - // *c ^= 0x20; - // } - // } - - // let mut write_offset = 0; - // while write_offset < count { - // match serial.write(&buf[write_offset..count]) { - // Ok(len) if len > 0 => { - // write_offset += len; - // cortex_m_semihosting::hprintln!("wrote {:?}", len).ok(); - - // }, - // _ => {}, - // } - // } - - // // hprintln!("wrote it back").ok(); - // } - // _ => {} - // } - + usb_dev.poll(&mut [&mut uac2]); + audio.poll(); red_led.set_high().ok(); // Turn off } } diff --git a/src/lib.rs b/src/lib.rs index f657b98..ff731a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -703,6 +703,10 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass } // UAC2 4.9 & 2.4.10 + if let Some(stream) = &self.input { + stream.write_interface_descriptors(writer)?; + stream.write_endpoint_descriptors(writer)?; + } if let Some(stream) = &self.output { stream.write_interface_descriptors(writer)?; stream.write_endpoint_descriptors(writer)?; @@ -714,11 +718,6 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass writer.endpoint(feedback)?; } - if let Some(stream) = &self.input { - stream.write_interface_descriptors(writer)?; - stream.write_endpoint_descriptors(writer)?; - } - Ok(()) } fn control_out(&mut self, xfer: ControlOut) { @@ -741,6 +740,39 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass } } } + fn endpoint_out(&mut self, addr: EndpointAddress) { + debug!("EP {} out data", addr); + if addr.index() == self.out_ep { + self.audio_impl + .audio_data_rx(&self.output.as_ref().unwrap().endpoint) + } else { + debug!(" unexpected OUT on {}", addr); + } + } + + fn poll(&mut self) { + debug!("poll"); + // no streaming in alt 0 + if self.output.as_ref().unwrap().alt_setting != 1 { + return; + } + loop { + let mut buf = [0; 1024]; + match self.output.as_ref().unwrap().endpoint.read(&mut buf) { + Ok(len) if len > 0 => { + debug!("EP OUT data {:?}", len); + } + Ok(_) => { + debug!("EP OUT empty"); + break; + } + Err(UsbError::WouldBlock) => break, + Err(err) => { + debug!("EP OUT error {:?}", err); + } + } + } + } } impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<'a, B, CS, AU> { @@ -816,6 +848,11 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass< self.output.as_mut().unwrap().alt_setting = alt_setting; xfer.accept().ok(); } + } else { + debug!( + " not handled (in: {}, out: {}, got: {}).", + self.in_iface, self.out_iface, iface + ); } } fn get_alt_interface(&mut self, xfer: ControlIn) { @@ -985,7 +1022,7 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass< debug!(" SamplingFreqControl"); if channel != 0 { error!( - " Invalid channel {} for SamplingFreqControl GET RANGE. Ignoring.", + " Invalid channel {} for SamplingFreqControl GET CUR. Ignoring.", channel ); } @@ -1000,7 +1037,28 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass< }) .ok(); } - _ => debug!(" Unimplemented."), + Ok(ClockSourceControlSelector::ClockValidControl) => { + debug!(" ClockValidControl"); + if channel != 0 { + error!( + " Invalid channel {} for ClockValidControl GET CUR. Ignoring.", + channel + ); + } + xfer.accept(|mut buf| match self.clock_impl.get_clock_validity() { + Ok(valid) => { + debug!(" {}", valid); + buf.write_u8(valid as u8) + .map_err(|_e| UsbError::BufferOverflow)?; + Ok(1) + } + Err(_e) => Err(UsbError::InvalidState), + }) + .ok(); + } + _ => { + debug!(" Unimplemented."); + } } } fn get_clock_range(&mut self, xfer: ControlIn, channel: u8, control: u8) { @@ -1034,4 +1092,17 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass< } } } + pub fn read(&mut self, buf: &mut [u8]) -> usb_device::Result { + match self.output.as_mut().unwrap().endpoint.read(buf) { + Ok(len) => { + debug!("NO CB read {} bytes", len); + Ok(len) + } + Err(UsbError::WouldBlock) => Err(UsbError::WouldBlock), + Err(e) => { + error!("read error"); + Err(e) + } + } + } }