add stopping state, handle dma drain, improve cli readability (state) by moving state to shared

This commit is contained in:
2026-08-25 12:51:52 -07:00
parent 31edf91933
commit bdd946ad11
7 changed files with 271 additions and 121 deletions
+4
View File
@@ -3,9 +3,13 @@ name = "shared"
edition = "2024"
[features]
std = []
serde = ["dep:serde"]
[dependencies]
bytemuck = { version = "1.25.0", features = ["derive"] }
defmt = "1.1.1"
deku = { version = "0.20.3", default-features = false }
num_enum = { version = "0.7.6", default-features = false }
serde = { version = "1.0.228", optional = true, features = ["derive"] }
usbd-hid = { version = "0.10.0" }
+124 -2
View File
@@ -1,14 +1,120 @@
#![no_std]
use bytemuck::NoUninit;
use num_enum::TryFromPrimitive;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, NoUninit, Eq, PartialEq, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum AudioState {
/// Knowingly stopped, ie. AltSetting=0. DAC muted, I2S disabled.
///
/// AltSetting = 1 -> ARMED
Stopped = 0,
/// Waiting for data. DAC muted, I2S running sending 0s (FIFO not serviced).
///
/// USB OUT data packet -> ARMED
/// AltSetting = 0 -> STOPPED
Armed = 1,
/// Filling the buffer before playback starts. Feedback does not run,
/// playout does not start draining the queue. Gets us better feedback
/// behaviour and a full buffer without a feedback rate spike at startup.
///
/// queue reaches <QUEUE_RUNNING_UP> -> RUNNING
/// AltSetting = 0 -> STOPPED
///
Prefill = 2,
/// Normal running state. Start servicing FIFO and begin playing out from the buffer.
///
/// queue reaches <QUEUE_RUNNING_DOWN> -> DRAINING
/// AltSetting = 0 -> DRAINING
Running = 3,
/// The queue is low. We will continue playout.
///
/// queue is empty && altSetting 1 -> NODATA
/// queue is empty && altSetting 0 -> STOPPED
/// queue reaches <QUEUE_RUNNING_UP> && altSetting 1 -> RUNNING
LowData = 4,
/// There is no data in the queue. We will count underflows for a while, send 0s, and hope the host comes back.
///
/// countdown reaches DATA_TIMEOUT -> STOPPED
/// AltSetting = 0 -> STOPPED
NoData = 5,
/// The host has asked us to stop (altSetting 0), but we need to play out the remaining buffer
///
/// queue is empty -> STOPPED
Stopping = 6,
}
impl Default for AudioState {
fn default() -> Self {
AudioState::Stopped
}
}
impl defmt::Format for AudioState {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(
fmt,
"{}",
match self {
Self::Stopped => "Stopped",
Self::Armed => "Armed",
Self::Prefill => "Prefill",
Self::Running => "Running",
Self::LowData => "Draining",
Self::NoData => "NoData",
Self::Stopping => "Stopping",
}
)
}
}
impl core::fmt::Display for AudioState {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{}",
match self {
Self::Stopped => "Stopped",
Self::Armed => "Armed",
Self::Prefill => "Prefill",
Self::Running => "Running",
Self::LowData => "Draining",
Self::NoData => "NoData",
Self::Stopping => "Stopping",
}
)
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AudioTelemetrySnapshot {
pub state: AudioState,
pub average_buffer_fill: u16,
pub frame_count: u32,
pub dac_underflow_count: u16,
pub usb_underflow_count: u16,
pub dac_overflow_count: u16,
pub p: i32,
pub i: i32,
pub fb: i32,
}
pub mod hid {
use deku::DekuRead;
use usbd_hid::descriptor::generator_prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use usbd_hid::descriptor::generator_prelude::*;
use crate::{AudioState, AudioTelemetrySnapshot};
#[derive(DekuRead)]
#[deku(endian = "little")]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = VENDOR_DEFINED_START, usage = 0x01, ) = {
state=input;
@@ -37,4 +143,20 @@ pub mod hid {
pub fb: i32,
pub integrator: i32,
}
impl From<AudioTelemetryReport> for AudioTelemetrySnapshot {
fn from(value: AudioTelemetryReport) -> Self {
AudioTelemetrySnapshot {
state: AudioState::try_from(value.state).expect("Invalid AudioState"),
average_buffer_fill: value.average_buffer_fill,
frame_count: i32::cast_unsigned(value.frame_count), // on firmware side is usize == u32
dac_underflow_count: value.dac_underflow_count,
usb_underflow_count: value.usb_underflow_count,
dac_overflow_count: value.dac_overflow_count,
p: value.p,
i: value.i,
fb: value.fb,
}
}
}
}