Mathias Gottschlag il y a 4 ans
révision
69dce1af2a
13 fichiers modifiés avec 2372 ajouts et 0 suppressions
  1. 14
    0
      .cargo/config
  2. 2
    0
      .gitignore
  3. 998
    0
      Cargo.lock
  4. 36
    0
      Cargo.toml
  5. 31
    0
      build.rs
  6. 13
    0
      memory.x
  7. 6
    0
      openocd.cfg
  8. 26
    0
      openocd.gdb
  9. 205
    0
      src/audio_buffer.rs
  10. 121
    0
      src/i2s_master.rs
  11. 278
    0
      src/main.rs
  12. 66
    0
      src/usb.rs
  13. 576
    0
      src/usb_audio.rs

+ 14
- 0
.cargo/config Voir le fichier

@@ -0,0 +1,14 @@
1
+[build]
2
+# Instruction set of Cortex-M0+
3
+target = "thumbv6m-none-eabi"
4
+
5
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
6
+rustflags = [
7
+    "-C", "link-arg=--nmagic",
8
+    "-C", "link-arg=-Tlink.x",
9
+    "-C", "inline-threshold=5",
10
+    "-C", "no-vectorize-loops",
11
+]
12
+#runner = "elf2uf2-rs -d"
13
+runner = "arm-none-eabi-gdb -x openocd.gdb"
14
+

+ 2
- 0
.gitignore Voir le fichier

@@ -0,0 +1,2 @@
1
+/target
2
+*.swp

+ 998
- 0
Cargo.lock
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 36
- 0
Cargo.toml Voir le fichier

@@ -0,0 +1,36 @@
1
+[package]
2
+name = "rp2040-usb-sound-card"
3
+version = "0.1.0"
4
+edition = "2018"
5
+resolver = "2"
6
+
7
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
+
9
+[dependencies]
10
+cortex-m = "0.7.2"
11
+cortex-m-rt = "0.7"
12
+cortex-m-rtic = "0.6.0-alpha.5"
13
+cortex-m-semihosting = "0.3.7"
14
+embedded-hal ="0.2.5"
15
+embedded-time = "0.12.0"
16
+nb = "1.0"
17
+#panic-halt = "0.2.0"
18
+panic-semihosting = "0.5"
19
+#panic-write = "0.1.0"
20
+pio = { git = "https://github.com/rp-rs/pio-rs.git", branch = "main" }
21
+pio-proc = { git = "https://github.com/rp-rs/pio-rs.git", branch = "main" }
22
+#rp2040-hal = { git = "https://github.com/rp-rs/rp-hal.git", features = ["rt"] }
23
+rp2040-hal = { path = "../rp-hal/rp2040-hal", features = ["rt"] }
24
+rp2040-boot2 = "0.1.2"
25
+stable_deref_trait = { version = "1.2.0", default-features = false }
26
+systick-monotonic = "0.1.0-alpha.0"
27
+usb-device = { version = "0.2.8", features = ["control-buffer-256"] }
28
+void = { version = "*", default_features = false }
29
+
30
+[patch.crates-io]
31
+usb-device = { path = "../usb-device" }
32
+
33
+[profile]
34
+[profile.release]
35
+debug = true
36
+lto = true

+ 31
- 0
build.rs Voir le fichier

@@ -0,0 +1,31 @@
1
+//! This build script copies the `memory.x` file from the crate root into
2
+//! a directory where the linker can always find it at build time.
3
+//! For many projects this is optional, as the linker always searches the
4
+//! project root directory -- wherever `Cargo.toml` is. However, if you
5
+//! are using a workspace or have a more complicated build setup, this
6
+//! build script becomes required. Additionally, by requesting that
7
+//! Cargo re-run the build script whenever `memory.x` is changed,
8
+//! updating `memory.x` ensures a rebuild of the application with the
9
+//! new memory settings.
10
+
11
+use std::env;
12
+use std::fs::File;
13
+use std::io::Write;
14
+use std::path::PathBuf;
15
+
16
+fn main() {
17
+    // Put `memory.x` in our output directory and ensure it's
18
+    // on the linker search path.
19
+    let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20
+    File::create(out.join("memory.x"))
21
+        .unwrap()
22
+        .write_all(include_bytes!("memory.x"))
23
+        .unwrap();
24
+    println!("cargo:rustc-link-search={}", out.display());
25
+
26
+    // By default, Cargo will re-run a build script whenever
27
+    // any file in the project changes. By specifying `memory.x`
28
+    // here, we ensure the build script is only re-run when
29
+    // `memory.x` is changed.
30
+    println!("cargo:rerun-if-changed=memory.x");
31
+}

+ 13
- 0
memory.x Voir le fichier

@@ -0,0 +1,13 @@
1
+MEMORY {
2
+    BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
3
+    FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
4
+    RAM   : ORIGIN = 0x20000000, LENGTH = 256K
5
+}
6
+
7
+SECTIONS {
8
+    /* ### Boot loader */
9
+    .boot2 ORIGIN(BOOT2) :
10
+    {
11
+        KEEP(*(.boot2));
12
+    } > BOOT2
13
+} INSERT BEFORE .text;

+ 6
- 0
openocd.cfg Voir le fichier

@@ -0,0 +1,6 @@
1
+source [find interface/cmsis-dap.cfg]
2
+transport select swd
3
+
4
+adapter speed 100
5
+source [find target/rp2040.cfg]
6
+

+ 26
- 0
openocd.gdb Voir le fichier

@@ -0,0 +1,26 @@
1
+set history save on
2
+set confirm off
3
+target extended-remote :3333
4
+monitor arm semihosting enable
5
+monitor reset halt
6
+
7
+# print demangled symbols
8
+set print asm-demangle on
9
+
10
+# set backtrace limit to not have infinite backtrace loops
11
+set backtrace limit 32
12
+
13
+# detect unhandled exceptions, hard faults and panics
14
+break DefaultHandler
15
+break HardFault
16
+#break rust_begin_unwind
17
+
18
+#break main
19
+
20
+load
21
+run
22
+# continue
23
+# monitor verify
24
+# monitor reset
25
+# quit
26
+

+ 205
- 0
src/audio_buffer.rs Voir le fichier

@@ -0,0 +1,205 @@
1
+//! Audio buffer type to transfer audio data within different software/hardware components.
2
+#![warn(missing_docs)]
3
+
4
+use core::cell::{Cell, UnsafeCell};
5
+use core::ops::Deref;
6
+
7
+use cortex_m::interrupt::{CriticalSection, Mutex};
8
+use stable_deref_trait::StableDeref;
9
+
10
+/// Each packet contains 1 millisecond of audio data (48 samples at 48000kHz).
11
+///
12
+/// This translates two twice the number of values, as each sample contains two channels.
13
+pub const SAMPLES_PER_PACKET: usize = 48;
14
+/// Number of channels for all audio data.
15
+pub const CHANNELS: usize = 2;
16
+
17
+/// Buffer for a single packet with audio data.
18
+pub type Packet = [u32; SAMPLES_PER_PACKET * CHANNELS];
19
+
20
+/// Type for audio packet buffering.
21
+///
22
+/// The audio packets always contain two channels with 32-bit samples. The type contains a number
23
+/// of buffers each containing the same fixed number of samples.
24
+pub struct AudioBuffer<const PACKETS: usize> {
25
+    mutex: Mutex<AudioBufferInner<PACKETS>>,
26
+}
27
+
28
+struct AudioBufferInner<const PACKETS: usize> {
29
+    /// Packet contents.
30
+    packets: [UnsafeCell<Packet>; PACKETS],
31
+    /// Bitmask of packets which contain valid data and are not currently being read.
32
+    valid: Cell<u32>,
33
+    /// Bitmask of packets which do not contain valid data and which are not currently being
34
+    /// written.
35
+    invalid: Cell<u32>,
36
+    /// Bitmask of all packets which are currently borrowed via `borrow_read()`.
37
+    reading: Cell<u32>,
38
+    /// Bitmask of all packets which are currently borrowed via `borrow_write()`.
39
+    writing: Cell<u32>,
40
+}
41
+
42
+impl AudioBuffer<4> {
43
+    /// Creates a new audio buffer.
44
+    pub const fn new() -> Self {
45
+        const PACKETS: usize = 4;
46
+        // Initially, all packet contents are invalid and no packets are currently being read or
47
+        // written.
48
+        AudioBuffer {
49
+            mutex: Mutex::new(AudioBufferInner {
50
+                packets: [
51
+                    UnsafeCell::new([0; SAMPLES_PER_PACKET * CHANNELS]),
52
+                    UnsafeCell::new([0; SAMPLES_PER_PACKET * CHANNELS]),
53
+                    UnsafeCell::new([0; SAMPLES_PER_PACKET * CHANNELS]),
54
+                    UnsafeCell::new([0; SAMPLES_PER_PACKET * CHANNELS]),
55
+                ],
56
+                valid: Cell::new(0),
57
+                invalid: Cell::new(((1 << PACKETS) - 1) as u32),
58
+                reading: Cell::new(0),
59
+                writing: Cell::new(0),
60
+            }),
61
+        }
62
+    }
63
+}
64
+
65
+impl<const PACKETS: usize> AudioBuffer<PACKETS> {
66
+    /// Returns an interface to read packets from this buffer.
67
+    pub fn read(&'static self) -> ReadBuffer<PACKETS> {
68
+        ReadBuffer { buffer: self }
69
+    }
70
+
71
+    /// Returns an interface to write packets to this buffer.
72
+    pub fn write(&'static self) -> WriteBuffer<PACKETS> {
73
+        WriteBuffer { buffer: self }
74
+    }
75
+}
76
+
77
+/// Interface to read packets from a buffer.
78
+pub struct ReadBuffer<const PACKETS: usize> {
79
+    buffer: &'static AudioBuffer<PACKETS>,
80
+}
81
+
82
+impl<const PACKETS: usize> ReadBuffer<PACKETS> {
83
+    /// Returns whether there are any buffers with valid data ready for reading.
84
+    pub fn can_read(&self, cs: &CriticalSection) -> bool {
85
+        let inner = self.buffer.mutex.borrow(cs);
86
+        inner.valid.get() != 0
87
+    }
88
+
89
+    /// Borrows a single buffer for reading, returns `None` if no buffer with valid data is
90
+    /// available.
91
+    ///
92
+    /// The caller needs to return the buffer via `read_finished()` once the data has been
93
+    /// processed.
94
+    pub fn borrow_read(&self, cs: &CriticalSection) -> Option<ReadPacket<PACKETS>> {
95
+        let inner = self.buffer.mutex.borrow(cs);
96
+        let valid = inner.valid.get();
97
+        if valid == 0 {
98
+            return None;
99
+        }
100
+        let index = valid.trailing_zeros();
101
+        inner.valid.set(valid & !(1 << index));
102
+        inner.reading.set(inner.reading.get() | (1 << index));
103
+
104
+        let index = index as usize;
105
+        Some(ReadPacket {
106
+            buffer: self.buffer,
107
+            data: unsafe { &*inner.packets[index].get() },
108
+            index,
109
+        })
110
+    }
111
+
112
+    /// Returns a buffer that was allocated via `borrow_read()` and marks it as empty.
113
+    pub fn read_finished(&self, packet: ReadPacket<PACKETS>, cs: &CriticalSection) {
114
+        assert!((self.buffer as *const _) == (packet.buffer as *const _));
115
+        let inner = self.buffer.mutex.borrow(cs);
116
+        let index = packet.index;
117
+        inner.reading.set(inner.reading.get() & !(1 << index));
118
+        inner.invalid.set(inner.invalid.get() | (1 << index));
119
+    }
120
+}
121
+
122
+/// Interface to write packets to a buffer.
123
+pub struct WriteBuffer<const PACKETS: usize> {
124
+    buffer: &'static AudioBuffer<PACKETS>,
125
+}
126
+
127
+impl<const PACKETS: usize> WriteBuffer<PACKETS> {
128
+    /// Returns whether there are any empty buffers ready for writing.
129
+    pub fn can_write(&self, cs: &CriticalSection) -> bool {
130
+        let inner = self.buffer.mutex.borrow(cs);
131
+        inner.invalid.get() != 0
132
+    }
133
+
134
+    /// Borrows a single buffer for writing, returns `None` if no empty buffer is available.
135
+    ///
136
+    /// The caller needs to return the buffer via `write_finished()` once it was filled with data.
137
+    pub fn borrow_write(&self, cs: &CriticalSection) -> Option<WritePacket<PACKETS>> {
138
+        let inner = self.buffer.mutex.borrow(cs);
139
+        let invalid = inner.invalid.get();
140
+        if invalid == 0 {
141
+            return None;
142
+        }
143
+        let index = invalid.trailing_zeros();
144
+        inner.invalid.set(invalid & !(1 << index));
145
+        inner.writing.set(inner.writing.get() | (1 << index));
146
+
147
+        let index = index as usize;
148
+        Some(WritePacket {
149
+            buffer: self.buffer,
150
+            data: unsafe { &mut *inner.packets[index].get() },
151
+            index,
152
+        })
153
+    }
154
+
155
+    /// Returns a buffer that was allocated via `borrow_write()` and marks it as ready for reading.
156
+    pub fn write_finished(&self, packet: WritePacket<PACKETS>, cs: &CriticalSection) {
157
+        assert!((self.buffer as *const _) == (packet.buffer as *const _));
158
+        let inner = self.buffer.mutex.borrow(cs);
159
+        let index = packet.index;
160
+        inner.writing.set(inner.writing.get() & !(1 << index));
161
+        inner.valid.set(inner.valid.get() | (1 << index));
162
+    }
163
+}
164
+
165
+/// Packet buffer that has been borrowed for reading (i.e., the buffer contains valid data).
166
+pub struct ReadPacket<const PACKETS: usize> {
167
+    buffer: &'static AudioBuffer<PACKETS>,
168
+    data: &'static Packet,
169
+    index: usize,
170
+}
171
+
172
+impl<const PACKETS: usize> AsRef<[u32]> for ReadPacket<PACKETS> {
173
+    fn as_ref(&self) -> &[u32] {
174
+        self.data
175
+    }
176
+}
177
+
178
+impl<const PACKETS: usize> Deref for ReadPacket<PACKETS> {
179
+    type Target = Packet;
180
+
181
+    fn deref(&self) -> &Self::Target {
182
+        &self.data
183
+    }
184
+}
185
+
186
+unsafe impl<const PACKETS: usize> StableDeref for ReadPacket<PACKETS> {}
187
+
188
+/// Packet buffer that has been borrowed for writing (i.e., the buffer was previously empty).
189
+pub struct WritePacket<const PACKETS: usize> {
190
+    buffer: &'static AudioBuffer<PACKETS>,
191
+    data: &'static mut Packet,
192
+    index: usize,
193
+}
194
+
195
+impl<const PACKETS: usize> AsRef<[u32]> for WritePacket<PACKETS> {
196
+    fn as_ref(&self) -> &[u32] {
197
+        self.data
198
+    }
199
+}
200
+
201
+impl<const PACKETS: usize> AsMut<[u32]> for WritePacket<PACKETS> {
202
+    fn as_mut(&mut self) -> &mut [u32] {
203
+        self.data
204
+    }
205
+}

+ 121
- 0
src/i2s_master.rs Voir le fichier

@@ -0,0 +1,121 @@
1
+use crate::audio_buffer::{ReadBuffer, ReadPacket};
2
+use core::marker::PhantomData;
3
+use cortex_m::interrupt;
4
+use rp2040_hal::dma::{Channel, ChannelIndex, SingleBuffering, SingleBufferingConfig};
5
+use rp2040_hal::gpio::bank0::{Gpio0, Gpio1, Gpio3, Gpio6};
6
+use rp2040_hal::gpio::{Function, Pin, Pio0};
7
+use rp2040_hal::pac::PIO0;
8
+use rp2040_hal::pio::{
9
+    PIOBuilder, PIOExt, StateMachineIndex, Tx, UninitStateMachine, ValidStateMachine, PIO,
10
+};
11
+
12
+pub trait PinConfig {
13
+    type PIO;
14
+
15
+    const OUT_MASK: u32;
16
+}
17
+
18
+impl PinConfig
19
+    for (
20
+        Pin<Gpio0, Function<Pio0>>,
21
+        Pin<Gpio1, Function<Pio0>>,
22
+        Pin<Gpio6, Function<Pio0>>,
23
+        Pin<Gpio3, Function<Pio0>>,
24
+    )
25
+{
26
+    type PIO = PIO0;
27
+
28
+    const OUT_MASK: u32 = 0x4b;
29
+}
30
+
31
+pub struct I2sMaster<SM: ValidStateMachine, DMACH: ChannelIndex, LRCLK, BCLK, SDO, SDI> {
32
+    tx: Option<SingleBuffering<Channel<DMACH>, ReadPacket<4>, Tx<SM>>>,
33
+    out_buffer: ReadBuffer<4>,
34
+    _phantom: PhantomData<(SM, LRCLK, BCLK, SDO, SDI)>,
35
+}
36
+
37
+impl<P: PIOExt, SM: StateMachineIndex, DMACH: ChannelIndex, LRCLK, BCLK, SDO, SDI>
38
+    I2sMaster<(P, SM), DMACH, LRCLK, BCLK, SDO, SDI>
39
+where
40
+    (LRCLK, BCLK, SDO, SDI): PinConfig,
41
+{
42
+    pub fn init(
43
+        pio: &mut PIO<P>,
44
+        sm: UninitStateMachine<(P, SM)>,
45
+        dma: Channel<DMACH>,
46
+        out_buffer: ReadBuffer<4>,
47
+        _lrclk: LRCLK,
48
+        _bclk: BCLK,
49
+        _sdo: SDO,
50
+        _sdi: SDI,
51
+    ) -> Self {
52
+        let program = pio_proc::pio!(
53
+            32,
54
+            "
55
+        .side_set 2
56
+
57
+        .wrap_target
58
+            pull noblock      side 0b11 [0]
59
+            set x, 14         side 0b11 [0]
60
+
61
+        leftloop:
62
+            out pins, 1       side 0b00 [6]
63
+            in pins, 1        side 0b01 [5]
64
+            jmp x-- leftloop  side 0b01 [0]
65
+
66
+            out pins, 1       side 0b00 [6]
67
+            in pins, 1        side 0b01 [5]
68
+            set x, 14         side 0b01 [0]
69
+        rightloop:
70
+            out pins, 1       side 0b10 [6]
71
+            in pins, 1        side 0b11 [5]
72
+            jmp x-- rightloop side 0b11 [0]
73
+
74
+            out pins, 1       side 0b10 [6]
75
+            in pins, 1        side 0b11 [3]
76
+            push noblock      side 0b11 [0]
77
+        .wrap
78
+                    "
79
+        );
80
+
81
+        let installed = pio.install(&program.program).unwrap();
82
+        let (mut sm, _rx, tx) = PIOBuilder::from_program(installed)
83
+            .side_set_pin_base(0)
84
+            //.set_pins(6, 1)
85
+            .out_pins(6, 1)
86
+            .in_pin_base(3)
87
+            .clock_divisor(4.0)
88
+            .build(sm);
89
+        sm.set_pindirs_with_mask(
90
+            <(LRCLK, BCLK, SDO, SDI)>::OUT_MASK,
91
+            <(LRCLK, BCLK, SDO, SDI)>::OUT_MASK,
92
+        );
93
+
94
+        // Start the first DMA transfer.
95
+        let packet = interrupt::free(|cs| out_buffer.borrow_read(cs).unwrap());
96
+        let tx = SingleBufferingConfig::new(dma, packet, tx).start();
97
+
98
+        // Start I2S.
99
+        // TODO: Synchronized start of multiple I2S instances?
100
+        sm.start();
101
+
102
+        I2sMaster {
103
+            tx: Some(tx),
104
+            out_buffer,
105
+            _phantom: PhantomData,
106
+        }
107
+    }
108
+
109
+    pub fn next_packet(&mut self) {
110
+        if !self.tx.as_mut().unwrap().check_irq0() {
111
+            return;
112
+        }
113
+
114
+        let (dma, prev_packet, tx) = self.tx.take().unwrap().wait();
115
+        let packet = interrupt::free(|cs| {
116
+            self.out_buffer.read_finished(prev_packet, cs);
117
+            self.out_buffer.borrow_read(cs).unwrap()
118
+        });
119
+        self.tx = Some(SingleBufferingConfig::new(dma, packet, tx).start());
120
+    }
121
+}

+ 278
- 0
src/main.rs Voir le fichier

@@ -0,0 +1,278 @@
1
+#![no_std]
2
+#![no_main]
3
+
4
+//use panic_halt as _;
5
+use cortex_m::interrupt::CriticalSection;
6
+use panic_semihosting as _;
7
+use rp2040_hal::pac::UART0;
8
+use rp2040_hal::uart;
9
+
10
+mod audio_buffer;
11
+mod i2s_master;
12
+mod usb;
13
+mod usb_audio;
14
+
15
+#[link_section = ".boot2"]
16
+#[used]
17
+pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
18
+
19
+/*pub fn uart<'cs>(cs: &'cs CriticalSection) -> &'cs mut uart::UartPeripheral<uart::Enabled, UART0> {
20
+    use panic_write::PanicHandler;
21
+
22
+    unsafe {
23
+        let panic_handler = app::PANIC_HANDLER.borrow(cs).get();
24
+        let panic_handler_ref = core::mem::transmute::<
25
+            *mut Option<core::pin::Pin<PanicHandler<uart::UartPeripheral<uart::Enabled, UART0>>>>,
26
+            &mut Option<core::pin::Pin<PanicHandler<uart::UartPeripheral<uart::Enabled, UART0>>>>,
27
+        >(panic_handler);
28
+        panic_handler_ref.as_mut().unwrap()
29
+    }
30
+}*/
31
+
32
+#[rtic::app(device = rp2040_hal::pac, peripherals = true, dispatchers = [RTC_IRQ, XIP_IRQ])]
33
+mod app {
34
+    use embedded_hal::digital::v2::OutputPin;
35
+    use embedded_time::duration::Milliseconds;
36
+    use embedded_time::rate::{Extensions, Megahertz};
37
+    use rp2040_hal::clocks::ClocksManager;
38
+    use rp2040_hal::gpio;
39
+    //use rp2040_hal::pio::{PIOBuilder, PIOExt};
40
+    use crate::audio_buffer::AudioBuffer;
41
+    use crate::i2s_master::I2sMaster;
42
+    use crate::usb::Usb;
43
+    //use core::cell::UnsafeCell;
44
+    use core::fmt::Write;
45
+    use cortex_m::interrupt::{self, Mutex};
46
+    //use panic_write::PanicHandler;
47
+    use rp2040_hal::dma::{DMAExt, SingleChannel, CH0};
48
+    use rp2040_hal::gpio::bank0::{Gpio0, Gpio1, Gpio3, Gpio6};
49
+    use rp2040_hal::gpio::{Function, FunctionUart, Pin, Pio0};
50
+    use rp2040_hal::pac::UART0;
51
+    use rp2040_hal::pio::{PIOExt, PIO0SM0};
52
+    use rp2040_hal::pll::common_configs::PLL_USB_48MHZ;
53
+    use rp2040_hal::pll::{setup_pll_blocking, PLLConfig};
54
+    use rp2040_hal::sio::Sio;
55
+    use rp2040_hal::timer::Timer;
56
+    //use rp2040_hal::uart;
57
+    use rp2040_hal::usb::UsbBus;
58
+    use rp2040_hal::watchdog::Watchdog;
59
+    use rp2040_hal::xosc::setup_xosc_blocking;
60
+    use systick_monotonic::Systick;
61
+    use usb_device::class_prelude::*;
62
+
63
+    const XOSC_CRYSTAL_FREQ: u32 = 12_000_000;
64
+    pub const PLL_SYS_86MHZ: PLLConfig<Megahertz> = PLLConfig {
65
+        vco_freq: Megahertz(1032),
66
+        refdiv: 1,
67
+        post_div1: 6,
68
+        post_div2: 2,
69
+    };
70
+
71
+    static AUDIO_BUFFER: AudioBuffer<4> = AudioBuffer::new();
72
+    /*pub static UART: Mutex<
73
+        MaybeUninit<Option<&'static mut uart::UartPeripheral<uart::Enabled, UART0>>>,
74
+    > = Mutex::new(UnsafeCell::new(None));*/
75
+    /*pub static mut PANIC_HANDLER: Mutex<
76
+        UnsafeCell<
77
+            Option<core::pin::Pin<PanicHandler<uart::UartPeripheral<uart::Enabled, UART0>>>>,
78
+        >,
79
+    > = Mutex::new(UnsafeCell::new(None));*/
80
+
81
+    #[monotonic(binds = SysTick, default = true)]
82
+    type SystickMono = Systick<100>;
83
+
84
+    #[shared]
85
+    struct Shared {}
86
+
87
+    #[local]
88
+    struct Local {
89
+        led: gpio::Pin<gpio::pin::bank0::Gpio25, gpio::PushPullOutput>,
90
+        i2s: I2sMaster<
91
+            PIO0SM0,
92
+            CH0,
93
+            Pin<Gpio0, Function<Pio0>>,
94
+            Pin<Gpio1, Function<Pio0>>,
95
+            Pin<Gpio6, Function<Pio0>>,
96
+            Pin<Gpio3, Function<Pio0>>,
97
+        >,
98
+        usb: Usb,
99
+    }
100
+
101
+    #[init]
102
+    fn init(c: init::Context) -> (Shared, Local, init::Monotonics) {
103
+        let mut resets = c.device.RESETS;
104
+
105
+        // The timer needs a 1MHz input from the watchdog.
106
+        let mut watchdog = Watchdog::new(c.device.WATCHDOG);
107
+        watchdog.enable_tick_generation((XOSC_CRYSTAL_FREQ / 1000000) as u8);
108
+
109
+        // We let an LED blink to show that the system is still alive.
110
+        let sio = Sio::new(c.device.SIO);
111
+        let pins = gpio::Pins::new(
112
+            c.device.IO_BANK0,
113
+            c.device.PADS_BANK0,
114
+            sio.gpio_bank0,
115
+            &mut resets,
116
+        );
117
+
118
+        // We select a 86 MHz system clock as that lets us generate a clean and precise 12.288 MHz
119
+        // master clock by dividing the system clock by 7.
120
+        // TODO: Select 123 MHz instead (0.1% deviation, whereas 86 MHz results in 0.02%
121
+        // deviation), or look for an even better frequency that is not a multiple of 1MHz.
122
+        let mut clocks = ClocksManager::new(c.device.CLOCKS);
123
+        let xosc = setup_xosc_blocking(c.device.XOSC, XOSC_CRYSTAL_FREQ.Hz())
124
+            .ok()
125
+            .unwrap();
126
+        let pll_sys = setup_pll_blocking(
127
+            c.device.PLL_SYS,
128
+            xosc.operating_frequency().into(),
129
+            PLL_SYS_86MHZ,
130
+            &mut clocks,
131
+            &mut resets,
132
+        )
133
+        .ok()
134
+        .unwrap();
135
+        let pll_usb = setup_pll_blocking(
136
+            c.device.PLL_USB,
137
+            xosc.operating_frequency().into(),
138
+            PLL_USB_48MHZ,
139
+            &mut clocks,
140
+            &mut resets,
141
+        )
142
+        .ok()
143
+        .unwrap();
144
+        let _clocks = clocks.init_default(&xosc, &pll_sys, &pll_usb).ok().unwrap();
145
+
146
+        let mut led = pins.gpio25.into_push_pull_output();
147
+        led.set_low().ok().unwrap();
148
+
149
+        /*// We need a UART to debug panics.
150
+        let mut uart = uart::UartPeripheral::<_, _>::enable(
151
+            c.device.UART0,
152
+            &mut resets,
153
+            uart::common_configs::_115200_8_N_1,
154
+            clocks.peripheral_clock.into(),
155
+        )
156
+        .unwrap();
157
+        let _tx_pin = pins.gpio16.into_mode::<FunctionUart>();
158
+        let _rx_pin = pins.gpio17.into_mode::<FunctionUart>();
159
+        writeln!(&mut uart, "Initializing...\r").unwrap();
160
+        interrupt::free(|cs| unsafe {
161
+            let panic_handler = PANIC_HANDLER.borrow(cs);
162
+            *panic_handler.get() = Some(PanicHandler::new(uart));
163
+            writeln!(super::uart(cs), "Panic handler installed.\r").unwrap();
164
+        });*/
165
+
166
+        // We need a timer so that we can determine the USB SOF frequency for asynchronous USB
167
+        // audio.
168
+        let timer = Timer::new(c.device.TIMER, &mut resets);
169
+
170
+        // We initialize the output audio buffers with some data. As the I2S input buffers are
171
+        // filled at the same rate as the I2S output buffers, this initial state ensures that the
172
+        // output buffers never run empty (note that the USB audio interface performs its own
173
+        // adaptive synchronization).
174
+        interrupt::free(|cs| {
175
+            let write = AUDIO_BUFFER.write();
176
+            for _ in 0..2 {
177
+                let mut packet = write.borrow_write(cs).unwrap();
178
+                let data = packet.as_mut();
179
+                for i in 0..data.len() {
180
+                    //data[i] = 0x80000000; // "0"
181
+                    data[i] = 0xa5a5aa55;
182
+                }
183
+                write.write_finished(packet, cs);
184
+            }
185
+        });
186
+
187
+        // Configure USB audio.
188
+        static mut USB_BUS: Option<UsbBusAllocator<UsbBus>> = None;
189
+        unsafe {
190
+            USB_BUS = Some(UsbBusAllocator::new(UsbBus::new(
191
+                c.device.USBCTRL_REGS,
192
+                c.device.USBCTRL_DPRAM,
193
+                clocks.usb_clock,
194
+                true,
195
+                &mut resets,
196
+            )))
197
+        };
198
+
199
+        let usb = Usb::init(
200
+            unsafe { USB_BUS.as_ref().unwrap() },
201
+            AUDIO_BUFFER.write(),
202
+            timer,
203
+        );
204
+
205
+        // Configure the PIO unit and start I2S.
206
+        let mut dma = c.device.DMA.split(&mut resets);
207
+        let (mut pio, sm0, _sm1, _sm2, _sm3) = c.device.PIO0.split(&mut resets);
208
+        dma.ch0.listen_irq0();
209
+        let i2s = I2sMaster::init(
210
+            &mut pio,
211
+            sm0,
212
+            dma.ch0,
213
+            AUDIO_BUFFER.read(),
214
+            pins.gpio0.into_mode(),
215
+            pins.gpio1.into_mode(),
216
+            pins.gpio6.into_mode(),
217
+            pins.gpio3.into_mode(),
218
+        );
219
+
220
+        // A speaker amplifier (TAS5760) is connected to I2S - we need to configure it via I2C.
221
+        // TODO
222
+        // TODO: I2C stuff should happen in a low-priority task as we do not want to make data
223
+        // streaming to I2S wait.
224
+
225
+        let systick = c.core.SYST;
226
+        let mono = Systick::new(systick, 86_000_000);
227
+        blink::spawn().ok().unwrap();
228
+        (Shared {}, Local { led, i2s, usb }, init::Monotonics(mono))
229
+    }
230
+
231
+    #[task(shared = [], local = [led, state: bool = false])]
232
+    fn blink(ctx: blink::Context) {
233
+        blink::spawn_after(Milliseconds(500_u32)).ok().unwrap();
234
+        *ctx.local.state = !*ctx.local.state;
235
+        if *ctx.local.state {
236
+            ctx.local.led.set_high().ok().unwrap();
237
+        } else {
238
+            ctx.local.led.set_low().ok().unwrap();
239
+        }
240
+    }
241
+
242
+    /*/// DMA interrupt handler.
243
+    ///
244
+    /// DMA interrupts need to have highest priority as we need to quickly restart the DMA transfer
245
+    /// to ensure that the PIO FIFO always contains data.
246
+    #[task(binds = DMA_IRQ_0, priority = 3, local = [i2s])]
247
+    fn dma(ctx: dma::Context) {
248
+        ctx.local.i2s.next_packet();
249
+        process_buffers::spawn().unwrap();
250
+    }
251
+
252
+    #[task]
253
+    fn process_buffers(_: process_buffers::Context) {
254
+        interrupt::free(|cs| {
255
+            // If we have run out of data from USB, insert a packet filled with zeros.
256
+            // TODO
257
+
258
+            let write = AUDIO_BUFFER.write();
259
+            let mut packet = write.borrow_write(cs).unwrap();
260
+            let data = packet.as_mut();
261
+            for i in 0..data.len() {
262
+                //data[i] = 0x80000000; // "0"
263
+                data[i] = 0xa5a5aa55;
264
+            }
265
+            write.write_finished(packet, cs);
266
+        });
267
+    }*/
268
+
269
+    /// USB interrupt handler.
270
+    ///
271
+    /// USB interrupts have a high priority (albeit not as high as DMA) so that we can precisely
272
+    /// measure the SOF frequency relative to our system clock. The frequency ratio is required for
273
+    /// asynchronous USB audio.
274
+    #[task(binds = USBCTRL_IRQ, priority = 2, local = [usb])]
275
+    fn usb(ctx: usb::Context) {
276
+        ctx.local.usb.poll();
277
+    }
278
+}

+ 66
- 0
src/usb.rs Voir le fichier

@@ -0,0 +1,66 @@
1
+use crate::audio_buffer::{WriteBuffer, WritePacket};
2
+use crate::usb_audio::UsbAudioClass;
3
+use rp2040_hal::pac::USBCTRL_REGS;
4
+use rp2040_hal::timer::Timer;
5
+use rp2040_hal::usb::UsbBus;
6
+use usb_device::{class_prelude::*, prelude::*};
7
+
8
+pub struct Usb {
9
+    usb_audio: UsbAudioClass<'static, UsbBus, WritePacket<4>>,
10
+    usb_dev: UsbDevice<'static, UsbBus>,
11
+    in_buffer: WriteBuffer<4>,
12
+
13
+    timer: Timer,
14
+}
15
+
16
+impl Usb {
17
+    pub fn init(
18
+        bus: &'static UsbBusAllocator<UsbBus>,
19
+        in_buffer: WriteBuffer<4>,
20
+        timer: Timer,
21
+    ) -> Usb {
22
+        let usb_audio = UsbAudioClass::new(bus);
23
+
24
+        // This PID/VID combination is selected from the pid.codes PID space and only intended for
25
+        // software development. It is not universally unique and should not be used outside of
26
+        // test environments!
27
+        let usb_dev = UsbDeviceBuilder::new(bus, UsbVidPid(0x1209, 0x000d))
28
+            .manufacturer("TEST")
29
+            .product("USB audio test")
30
+            .serial_number("TEST")
31
+            .build();
32
+
33
+        // TODO
34
+        Usb {
35
+            usb_audio,
36
+            usb_dev,
37
+            in_buffer,
38
+            timer,
39
+        }
40
+    }
41
+
42
+    pub fn poll(&mut self) {
43
+        // Safety: The read access does not have any side effect, and this function is the only one
44
+        // using the USB peripheral at this time.
45
+        let sof = unsafe { (&*USBCTRL_REGS::ptr()).intr.read().dev_sof().bit_is_set() };
46
+        if sof {
47
+            // Clear the SOF IRQ.
48
+            unsafe { (&*USBCTRL_REGS::ptr()).sof_rd.read() };
49
+
50
+            // Calculate the frequency difference (frames versus 1kHz derived from the system
51
+            // clock) and update the synchronization data for the USB audio class accordingly.
52
+            // Also, add a correction factor if the buffers are running low or high.
53
+            // TODO
54
+
55
+            /*let mut buffer = [0u8; 64];
56
+            for i in 0..64 {
57
+                buffer[i] = i as u8 * 4;
58
+            }
59
+            let _err = self.usb_audio.write_packet(&buffer);*/
60
+        }
61
+        if self.usb_dev.poll(&mut [&mut self.usb_audio]) {
62
+            // If we received audio data, move it into the buffer.
63
+            // TODO
64
+        }
65
+    }
66
+}

+ 576
- 0
src/usb_audio.rs Voir le fichier

@@ -0,0 +1,576 @@
1
+//! USB audio class for asynchronous, bidirectional (microphone + speakers) 48kHz audio.
2
+//!
3
+//! See "Universal Serial Bus Device Class Definition for Audio Devices", release 1.0 (March 18,
4
+//! 1998) for a specification of the USB audio class.
5
+use cortex_m::interrupt;
6
+use usb_device::class_prelude::*;
7
+use usb_device::endpoint::{IsochronousSynchronizationType, IsochronousUsageType};
8
+use usb_device::Result;
9
+
10
+const DEVICE_CLASS_AUDIO: u8 = 0x01;
11
+const AUDIO_SUBCLASS_CONTROL: u8 = 0x01;
12
+const AUDIO_SUBCLASS_STREAMING: u8 = 0x02;
13
+const AUDIO_PROTOCOL_NONE: u8 = 0x00;
14
+
15
+const AUDIO_INTERFACE_DESC_TYPE: u8 = 0x24;
16
+const AUDIO_ENDPOINT_DESC_TYPE: u8 = 0x25;
17
+
18
+const AUDIO_CONTROL_HEADER: u8 = 0x01;
19
+const AUDIO_CONTROL_INPUT_TERMINAL: u8 = 0x02;
20
+const AUDIO_CONTROL_OUTPUT_TERMINAL: u8 = 0x03;
21
+const AUDIO_CONTROL_FEATURE_UNIT: u8 = 0x06;
22
+
23
+const AUDIO_STREAMING_GENERAL: u8 = 0x01;
24
+const AUDIO_STREAMING_FORMAT_TYPE: u8 = 0x02;
25
+
26
+const AUDIO_ENDPOINT_GENERAL: u8 = 0x01;
27
+
28
+// TODO: Update bDelay.
29
+
30
+pub struct UsbAudioClass<'a, B: UsbBus, OUTBUF: AsRef<[u32]>> {
31
+    audio_control: InterfaceNumber,
32
+    audio_streaming_inactive: InterfaceNumber,
33
+    audio_streaming: InterfaceNumber,
34
+    audio_out: EndpointOut<'a, B>,
35
+    //audio_out_sync: EndpointIn<'a, B>,
36
+    //audio_in: EndpointIn<'a, B>,
37
+    samples_per_frame: [u8; 3],
38
+
39
+    audio_out_buf: Option<OUTBUF>,
40
+}
41
+
42
+impl<'a, B: UsbBus, OUTBUF: AsRef<[u32]>> UsbAudioClass<'a, B, OUTBUF> {
43
+    pub fn new(bus_alloc: &'a UsbBusAllocator<B>) -> Self {
44
+        UsbAudioClass {
45
+            audio_control: bus_alloc.interface(),
46
+            audio_streaming_inactive: bus_alloc.interface(),
47
+            audio_streaming: bus_alloc.interface(),
48
+            // 48kHz * 2 * 16bit = 192B per packet. We allocate a bit more in case the device clock
49
+            // is faster than the host clock.
50
+            /*audio_out: bus_alloc.isochronous(
51
+                IsochronousSynchronizationType::Asynchronous,
52
+                IsochronousUsageType::Data,
53
+                256,
54
+                1,
55
+            ),*/
56
+            audio_out: bus_alloc.isochronous(
57
+                IsochronousSynchronizationType::Synchronous,
58
+                IsochronousUsageType::Data,
59
+                256,
60
+                1,
61
+            ),
62
+            /*audio_out_sync: bus_alloc.isochronous(
63
+                IsochronousSynchronizationType::Asynchronous,
64
+                IsochronousUsageType::Feedback,
65
+                3,
66
+                1,
67
+            ),*/
68
+            /*audio_in: bus_alloc.isochronous(
69
+                IsochronousSynchronizationType::Asynchronous,
70
+                IsochronousUsageType::Data,
71
+                256,
72
+                1,
73
+            ),*/
74
+            samples_per_frame: [48 >> 2, 48 << 6, 0], // 48 kHz
75
+            audio_out_buf: None,
76
+        }
77
+    }
78
+
79
+    // TODO: Functions to read and write data?
80
+}
81
+
82
+impl<B: UsbBus, OUTBUF: AsRef<[u32]>> UsbClass<B> for UsbAudioClass<'_, B, OUTBUF> {
83
+    fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
84
+        /*interrupt::free(|cs| {
85
+            writeln!(crate::uart(cs), "A\r").unwrap();
86
+        });*/
87
+        writer.iad(
88
+            self.audio_control,
89
+            2,   // Two interfaces (control + streaming).
90
+            0x0, // Each interface specifies its own class.
91
+            0x0, // Each interface specifies its own subclass.
92
+            0x0, // No class-specific protocols for this device.
93
+        )?;
94
+
95
+        // Audio control interface.
96
+        writer.interface(
97
+            self.audio_control,
98
+            DEVICE_CLASS_AUDIO,
99
+            AUDIO_SUBCLASS_CONTROL,
100
+            AUDIO_PROTOCOL_NONE,
101
+        )?;
102
+
103
+        writer.write(
104
+            AUDIO_INTERFACE_DESC_TYPE,
105
+            &[
106
+                AUDIO_CONTROL_HEADER,        // bDescriptorSubtype
107
+                0x00,                        // bcdADC
108
+                0x01,                        //
109
+                37,                          // wTotalLength
110
+                0,                           //
111
+                0x01,                        // bInCollection
112
+                self.audio_streaming.into(), // baInterfaceNr
113
+            ],
114
+        )?;
115
+
116
+        // Input terminal (USB streaming).
117
+        writer.write(
118
+            AUDIO_INTERFACE_DESC_TYPE,
119
+            &[
120
+                AUDIO_CONTROL_INPUT_TERMINAL, // bDescriptorSubtype
121
+                0x01,                         // bTerminalID
122
+                0x01,                         // wTerminalType
123
+                0x01,                         //
124
+                0x00,                         // bAssocTerminal
125
+                0x02,                         // bNrChannels
126
+                0x03,                         // wChannelConfig
127
+                0x00,                         //
128
+                0x00,                         // iChannelNames
129
+                0x00,                         // iTerminal
130
+            ],
131
+        )?;
132
+
133
+        // Feature unit (volume and mute).
134
+        writer.write(
135
+            AUDIO_INTERFACE_DESC_TYPE,
136
+            &[
137
+                AUDIO_CONTROL_FEATURE_UNIT, // bDescriptorSubtype
138
+                0x02,                       // bUnitID
139
+                0x01,                       // bSourceID
140
+                0x01,                       // bControlSize
141
+                0x03,                       // bmaControls(0)
142
+                0x00,                       // iFeature
143
+            ],
144
+        )?;
145
+
146
+        // Output terminal (speaker).
147
+        writer.write(
148
+            AUDIO_INTERFACE_DESC_TYPE,
149
+            &[
150
+                AUDIO_CONTROL_OUTPUT_TERMINAL, // bDescriptorSubtype
151
+                0x03,                          // bTerminalID
152
+                0x01,                          // wTerminalType
153
+                0x03,                          //
154
+                0x00,                          // bAssocTerminal
155
+                0x02,                          // bSourceID
156
+                0x00,                          // iTerminal
157
+            ],
158
+        )?;
159
+
160
+        // Audio streaming interface (zero-bandwidth).
161
+        writer.interface(
162
+            self.audio_streaming_inactive,
163
+            DEVICE_CLASS_AUDIO,
164
+            AUDIO_SUBCLASS_STREAMING,
165
+            AUDIO_PROTOCOL_NONE,
166
+        )?;
167
+
168
+        // Audio streaming interface (operational).
169
+        writer.interface_alt(
170
+            self.audio_streaming,
171
+            1,
172
+            DEVICE_CLASS_AUDIO,
173
+            AUDIO_SUBCLASS_STREAMING,
174
+            AUDIO_PROTOCOL_NONE,
175
+            None,
176
+        )?;
177
+
178
+        writer.write(
179
+            AUDIO_INTERFACE_DESC_TYPE,
180
+            &[
181
+                AUDIO_STREAMING_GENERAL, // bDescriptorSubtype
182
+                0x01,                    // bTerminalLink
183
+                0x03,                    // bDelay
184
+                0x01,                    // wFormatTag
185
+                0x00,                    //
186
+            ],
187
+        )?;
188
+
189
+        writer.write(
190
+            AUDIO_INTERFACE_DESC_TYPE,
191
+            &[
192
+                AUDIO_STREAMING_FORMAT_TYPE, // bDescriptorSubtype
193
+                0x01,                        // bFormatType
194
+                0x02,                        // bNrChannels
195
+                0x02,                        // bSubframeSize
196
+                0x10,                        // bBitResolution
197
+                0x01,                        // bSamFreqType
198
+            ],
199
+        )?;
200
+
201
+        /*writer.endpoint_ex(&self.audio_out, |data| {
202
+            // TODO: Faster refresh
203
+            data[0] = 0x09; // bRefresh
204
+            data[1] = self.audio_out_sync.address().into(); // bSynchAddress
205
+            Ok(2)
206
+        })?;*/
207
+        writer.endpoint(&self.audio_out)?;
208
+
209
+        writer.write(
210
+            AUDIO_ENDPOINT_DESC_TYPE,
211
+            &[
212
+                AUDIO_ENDPOINT_GENERAL, // bDescriptorSubtype
213
+                0x00,                   // bmAttributes
214
+                0x01,                   // bLockDelayUnits
215
+                0x00,                   // wLockDelay
216
+                0x00,
217
+            ],
218
+        )?;
219
+
220
+        /*writer.endpoint_ex(&self.audio_out_sync, |data| {
221
+            data[0] = 0x00; // bRefresh
222
+            data[1] = 0x00; // bSynchAddress
223
+            Ok(2)
224
+        })?;*/
225
+
226
+        /*interrupt::free(|cs| {
227
+            writeln!(crate::uart(cs), "B\r").unwrap();
228
+        });*/
229
+
230
+        Ok(())
231
+    }
232
+
233
+    fn get_string(&self, _index: StringIndex, _lang_id: u16) -> Option<&str> {
234
+        // TODO
235
+        None
236
+    }
237
+
238
+    fn reset(&mut self) {
239
+        // Start sending synchronization data.
240
+        //self.endpoint_in_complete(self.audio_out_sync.address());
241
+    }
242
+
243
+    fn control_out(&mut self, xfer: ControlOut<B>) {
244
+        let req = xfer.request();
245
+
246
+        if !(req.request_type == control::RequestType::Class
247
+            && req.recipient == control::Recipient::Interface
248
+            && req.index == u8::from(self.audio_control) as u16)
249
+        {
250
+            return;
251
+        }
252
+
253
+        match req.request {
254
+            // TODO
255
+            _ => {
256
+                xfer.reject().ok();
257
+            }
258
+        };
259
+    }
260
+
261
+    fn control_in(&mut self, xfer: ControlIn<B>) {
262
+        let req = xfer.request();
263
+
264
+        if !(req.request_type == control::RequestType::Class
265
+            && req.recipient == control::Recipient::Interface
266
+            && req.index == u8::from(self.audio_control) as u16)
267
+        {
268
+            return;
269
+        }
270
+
271
+        match req.request {
272
+            // TODO
273
+            _ => {
274
+                xfer.reject().ok();
275
+            }
276
+        };
277
+    }
278
+
279
+    /*fn endpoint_out(&mut self, addr: EndpointAddress) {
280
+        if addr == self.audio_out.address() {
281
+            if self.audio_out_buf.is_some() {
282
+                // TODO: Write data into buffer, move buffer somewhere else.
283
+            } else {
284
+                // TODO
285
+            }
286
+            // TODO: Process incoming audio data.
287
+            let mut buffer = [0u8; 256];
288
+            self.audio_out.read(&mut buffer);
289
+        }
290
+    }
291
+
292
+    fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
293
+        if addr == self.audio_out_sync.address() {
294
+            // Immediately write the next sync value.
295
+            //self.audio_out_sync.write(&self.samples_per_frame).unwrap();
296
+        }
297
+    }*/
298
+}
299
+
300
+/*use usb_device::class_prelude::*;
301
+use usb_device::endpoint::{IsochronousSynchronizationType, IsochronousUsageType};
302
+use usb_device::Result;
303
+
304
+/// This should be used as `device_class` when building the `UsbDevice`.
305
+pub const USB_INTERFACE_CLASS_AUDIO: u8 = 0x01;
306
+
307
+const USB_AUDIO_SUBCLASS_UNDEFINED: u8 = 0x0;
308
+const USB_AUDIO_SUBCLASS_AUDIOCONTROL: u8 = 0x01;
309
+const USB_AUDIO_SUBCLASS_AUDIOSTREAMING: u8 = 0x02;
310
+const USB_AUDIO_SUBCLASS_MIDISTREAMING: u8 = 0x03;
311
+
312
+const USB_CLASS_CDC_DATA: u8 = 0x0a;
313
+const CDC_SUBCLASS_ACM: u8 = 0x02;
314
+const USB_AUDIO_PROTOCOL_NONE: u8 = 0x00;
315
+
316
+const CS_DEVICE: u8 = 0x21;
317
+const CS_CONFIGURATION: u8 = 0x22;
318
+const CS_STRING: u8 = 0x23;
319
+const CS_INTERFACE: u8 = 0x24;
320
+const CS_ENDPOINT: u8 = 0x25;
321
+
322
+const EP_GENERAL: u8 = 0x1;
323
+
324
+const AC_DESC_TYPE_HEADER: u8 = 0x1;
325
+const AC_DESC_TYPE_INPUT_TERMINAL: u8 = 0x2;
326
+const AC_DESC_TYPE_OUTPUT_TERMINAL: u8 = 0x3;
327
+const AC_DESC_TYPE_MIXER_UNIT: u8 = 0x4;
328
+const AC_DESC_TYPE_SELECTOR_UNIT: u8 = 0x5;
329
+const AC_DESC_TYPE_FEATURE_UNIT: u8 = 0x6;
330
+const AC_DESC_TYPE_PROCESSING_UNIT: u8 = 0x7;
331
+const AC_DESC_TYPE_EXTENSION_UNIT: u8 = 0x8;
332
+
333
+const AC_DESC_ST_GENERAL: u8 = 0x1;
334
+const AC_DESC_ST_FORMAT_TYPE: u8 = 0x2;
335
+const AC_DESC_ST_FORMAT_SPECIFIC: u8 = 0x3;
336
+
337
+const AUDIO_SUB_TYPE_HEADER: u8 = 0x01;
338
+const AUDIO_SUB_TYPE_INPUT_TERMINAL: u8 = 0x02;
339
+const AUDIO_SUB_TYPE_FEATURE_UNIT: u8 = 0x06;
340
+const AUDIO_SUB_TYPE_OUTPUT_TERMINAL: u8 = 0x03;
341
+const AUDIO_SUB_TYPE_AS_GENERAL: u8 = 0x01;
342
+const AUDIO_SUB_TYPE_FORMAT_TYPE: u8 = 0x02;
343
+
344
+pub struct UsbAudioClass<'a, B: UsbBus> {
345
+    audio_control_if: InterfaceNumber,
346
+    audio_stream: InterfaceNumber,
347
+    alt_audio_stream: InterfaceNumber,
348
+    audio_in_ep: EndpointIn<'a, B>,
349
+}
350
+
351
+impl<B: UsbBus> UsbAudioClass<'_, B> {
352
+    /// Creates a new UsbAudioClass with the provided UsbBus and max_packet_size in bytes. For
353
+    /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
354
+    pub fn new(alloc: &UsbBusAllocator<B>, max_packet_size: u16) -> UsbAudioClass<'_, B> {
355
+        UsbAudioClass {
356
+            audio_control_if: alloc.interface(),
357
+            audio_stream: alloc.interface(),
358
+            alt_audio_stream: alloc.interface(),
359
+            audio_in_ep: alloc.isochronous(
360
+                IsochronousSynchronizationType::Synchronous,
361
+                IsochronousUsageType::Data,
362
+                max_packet_size,
363
+                4,
364
+            ),
365
+        }
366
+    }
367
+
368
+    /// Gets the maximum packet size in bytes.
369
+    pub fn max_packet_size(&self) -> u16 {
370
+        self.audio_in_ep.max_packet_size()
371
+    }
372
+
373
+    /// Writes a single packet into the IN endpoint.
374
+    pub fn write_packet(&mut self, data: &[u8]) -> Result<usize> {
375
+        //defmt::info!("write_packet");
376
+        self.audio_in_ep.write(data)
377
+    }
378
+
379
+    /// Gets the address of the IN endpoint.
380
+    pub(crate) fn write_ep_address(&self) -> EndpointAddress {
381
+        self.audio_in_ep.address()
382
+    }
383
+}
384
+
385
+impl<B: UsbBus> UsbClass<B> for UsbAudioClass<'_, B> {
386
+    fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
387
+        writer.iad(
388
+            self.audio_control_if,
389
+            3,
390
+            0x0, // device defined at interface level
391
+            0x0, // subclass unused
392
+            USB_AUDIO_PROTOCOL_NONE,
393
+        )?;
394
+
395
+        writer.interface(
396
+            self.audio_control_if,
397
+            USB_INTERFACE_CLASS_AUDIO,
398
+            USB_AUDIO_SUBCLASS_AUDIOCONTROL,
399
+            USB_AUDIO_PROTOCOL_NONE,
400
+        )?;
401
+
402
+        writer.write(
403
+            CS_INTERFACE,
404
+            &[
405
+                AUDIO_SUB_TYPE_HEADER, // bDescriptorSubtype
406
+                0x00,
407
+                0x1, // bcdADC (1.0),
408
+                43,
409
+                0,    // wTotalLength (Compute this!)
410
+                0x01, // bInCollection (1 streaming interface)
411
+                0x01, // baInterfaceNr (Interface 1 is stream)
412
+            ],
413
+        )?;
414
+
415
+        writer.write(
416
+            CS_INTERFACE,
417
+            &[
418
+                AUDIO_SUB_TYPE_INPUT_TERMINAL, // bDescriptorSubtype
419
+                0x01,                          // bTerminalID 1,
420
+                0x10,
421
+                0x07, // wTerminalType (radio receiver)
422
+                0x00, // bAssocTerminal (none)
423
+                0x02, // bNrChannels - 2 for I and Q
424
+                0x03,
425
+                0x00, // wChannelConfig (left, right)
426
+                0x00, // iChannelNames (none)
427
+                0x00, // iTerminal (none)
428
+            ],
429
+        )?;
430
+
431
+        writer.write(
432
+            CS_INTERFACE,
433
+            &[
434
+                AUDIO_SUB_TYPE_FEATURE_UNIT, // bDescriptorSubtype
435
+                0x02,                        // bUnitID,
436
+                0x01,                        // bSourceID (input terminal 1)
437
+                0x02,                        // bControlSize (2 bytes)
438
+                0x01,
439
+                0x00, // Master controls
440
+                0x00,
441
+                0x00, // Channel 0 controls
442
+                0x00,
443
+                0x00, // Channel 1 controls
444
+                0x00, // iFeature (none)
445
+            ],
446
+        )?;
447
+
448
+        writer.write(
449
+            CS_INTERFACE,
450
+            &[
451
+                AUDIO_SUB_TYPE_OUTPUT_TERMINAL, // bDescriptorSubtype
452
+                0x03,                           // bTerminalID,
453
+                0x01,
454
+                0x01, // wTerminalType (USB Streaming)
455
+                0x00, // bAssocTerminal (none)
456
+                0x02, // bSourceID (feature unit 2)
457
+                0x00, // iTerminal (none)
458
+            ],
459
+        )?;
460
+
461
+        writer.interface(
462
+            self.audio_stream,
463
+            USB_INTERFACE_CLASS_AUDIO,
464
+            USB_AUDIO_SUBCLASS_AUDIOSTREAMING,
465
+            USB_AUDIO_PROTOCOL_NONE,
466
+        )?;
467
+
468
+        //alternate audio stream
469
+        /*writer.interface_alt(
470
+            self.alt_audio_stream,
471
+            1,
472
+            USB_INTERFACE_CLASS_AUDIO,
473
+            USB_AUDIO_SUBCLASS_AUDIOSTREAMING,
474
+            USB_AUDIO_PROTOCOL_NONE,
475
+            None,
476
+        )?;*/
477
+
478
+        // Audio Stream Audio Class Descriptor
479
+        writer.write(
480
+            CS_INTERFACE,
481
+            &[
482
+                AUDIO_SUB_TYPE_AS_GENERAL, // bDescriptorSubtype
483
+                0x03,                      // bTerminalID,
484
+                0x00,                      // bDelay
485
+                0x01,
486
+                0x00, // wFormatTag (PCM Format)
487
+            ],
488
+        )?;
489
+
490
+        // Format Type Audio Descriptor
491
+        writer.write(
492
+            CS_INTERFACE,
493
+            &[
494
+                AUDIO_SUB_TYPE_FORMAT_TYPE, // bDescriptorSubtype
495
+                0x01,                       // bFormatType (TYPE_I)
496
+                0x02,                       // bNrChannels (2)
497
+                0x02,                       // bSubFrameSize (2)
498
+                0x10,                       // bBitResolution (16 bits)
499
+                0x01,                       // bSamFreqType (1 sample frequency)
500
+                0x80,                       // 8*2 = 16 KHz byte 0
501
+                0x3E,                       // 8*2 = 16 KHz byte 1
502
+                0x00,                       // 8*2 = 16 KHz byte 2
503
+            ],
504
+        )?;
505
+
506
+        // TODO: Set the necessary flags for Isochronous
507
+        writer.endpoint(&self.audio_in_ep)?;
508
+
509
+        // Isochronous endpoint Audio Class descriptor
510
+        writer.write(
511
+            CS_ENDPOINT,
512
+            &[
513
+                EP_GENERAL, // bDescriptorSubtype
514
+                0x00,       // bmAttributes (none)
515
+                0x02,       // bLockDelayUnits (PCM Samples)
516
+                0x00, 0x00, // wLockDelay (0)  - should be zero for asynchronous
517
+            ],
518
+        )?;
519
+        Ok(())
520
+    }
521
+
522
+    fn reset(&mut self) {}
523
+
524
+    fn control_in(&mut self, xfer: ControlIn<B>) {
525
+        let req = xfer.request();
526
+
527
+        if !(req.request_type == control::RequestType::Class
528
+            && req.recipient == control::Recipient::Interface
529
+            && req.index == u8::from(self.audio_control_if) as u16)
530
+        {
531
+            return;
532
+        }
533
+
534
+        //defmt::info!("control_in - req : {:?}", req.request);
535
+        match req.request {
536
+            /*
537
+            REQ_GET_LINE_CODING if req.length == 7 => {
538
+                xfer.accept(|data| {
539
+                    data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
540
+                    data[4] = self.line_coding.stop_bits as u8;
541
+                    data[5] = self.line_coding.parity_type as u8;
542
+                    data[6] = self.line_coding.data_bits;
543
+
544
+                    Ok(7)
545
+                }).ok();
546
+            },
547
+            */
548
+            _ => {
549
+                xfer.reject().ok();
550
+            }
551
+        }
552
+    }
553
+
554
+    fn control_out(&mut self, xfer: ControlOut<B>) {
555
+        let req = xfer.request();
556
+
557
+        if !(req.request_type == control::RequestType::Class
558
+            && req.recipient == control::Recipient::Interface
559
+            && req.index == u8::from(self.audio_control_if) as u16)
560
+        {
561
+            return;
562
+        }
563
+
564
+        match req.request {
565
+            /*
566
+            REQ_SEND_ENCAPSULATED_COMMAND => {
567
+                // We don't actually support encapsulated commands but pretend we do for standards
568
+                // compatibility.
569
+                xfer.accept().ok();
570
+            },*/
571
+            _ => {
572
+                xfer.reject().ok();
573
+            }
574
+        };
575
+    }
576
+}*/

Loading…
Annuler
Enregistrer