⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/audio/
usb_audio.rs

1//! USB Audio Class (UAC) and HDMI Audio drivers
2//!
3//! Provides USB Audio Class 1.0/2.0 device support and HDMI audio output:
4//!
5//! ## USB Audio Class (UAC)
6//! - UAC 1.0 and 2.0 descriptor parsing (Audio Control, Audio Streaming)
7//! - Terminal types: Input Terminal, Output Terminal, Feature Unit, Mixer Unit
8//! - Audio format descriptors: PCM, sample rates (8000-192000), bit depths
9//!   (16/24/32)
10//! - Isochronous endpoint management (adaptive, synchronous, asynchronous)
11//! - Sample rate control (SET_CUR/GET_CUR)
12//! - Volume/mute control via Feature Unit (dB scaling, 8.8 fixed-point)
13//!
14//! ## HDMI Audio
15//! - HDMI audio infoframe (CEA-861) construction
16//! - Channel allocation (2ch stereo, 5.1, 7.1)
17//! - Audio Clock Regeneration (N/CTS values)
18//! - ELD (EDID-Like Data) parsing for sink capabilities
19//! - Integration with GPU driver (HDA codec over HDMI)
20//!
21//! All arithmetic uses integer/fixed-point math only (no FPU).
22
23#![allow(dead_code)]
24
25use alloc::{string::String, vec::Vec};
26
27use crate::error::KernelError;
28
29// ============================================================================
30// USB Audio Class Constants
31// ============================================================================
32
33/// USB Audio device class code
34pub const USB_CLASS_AUDIO: u8 = 0x01;
35
36/// Audio subclass codes
37pub const USB_SUBCLASS_AUDIO_CONTROL: u8 = 0x01;
38pub const USB_SUBCLASS_AUDIO_STREAMING: u8 = 0x02;
39pub const USB_SUBCLASS_MIDI_STREAMING: u8 = 0x03;
40
41/// Audio protocol codes
42pub const UAC_PROTOCOL_NONE: u8 = 0x00;
43pub const UAC_PROTOCOL_IP_VERSION_02_00: u8 = 0x20;
44
45/// Class-specific descriptor types
46pub const CS_INTERFACE: u8 = 0x24;
47pub const CS_ENDPOINT: u8 = 0x25;
48
49// --- Audio Control Interface descriptor subtypes ---
50
51/// AC interface header
52pub const UAC_AC_HEADER: u8 = 0x01;
53/// Input terminal descriptor
54pub const UAC_INPUT_TERMINAL: u8 = 0x02;
55/// Output terminal descriptor
56pub const UAC_OUTPUT_TERMINAL: u8 = 0x03;
57/// Mixer unit descriptor
58pub const UAC_MIXER_UNIT: u8 = 0x04;
59/// Selector unit descriptor
60pub const UAC_SELECTOR_UNIT: u8 = 0x05;
61/// Feature unit descriptor
62pub const UAC_FEATURE_UNIT: u8 = 0x06;
63/// Processing unit descriptor (UAC 1.0)
64pub const UAC_PROCESSING_UNIT: u8 = 0x07;
65/// Extension unit descriptor (UAC 1.0)
66pub const UAC_EXTENSION_UNIT: u8 = 0x08;
67/// Clock source (UAC 2.0)
68pub const UAC2_CLOCK_SOURCE: u8 = 0x0A;
69/// Clock selector (UAC 2.0)
70pub const UAC2_CLOCK_SELECTOR: u8 = 0x0B;
71/// Clock multiplier (UAC 2.0)
72pub const UAC2_CLOCK_MULTIPLIER: u8 = 0x0C;
73
74// --- Audio Streaming Interface descriptor subtypes ---
75
76/// AS interface general descriptor
77pub const UAC_AS_GENERAL: u8 = 0x01;
78/// AS format type descriptor
79pub const UAC_AS_FORMAT_TYPE: u8 = 0x02;
80
81// --- Terminal types (USB Audio Terminal Types) ---
82
83/// USB streaming terminal (host connection)
84pub const UAC_TERMINAL_USB_STREAMING: u16 = 0x0101;
85/// Generic speaker output
86pub const UAC_TERMINAL_SPEAKER: u16 = 0x0301;
87/// Headphones output
88pub const UAC_TERMINAL_HEADPHONES: u16 = 0x0302;
89/// Desktop speaker
90pub const UAC_TERMINAL_DESKTOP_SPEAKER: u16 = 0x0304;
91/// Generic microphone input
92pub const UAC_TERMINAL_MICROPHONE: u16 = 0x0201;
93/// Desktop microphone
94pub const UAC_TERMINAL_DESKTOP_MIC: u16 = 0x0202;
95/// Headset microphone
96pub const UAC_TERMINAL_HEADSET_MIC: u16 = 0x0204;
97/// HDMI output terminal
98pub const UAC_TERMINAL_HDMI: u16 = 0x0605;
99/// S/PDIF digital output
100pub const UAC_TERMINAL_SPDIF: u16 = 0x0605;
101
102// --- Audio data format codes ---
103
104/// PCM format (uncompressed)
105pub const UAC_FORMAT_PCM: u16 = 0x0001;
106/// PCM8 format (8-bit unsigned)
107pub const UAC_FORMAT_PCM8: u16 = 0x0002;
108/// IEEE 754 float format
109pub const UAC_FORMAT_IEEE_FLOAT: u16 = 0x0003;
110/// A-Law format
111pub const UAC_FORMAT_ALAW: u16 = 0x0004;
112/// Mu-Law format
113pub const UAC_FORMAT_MULAW: u16 = 0x0005;
114
115// --- USB Audio control request codes ---
116
117pub const UAC_SET_CUR: u8 = 0x01;
118pub const UAC_GET_CUR: u8 = 0x81;
119pub const UAC_SET_MIN: u8 = 0x02;
120pub const UAC_GET_MIN: u8 = 0x82;
121pub const UAC_SET_MAX: u8 = 0x03;
122pub const UAC_GET_MAX: u8 = 0x83;
123pub const UAC_SET_RES: u8 = 0x04;
124pub const UAC_GET_RES: u8 = 0x84;
125
126// --- Feature unit control selectors ---
127
128pub const UAC_FU_MUTE_CONTROL: u8 = 0x01;
129pub const UAC_FU_VOLUME_CONTROL: u8 = 0x02;
130pub const UAC_FU_BASS_CONTROL: u8 = 0x03;
131pub const UAC_FU_TREBLE_CONTROL: u8 = 0x04;
132pub const UAC_FU_AUTOMATIC_GAIN: u8 = 0x07;
133
134// --- Isochronous endpoint sync types (bmAttributes bits 2-3) ---
135
136/// No synchronization
137pub const UAC_EP_SYNC_NONE: u8 = 0x00;
138/// Asynchronous: device sets its own clock
139pub const UAC_EP_SYNC_ASYNC: u8 = 0x04;
140/// Adaptive: device adapts to host clock
141pub const UAC_EP_SYNC_ADAPTIVE: u8 = 0x08;
142/// Synchronous: device uses SOF synchronization
143pub const UAC_EP_SYNC_SYNC: u8 = 0x0C;
144
145/// Maximum number of supported channels per stream
146const MAX_CHANNELS: usize = 8;
147
148/// Maximum number of supported sample rates per format
149const MAX_SAMPLE_RATES: usize = 16;
150
151/// Maximum number of audio units per device
152const MAX_UNITS: usize = 32;
153
154// ============================================================================
155// USB Audio Descriptor Types
156// ============================================================================
157
158/// UAC version supported by a device
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub enum UacVersion {
161    /// USB Audio Class 1.0
162    Uac10,
163    /// USB Audio Class 2.0
164    Uac20,
165}
166
167/// Isochronous endpoint synchronization type
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169pub enum IsoSyncType {
170    /// No synchronization
171    None,
172    /// Asynchronous: device provides its own clock
173    Asynchronous,
174    /// Adaptive: device adapts to host clock
175    Adaptive,
176    /// Synchronous: locked to SOF
177    Synchronous,
178}
179
180impl IsoSyncType {
181    /// Parse sync type from endpoint bmAttributes bits 2-3
182    pub(crate) fn from_attributes(attr: u8) -> Self {
183        match attr & 0x0C {
184            UAC_EP_SYNC_ASYNC => IsoSyncType::Asynchronous,
185            UAC_EP_SYNC_ADAPTIVE => IsoSyncType::Adaptive,
186            UAC_EP_SYNC_SYNC => IsoSyncType::Synchronous,
187            _ => IsoSyncType::None,
188        }
189    }
190
191    /// Convert to endpoint bmAttributes bits
192    pub(crate) fn to_attributes(self) -> u8 {
193        match self {
194            IsoSyncType::None => UAC_EP_SYNC_NONE,
195            IsoSyncType::Asynchronous => UAC_EP_SYNC_ASYNC,
196            IsoSyncType::Adaptive => UAC_EP_SYNC_ADAPTIVE,
197            IsoSyncType::Synchronous => UAC_EP_SYNC_SYNC,
198        }
199    }
200}
201
202/// Audio terminal type classification
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub enum TerminalDirection {
205    /// Input terminal (captures audio)
206    Input,
207    /// Output terminal (renders audio)
208    Output,
209}
210
211/// Input terminal descriptor
212#[derive(Debug, Clone)]
213pub struct InputTerminal {
214    /// Terminal ID (unique within the audio function)
215    pub terminal_id: u8,
216    /// Terminal type code (e.g., UAC_TERMINAL_MICROPHONE)
217    pub terminal_type: u16,
218    /// Associated output terminal ID (0 if none)
219    pub assoc_terminal: u8,
220    /// Number of logical output channels
221    pub nr_channels: u8,
222    /// Spatial location of channels (bitmask)
223    pub channel_config: u16,
224}
225
226/// Output terminal descriptor
227#[derive(Debug, Clone)]
228pub struct OutputTerminal {
229    /// Terminal ID
230    pub terminal_id: u8,
231    /// Terminal type code (e.g., UAC_TERMINAL_SPEAKER)
232    pub terminal_type: u16,
233    /// Associated input terminal ID (0 if none)
234    pub assoc_terminal: u8,
235    /// Source unit/terminal ID that feeds this output
236    pub source_id: u8,
237}
238
239/// Feature unit descriptor -- provides volume/mute/tone controls
240#[derive(Debug, Clone)]
241pub struct FeatureUnit {
242    /// Unit ID
243    pub unit_id: u8,
244    /// Source unit/terminal ID
245    pub source_id: u8,
246    /// Per-channel control bitmask (index 0 = master, 1..=N = channels)
247    /// Bit 0: Mute, Bit 1: Volume, Bit 2: Bass, etc.
248    pub controls: Vec<u32>,
249}
250
251impl FeatureUnit {
252    /// Check if mute control is available for a channel (0 = master)
253    pub(crate) fn has_mute(&self, channel: usize) -> bool {
254        self.controls
255            .get(channel)
256            .is_some_and(|c| c & (1 << 0) != 0)
257    }
258
259    /// Check if volume control is available for a channel (0 = master)
260    pub(crate) fn has_volume(&self, channel: usize) -> bool {
261        self.controls
262            .get(channel)
263            .is_some_and(|c| c & (1 << 1) != 0)
264    }
265}
266
267/// Mixer unit descriptor
268#[derive(Debug, Clone)]
269pub struct MixerUnit {
270    /// Unit ID
271    pub unit_id: u8,
272    /// Source IDs feeding this mixer
273    pub source_ids: Vec<u8>,
274    /// Number of output channels
275    pub nr_channels: u8,
276    /// Channel configuration bitmask
277    pub channel_config: u16,
278}
279
280/// Clock source descriptor (UAC 2.0)
281#[derive(Debug, Clone, Copy)]
282pub struct ClockSource {
283    /// Clock source ID
284    pub clock_id: u8,
285    /// Attributes: bit 0 = external, bit 1 = synced to SOF
286    pub attributes: u8,
287    /// Associated terminal ID
288    pub assoc_terminal: u8,
289}
290
291impl ClockSource {
292    /// Check if clock is external
293    pub(crate) fn is_external(&self) -> bool {
294        self.attributes & 0x01 != 0
295    }
296
297    /// Check if clock is synced to SOF
298    pub(crate) fn is_sof_synced(&self) -> bool {
299        self.attributes & 0x02 != 0
300    }
301}
302
303/// Audio unit -- variant type for all unit/terminal descriptors
304#[derive(Debug, Clone)]
305pub enum AudioUnit {
306    /// Input terminal
307    InputTerminal(InputTerminal),
308    /// Output terminal
309    OutputTerminal(OutputTerminal),
310    /// Feature unit (volume/mute)
311    FeatureUnit(FeatureUnit),
312    /// Mixer unit
313    MixerUnit(MixerUnit),
314    /// Clock source (UAC 2.0)
315    ClockSource(ClockSource),
316}
317
318impl AudioUnit {
319    /// Get the unit/terminal ID
320    pub(crate) fn id(&self) -> u8 {
321        match self {
322            AudioUnit::InputTerminal(t) => t.terminal_id,
323            AudioUnit::OutputTerminal(t) => t.terminal_id,
324            AudioUnit::FeatureUnit(u) => u.unit_id,
325            AudioUnit::MixerUnit(u) => u.unit_id,
326            AudioUnit::ClockSource(c) => c.clock_id,
327        }
328    }
329}
330
331// ============================================================================
332// Audio Format Descriptor
333// ============================================================================
334
335/// Supported sample rate entry
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337pub struct SampleRateRange {
338    /// Minimum sample rate in Hz
339    pub min: u32,
340    /// Maximum sample rate in Hz
341    pub max: u32,
342}
343
344impl SampleRateRange {
345    /// Create a discrete (single-value) sample rate
346    pub(crate) fn discrete(rate: u32) -> Self {
347        Self {
348            min: rate,
349            max: rate,
350        }
351    }
352
353    /// Create a continuous range
354    pub(crate) fn range(min: u32, max: u32) -> Self {
355        Self { min, max }
356    }
357
358    /// Check if a given rate falls within this range
359    pub(crate) fn contains(&self, rate: u32) -> bool {
360        rate >= self.min && rate <= self.max
361    }
362
363    /// Check if this is a discrete (single-value) rate
364    pub(crate) fn is_discrete(&self) -> bool {
365        self.min == self.max
366    }
367}
368
369/// Audio format type descriptor
370#[derive(Debug, Clone)]
371pub struct AudioFormatDescriptor {
372    /// Format tag (e.g., UAC_FORMAT_PCM)
373    pub format_tag: u16,
374    /// Number of physical channels
375    pub nr_channels: u8,
376    /// Number of bytes per audio subframe (sample container)
377    pub subframe_size: u8,
378    /// Number of significant bits per sample
379    pub bit_resolution: u8,
380    /// Supported sample rates
381    pub sample_rates: Vec<SampleRateRange>,
382}
383
384impl AudioFormatDescriptor {
385    /// Check if a given sample rate is supported
386    pub(crate) fn supports_rate(&self, rate: u32) -> bool {
387        // If no rates listed, we assume any rate is ok (continuous)
388        if self.sample_rates.is_empty() {
389            return true;
390        }
391        self.sample_rates.iter().any(|r| r.contains(rate))
392    }
393
394    /// Check if this format is PCM
395    pub(crate) fn is_pcm(&self) -> bool {
396        self.format_tag == UAC_FORMAT_PCM || self.format_tag == UAC_FORMAT_PCM8
397    }
398
399    /// Get the frame size in bytes (channels * subframe_size)
400    pub(crate) fn frame_size(&self) -> u16 {
401        self.nr_channels as u16 * self.subframe_size as u16
402    }
403}
404
405// ============================================================================
406// Audio Streaming Interface
407// ============================================================================
408
409/// An audio streaming interface alternate setting
410#[derive(Debug, Clone)]
411pub struct AudioStreamingInterface {
412    /// Interface number
413    pub interface_num: u8,
414    /// Alternate setting number (0 = zero-bandwidth)
415    pub alternate_setting: u8,
416    /// Terminal ID this stream is linked to
417    pub terminal_link: u8,
418    /// Format descriptor
419    pub format: AudioFormatDescriptor,
420    /// Isochronous endpoint address (0x80 | ep_num for IN, ep_num for OUT)
421    pub endpoint_address: u8,
422    /// Maximum packet size for the isochronous endpoint
423    pub max_packet_size: u16,
424    /// Synchronization type
425    pub sync_type: IsoSyncType,
426    /// Sync endpoint address (for async mode feedback)
427    pub sync_endpoint: u8,
428}
429
430impl AudioStreamingInterface {
431    /// Check if this is an input (capture) stream
432    pub(crate) fn is_input(&self) -> bool {
433        self.endpoint_address & 0x80 != 0
434    }
435
436    /// Check if this is an output (playback) stream
437    pub(crate) fn is_output(&self) -> bool {
438        self.endpoint_address & 0x80 == 0
439    }
440
441    /// Check if this is the zero-bandwidth alternate setting
442    pub(crate) fn is_zero_bandwidth(&self) -> bool {
443        self.alternate_setting == 0
444    }
445
446    /// Calculate bytes per frame at a given sample rate
447    /// Returns bytes per USB frame (1ms at full speed, 125us at high speed)
448    pub(crate) fn bytes_per_usb_frame(&self, sample_rate: u32) -> u32 {
449        // For full-speed: 1 frame = 1ms, so bytes = sample_rate/1000 * frame_size
450        let frame_size = self.format.frame_size() as u32;
451        // Use integer division: (sample_rate * frame_size) / 1000
452        sample_rate.saturating_mul(frame_size) / 1000
453    }
454}
455
456// ============================================================================
457// Volume Control (8.8 Fixed-Point dB)
458// ============================================================================
459
460/// Volume in 8.8 fixed-point dB format (USB Audio spec)
461///
462/// The USB Audio Class represents volume as a signed 16-bit value in
463/// 1/256 dB steps. The integer part is the upper 8 bits (signed),
464/// the fractional part is the lower 8 bits.
465///
466/// Examples:
467/// - 0x0000 = 0.0 dB (unity gain)
468/// - 0x0100 = +1.0 dB
469/// - 0xFF00 = -1.0 dB (two's complement)
470/// - 0x8000 = -128.0 dB (silence)
471/// - 0x7FFF = +127.99609375 dB (max)
472#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
473pub struct VolumeDb(pub i16);
474
475/// Silence volume (minimum, -128.0 dB)
476pub const VOLUME_SILENCE: VolumeDb = VolumeDb(i16::MIN);
477
478/// Unity gain (0.0 dB)
479pub const VOLUME_UNITY: VolumeDb = VolumeDb(0);
480
481/// Maximum volume (+127.99 dB)
482pub const VOLUME_MAX: VolumeDb = VolumeDb(i16::MAX);
483
484impl VolumeDb {
485    /// Create a volume from integer dB value
486    pub(crate) fn from_db(db: i8) -> Self {
487        VolumeDb((db as i16) << 8)
488    }
489
490    /// Get the integer part of the dB value
491    pub(crate) fn integer_db(&self) -> i8 {
492        (self.0 >> 8) as i8
493    }
494
495    /// Get the fractional part (0-255, representing 0/256 to 255/256 dB)
496    pub(crate) fn fraction(&self) -> u8 {
497        self.0 as u8
498    }
499
500    /// Get the raw 8.8 fixed-point value
501    pub(crate) fn raw(&self) -> i16 {
502        self.0
503    }
504
505    /// Convert to a linear gain multiplier (0..65535 range, 16-bit unsigned)
506    ///
507    /// Uses a piecewise integer approximation of 10^(dB/20):
508    /// - Maps -128 dB -> 0 (silence)
509    /// - Maps 0 dB -> 65535 (unity)
510    /// - Maps positive dB values to saturated 65535
511    ///
512    /// This is an approximation suitable for kernel audio mixing.
513    #[allow(clippy::wrong_self_convention)]
514    pub(crate) fn to_linear_u16(&self) -> u16 {
515        let db_int = self.integer_db();
516
517        if db_int >= 0 {
518            // Positive dB: clamp to max
519            return 65535;
520        }
521
522        if db_int <= -96 {
523            // Below -96 dB is effectively silence
524            return 0;
525        }
526
527        // Piecewise linear approximation for -96..0 dB range
528        // Map -96 dB -> 0, 0 dB -> 65535
529        // linear_gain = 65535 * 10^(dB/20)
530        // Approximation: use a lookup table for every 6 dB step
531        // Every -6 dB halves the amplitude
532        let abs_db = (-db_int) as u32;
533        let steps_6db = abs_db / 6;
534        let remainder = abs_db % 6;
535
536        // Start from 65535 and halve for each 6 dB step
537        let mut gain: u32 = 65535;
538        let mut i = 0u32;
539        while i < steps_6db && i < 16 {
540            gain /= 2;
541            i += 1;
542        }
543
544        // Interpolate the remainder (linear approx within 6 dB)
545        // For each dB below a 6 dB boundary, scale by ~(6-remainder)/6
546        if remainder > 0 {
547            gain = gain.saturating_mul(6u32.saturating_sub(remainder)) / 6;
548        }
549
550        if gain > 65535 {
551            65535
552        } else {
553            gain as u16
554        }
555    }
556
557    /// Create from a linear 0..65535 volume value
558    ///
559    /// Inverse of `to_linear_u16()`, approximate.
560    pub(crate) fn from_linear_u16(linear: u16) -> Self {
561        if linear == 0 {
562            return VOLUME_SILENCE;
563        }
564        if linear == u16::MAX {
565            return VOLUME_UNITY;
566        }
567
568        // Count how many times we can double to reach 65535
569        // Each doubling is ~+6 dB
570        let mut val = linear as u32;
571        let mut db: i32 = 0;
572        while val < 65535 && db > -96 {
573            val = val.saturating_mul(2);
574            db -= 6;
575        }
576
577        VolumeDb::from_db(db as i8)
578    }
579}
580
581// ============================================================================
582// USB Audio Device State
583// ============================================================================
584
585/// A parsed USB Audio device with all its audio topology
586#[derive(Debug, Clone)]
587pub struct UsbAudioDevice {
588    /// USB device address
589    pub device_address: u8,
590    /// UAC version detected
591    pub version: UacVersion,
592    /// Audio Control interface number
593    pub control_interface: u8,
594    /// All audio units/terminals in the topology
595    pub units: Vec<AudioUnit>,
596    /// All audio streaming interface alternate settings
597    pub streaming_interfaces: Vec<AudioStreamingInterface>,
598    /// Currently active streaming interface (interface_num, alternate_setting)
599    pub active_stream: Option<(u8, u8)>,
600    /// Current sample rate in Hz
601    pub current_sample_rate: u32,
602    /// Device name (from USB string descriptor)
603    pub name: String,
604}
605
606impl UsbAudioDevice {
607    /// Create a new USB audio device
608    pub fn new(device_address: u8, version: UacVersion) -> Self {
609        Self {
610            device_address,
611            version,
612            control_interface: 0,
613            units: Vec::new(),
614            streaming_interfaces: Vec::new(),
615            active_stream: None,
616            current_sample_rate: 0,
617            name: String::new(),
618        }
619    }
620
621    /// Find an input terminal by ID
622    pub(crate) fn find_input_terminal(&self, id: u8) -> Option<&InputTerminal> {
623        self.units.iter().find_map(|u| match u {
624            AudioUnit::InputTerminal(t) if t.terminal_id == id => Some(t),
625            _ => None,
626        })
627    }
628
629    /// Find an output terminal by ID
630    pub(crate) fn find_output_terminal(&self, id: u8) -> Option<&OutputTerminal> {
631        self.units.iter().find_map(|u| match u {
632            AudioUnit::OutputTerminal(t) if t.terminal_id == id => Some(t),
633            _ => None,
634        })
635    }
636
637    /// Find a feature unit by ID
638    pub(crate) fn find_feature_unit(&self, id: u8) -> Option<&FeatureUnit> {
639        self.units.iter().find_map(|u| match u {
640            AudioUnit::FeatureUnit(f) if f.unit_id == id => Some(f),
641            _ => None,
642        })
643    }
644
645    /// Get all feature units in the topology
646    pub(crate) fn feature_units(&self) -> Vec<&FeatureUnit> {
647        self.units
648            .iter()
649            .filter_map(|u| match u {
650                AudioUnit::FeatureUnit(f) => Some(f),
651                _ => None,
652            })
653            .collect()
654    }
655
656    /// Get all output (playback) streaming interfaces
657    pub(crate) fn playback_interfaces(&self) -> Vec<&AudioStreamingInterface> {
658        self.streaming_interfaces
659            .iter()
660            .filter(|s| s.is_output() && !s.is_zero_bandwidth())
661            .collect()
662    }
663
664    /// Get all input (capture) streaming interfaces
665    pub(crate) fn capture_interfaces(&self) -> Vec<&AudioStreamingInterface> {
666        self.streaming_interfaces
667            .iter()
668            .filter(|s| s.is_input() && !s.is_zero_bandwidth())
669            .collect()
670    }
671
672    /// Find a streaming interface that supports the given sample rate and
673    /// channels
674    pub(crate) fn find_compatible_interface(
675        &self,
676        sample_rate: u32,
677        channels: u8,
678        output: bool,
679    ) -> Option<&AudioStreamingInterface> {
680        self.streaming_interfaces.iter().find(|s| {
681            let direction_ok = if output { s.is_output() } else { s.is_input() };
682            direction_ok
683                && !s.is_zero_bandwidth()
684                && s.format.nr_channels == channels
685                && s.format.supports_rate(sample_rate)
686        })
687    }
688
689    /// Get the number of units in the topology
690    pub(crate) fn unit_count(&self) -> usize {
691        self.units.len()
692    }
693}
694
695// ============================================================================
696// UAC Descriptor Parser
697// ============================================================================
698
699/// Parse a UAC Audio Control interface header
700///
701/// Returns (UAC version, total_length) or error.
702pub(crate) fn parse_ac_header(data: &[u8]) -> Result<(UacVersion, u16), KernelError> {
703    // Minimum AC header: bLength(1) + bDescriptorType(1) + bDescriptorSubtype(1)
704    //                    + bcdADC(2) + wTotalLength(2) + bInCollection(1) +
705    //                      baInterfaceNr(1...)
706    if data.len() < 8 {
707        return Err(KernelError::InvalidArgument {
708            name: "ac_header",
709            value: "too short",
710        });
711    }
712
713    if data[1] != CS_INTERFACE || data[2] != UAC_AC_HEADER {
714        return Err(KernelError::InvalidArgument {
715            name: "ac_header",
716            value: "wrong descriptor type",
717        });
718    }
719
720    let bcd_adc = u16::from_le_bytes([data[3], data[4]]);
721    let version = if bcd_adc >= 0x0200 {
722        UacVersion::Uac20
723    } else {
724        UacVersion::Uac10
725    };
726
727    let total_length = u16::from_le_bytes([data[5], data[6]]);
728
729    Ok((version, total_length))
730}
731
732/// Parse an Input Terminal descriptor from raw bytes
733pub(crate) fn parse_input_terminal(data: &[u8]) -> Result<InputTerminal, KernelError> {
734    // UAC 1.0 input terminal is at least 12 bytes
735    if data.len() < 12 {
736        return Err(KernelError::InvalidArgument {
737            name: "input_terminal",
738            value: "too short",
739        });
740    }
741
742    if data[1] != CS_INTERFACE || data[2] != UAC_INPUT_TERMINAL {
743        return Err(KernelError::InvalidArgument {
744            name: "input_terminal",
745            value: "wrong subtype",
746        });
747    }
748
749    Ok(InputTerminal {
750        terminal_id: data[3],
751        terminal_type: u16::from_le_bytes([data[4], data[5]]),
752        assoc_terminal: data[6],
753        nr_channels: data[7],
754        channel_config: u16::from_le_bytes([data[8], data[9]]),
755    })
756}
757
758/// Parse an Output Terminal descriptor from raw bytes
759pub(crate) fn parse_output_terminal(data: &[u8]) -> Result<OutputTerminal, KernelError> {
760    // UAC 1.0 output terminal is at least 9 bytes
761    if data.len() < 9 {
762        return Err(KernelError::InvalidArgument {
763            name: "output_terminal",
764            value: "too short",
765        });
766    }
767
768    if data[1] != CS_INTERFACE || data[2] != UAC_OUTPUT_TERMINAL {
769        return Err(KernelError::InvalidArgument {
770            name: "output_terminal",
771            value: "wrong subtype",
772        });
773    }
774
775    Ok(OutputTerminal {
776        terminal_id: data[3],
777        terminal_type: u16::from_le_bytes([data[4], data[5]]),
778        assoc_terminal: data[6],
779        source_id: data[7],
780    })
781}
782
783/// Parse a Feature Unit descriptor from raw bytes (UAC 1.0)
784pub(crate) fn parse_feature_unit(data: &[u8]) -> Result<FeatureUnit, KernelError> {
785    // Minimum: bLength(1) + bDescriptorType(1) + bDescriptorSubtype(1)
786    //        + bUnitID(1) + bSourceID(1) + bControlSize(1) + bmaControls(...)
787    if data.len() < 7 {
788        return Err(KernelError::InvalidArgument {
789            name: "feature_unit",
790            value: "too short",
791        });
792    }
793
794    if data[1] != CS_INTERFACE || data[2] != UAC_FEATURE_UNIT {
795        return Err(KernelError::InvalidArgument {
796            name: "feature_unit",
797            value: "wrong subtype",
798        });
799    }
800
801    let unit_id = data[3];
802    let source_id = data[4];
803    let control_size = data[5] as usize;
804
805    if control_size == 0 {
806        return Ok(FeatureUnit {
807            unit_id,
808            source_id,
809            controls: Vec::new(),
810        });
811    }
812
813    // Parse per-channel control bitmasks
814    let controls_start = 6;
815    let bma_len = data[0] as usize - controls_start - 1; // subtract iString at end
816    let num_controls = if control_size > 0 {
817        bma_len / control_size
818    } else {
819        0
820    };
821
822    let mut controls = Vec::new();
823    for i in 0..num_controls {
824        let offset = controls_start + i * control_size;
825        let mut ctrl: u32 = 0;
826        for b in 0..control_size.min(4) {
827            if offset + b < data.len() {
828                ctrl |= (data[offset + b] as u32) << (b * 8);
829            }
830        }
831        controls.push(ctrl);
832    }
833
834    Ok(FeatureUnit {
835        unit_id,
836        source_id,
837        controls,
838    })
839}
840
841/// Parse a Mixer Unit descriptor from raw bytes
842pub(crate) fn parse_mixer_unit(data: &[u8]) -> Result<MixerUnit, KernelError> {
843    // Minimum: bLength(1) + bDescriptorType(1) + bDescriptorSubtype(1)
844    //        + bUnitID(1) + bNrInPins(1) + baSourceID(...)
845    if data.len() < 5 {
846        return Err(KernelError::InvalidArgument {
847            name: "mixer_unit",
848            value: "too short",
849        });
850    }
851
852    if data[1] != CS_INTERFACE || data[2] != UAC_MIXER_UNIT {
853        return Err(KernelError::InvalidArgument {
854            name: "mixer_unit",
855            value: "wrong subtype",
856        });
857    }
858
859    let unit_id = data[3];
860    let nr_in_pins = data[4] as usize;
861
862    let mut source_ids = Vec::new();
863    for i in 0..nr_in_pins {
864        let idx = 5 + i;
865        if idx < data.len() {
866            source_ids.push(data[idx]);
867        }
868    }
869
870    // After source IDs: bNrChannels, wChannelConfig
871    let chan_offset = 5 + nr_in_pins;
872    let nr_channels = if chan_offset < data.len() {
873        data[chan_offset]
874    } else {
875        0
876    };
877    let channel_config = if chan_offset + 2 < data.len() {
878        u16::from_le_bytes([data[chan_offset + 1], data[chan_offset + 2]])
879    } else {
880        0
881    };
882
883    Ok(MixerUnit {
884        unit_id,
885        source_ids,
886        nr_channels,
887        channel_config,
888    })
889}
890
891/// Parse an Audio Streaming format type descriptor (UAC 1.0 Type I)
892pub(crate) fn parse_format_type_i(data: &[u8]) -> Result<AudioFormatDescriptor, KernelError> {
893    // Minimum: bLength(1) + bDescriptorType(1) + bDescriptorSubtype(1)
894    //        + bFormatType(1) + bNrChannels(1) + bSubframeSize(1)
895    //        + bBitResolution(1) + bSamFreqType(1)
896    if data.len() < 8 {
897        return Err(KernelError::InvalidArgument {
898            name: "format_type",
899            value: "too short",
900        });
901    }
902
903    if data[1] != CS_INTERFACE || data[2] != UAC_AS_FORMAT_TYPE {
904        return Err(KernelError::InvalidArgument {
905            name: "format_type",
906            value: "wrong subtype",
907        });
908    }
909
910    let nr_channels = data[4];
911    let subframe_size = data[5];
912    let bit_resolution = data[6];
913    let sam_freq_type = data[7];
914
915    let mut sample_rates = Vec::new();
916
917    if sam_freq_type == 0 {
918        // Continuous range: tLowerSamFreq(3) + tUpperSamFreq(3)
919        if data.len() >= 14 {
920            let lower = read_u24_le(&data[8..11]);
921            let upper = read_u24_le(&data[11..14]);
922            sample_rates.push(SampleRateRange::range(lower, upper));
923        }
924    } else {
925        // Discrete sample rates: N * tSamFreq(3)
926        for i in 0..sam_freq_type as usize {
927            let offset = 8 + i * 3;
928            if offset + 3 <= data.len() {
929                let rate = read_u24_le(&data[offset..offset + 3]);
930                sample_rates.push(SampleRateRange::discrete(rate));
931            }
932        }
933    }
934
935    Ok(AudioFormatDescriptor {
936        format_tag: UAC_FORMAT_PCM, // Type I is always PCM-like
937        nr_channels,
938        subframe_size,
939        bit_resolution,
940        sample_rates,
941    })
942}
943
944/// Read a 24-bit little-endian unsigned integer
945fn read_u24_le(data: &[u8]) -> u32 {
946    data[0] as u32 | ((data[1] as u32) << 8) | ((data[2] as u32) << 16)
947}
948
949/// Parse a Clock Source descriptor (UAC 2.0)
950pub(crate) fn parse_clock_source(data: &[u8]) -> Result<ClockSource, KernelError> {
951    // bLength(1) + bDescriptorType(1) + bDescriptorSubtype(1)
952    // + bClockID(1) + bmAttributes(1) + bmControls(1) + bAssocTerminal(1)
953    if data.len() < 7 {
954        return Err(KernelError::InvalidArgument {
955            name: "clock_source",
956            value: "too short",
957        });
958    }
959
960    if data[1] != CS_INTERFACE || data[2] != UAC2_CLOCK_SOURCE {
961        return Err(KernelError::InvalidArgument {
962            name: "clock_source",
963            value: "wrong subtype",
964        });
965    }
966
967    Ok(ClockSource {
968        clock_id: data[3],
969        attributes: data[4],
970        assoc_terminal: data[6],
971    })
972}
973
974// ============================================================================
975// Sample Rate Control
976// ============================================================================
977
978/// Build a SET_CUR sample rate control request for UAC 1.0
979///
980/// Returns the control request bytes for setting the sample rate on an
981/// isochronous endpoint.
982pub(crate) fn build_set_sample_rate_request(endpoint: u8, sample_rate: u32) -> SampleRateRequest {
983    SampleRateRequest {
984        request_type: 0x22, // Host-to-device, class, endpoint
985        request: UAC_SET_CUR,
986        value: 0x0100, // Sampling Frequency Control
987        index: endpoint as u16,
988        sample_rate,
989    }
990}
991
992/// Build a GET_CUR sample rate control request for UAC 1.0
993pub(crate) fn build_get_sample_rate_request(endpoint: u8) -> SampleRateRequest {
994    SampleRateRequest {
995        request_type: 0xA2, // Device-to-host, class, endpoint
996        request: UAC_GET_CUR,
997        value: 0x0100,
998        index: endpoint as u16,
999        sample_rate: 0,
1000    }
1001}
1002
1003/// Sample rate control request
1004#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1005pub struct SampleRateRequest {
1006    /// bmRequestType
1007    pub request_type: u8,
1008    /// bRequest (SET_CUR or GET_CUR)
1009    pub request: u8,
1010    /// wValue (control selector << 8 | channel number)
1011    pub value: u16,
1012    /// wIndex (endpoint address)
1013    pub index: u16,
1014    /// Sample rate in Hz (3 bytes in USB, stored as u32)
1015    pub sample_rate: u32,
1016}
1017
1018impl SampleRateRequest {
1019    /// Encode the sample rate as 3-byte little-endian for USB transfer
1020    pub(crate) fn rate_bytes(&self) -> [u8; 3] {
1021        [
1022            (self.sample_rate & 0xFF) as u8,
1023            ((self.sample_rate >> 8) & 0xFF) as u8,
1024            ((self.sample_rate >> 16) & 0xFF) as u8,
1025        ]
1026    }
1027
1028    /// Decode a 3-byte sample rate response
1029    pub(crate) fn decode_rate(data: &[u8]) -> u32 {
1030        if data.len() >= 3 {
1031            read_u24_le(data)
1032        } else {
1033            0
1034        }
1035    }
1036}
1037
1038// ============================================================================
1039// Volume Control Requests
1040// ============================================================================
1041
1042/// Build a SET_CUR volume control request
1043pub(crate) fn build_set_volume_request(
1044    unit_id: u8,
1045    channel: u8,
1046    interface: u16,
1047    volume: VolumeDb,
1048) -> VolumeControlRequest {
1049    VolumeControlRequest {
1050        request_type: 0x21, // Host-to-device, class, interface
1051        request: UAC_SET_CUR,
1052        value: (UAC_FU_VOLUME_CONTROL as u16) << 8 | channel as u16,
1053        index: (unit_id as u16) << 8 | interface,
1054        volume,
1055    }
1056}
1057
1058/// Build a GET_CUR volume control request
1059pub(crate) fn build_get_volume_request(
1060    unit_id: u8,
1061    channel: u8,
1062    interface: u16,
1063) -> VolumeControlRequest {
1064    VolumeControlRequest {
1065        request_type: 0xA1, // Device-to-host, class, interface
1066        request: UAC_GET_CUR,
1067        value: (UAC_FU_VOLUME_CONTROL as u16) << 8 | channel as u16,
1068        index: (unit_id as u16) << 8 | interface,
1069        volume: VolumeDb(0),
1070    }
1071}
1072
1073/// Build a SET_CUR mute control request
1074pub(crate) fn build_set_mute_request(
1075    unit_id: u8,
1076    channel: u8,
1077    interface: u16,
1078    muted: bool,
1079) -> MuteControlRequest {
1080    MuteControlRequest {
1081        request_type: 0x21,
1082        request: UAC_SET_CUR,
1083        value: (UAC_FU_MUTE_CONTROL as u16) << 8 | channel as u16,
1084        index: (unit_id as u16) << 8 | interface,
1085        muted,
1086    }
1087}
1088
1089/// Volume control request
1090#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1091pub struct VolumeControlRequest {
1092    pub request_type: u8,
1093    pub request: u8,
1094    pub value: u16,
1095    pub index: u16,
1096    pub volume: VolumeDb,
1097}
1098
1099impl VolumeControlRequest {
1100    /// Encode the volume value as 2-byte little-endian for USB transfer
1101    pub(crate) fn volume_bytes(&self) -> [u8; 2] {
1102        self.volume.raw().to_le_bytes()
1103    }
1104
1105    /// Decode a 2-byte volume response
1106    pub(crate) fn decode_volume(data: &[u8]) -> VolumeDb {
1107        if data.len() >= 2 {
1108            VolumeDb(i16::from_le_bytes([data[0], data[1]]))
1109        } else {
1110            VOLUME_SILENCE
1111        }
1112    }
1113}
1114
1115/// Mute control request
1116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1117pub struct MuteControlRequest {
1118    pub request_type: u8,
1119    pub request: u8,
1120    pub value: u16,
1121    pub index: u16,
1122    pub muted: bool,
1123}
1124
1125// ============================================================================
1126// Device Enumeration
1127// ============================================================================
1128
1129/// Scan USB descriptor data for audio class interfaces and build device
1130/// topology
1131///
1132/// Takes the full configuration descriptor bytes and parses all audio-related
1133/// descriptors into a `UsbAudioDevice`.
1134pub(crate) fn enumerate_audio_device(
1135    device_address: u8,
1136    config_descriptor: &[u8],
1137) -> Result<UsbAudioDevice, KernelError> {
1138    if config_descriptor.len() < 9 {
1139        return Err(KernelError::InvalidArgument {
1140            name: "config_descriptor",
1141            value: "too short",
1142        });
1143    }
1144
1145    let mut device = UsbAudioDevice::new(device_address, UacVersion::Uac10);
1146    let mut offset = 0;
1147
1148    // Walk through all descriptors
1149    while offset + 2 <= config_descriptor.len() {
1150        let desc_len = config_descriptor[offset] as usize;
1151        if desc_len < 2 || offset + desc_len > config_descriptor.len() {
1152            break;
1153        }
1154
1155        let desc_type = config_descriptor[offset + 1];
1156        let desc_data = &config_descriptor[offset..offset + desc_len];
1157
1158        if desc_type == CS_INTERFACE && desc_len >= 3 {
1159            let subtype = desc_data[2];
1160            match subtype {
1161                UAC_AC_HEADER => {
1162                    if let Ok((version, _total_len)) = parse_ac_header(desc_data) {
1163                        device.version = version;
1164                    }
1165                }
1166                UAC_INPUT_TERMINAL => {
1167                    // UAC_INPUT_TERMINAL == UAC_AS_FORMAT_TYPE (0x02).
1168                    // Try input terminal first, then fall back to format type.
1169                    if let Ok(terminal) = parse_input_terminal(desc_data) {
1170                        device.units.push(AudioUnit::InputTerminal(terminal));
1171                    } else if let Ok(format) = parse_format_type_i(desc_data) {
1172                        if let Some(last_stream) = device.streaming_interfaces.last_mut() {
1173                            last_stream.format = format;
1174                        }
1175                    }
1176                }
1177                UAC_OUTPUT_TERMINAL => {
1178                    if let Ok(terminal) = parse_output_terminal(desc_data) {
1179                        device.units.push(AudioUnit::OutputTerminal(terminal));
1180                    }
1181                }
1182                UAC_FEATURE_UNIT => {
1183                    if let Ok(unit) = parse_feature_unit(desc_data) {
1184                        device.units.push(AudioUnit::FeatureUnit(unit));
1185                    }
1186                }
1187                UAC_MIXER_UNIT => {
1188                    if let Ok(unit) = parse_mixer_unit(desc_data) {
1189                        device.units.push(AudioUnit::MixerUnit(unit));
1190                    }
1191                }
1192                UAC2_CLOCK_SOURCE => {
1193                    if let Ok(clock) = parse_clock_source(desc_data) {
1194                        device.units.push(AudioUnit::ClockSource(clock));
1195                    }
1196                }
1197                _ => {} // Other subtypes ignored for now
1198            }
1199        }
1200
1201        offset += desc_len;
1202    }
1203
1204    Ok(device)
1205}
1206
1207// ============================================================================
1208// Standard Sample Rate Table
1209// ============================================================================
1210
1211/// Standard USB audio sample rates in Hz
1212pub const STANDARD_SAMPLE_RATES: &[u32] = &[
1213    8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000,
1214];
1215
1216/// Check if a sample rate is a standard USB audio rate
1217pub(crate) fn is_standard_sample_rate(rate: u32) -> bool {
1218    STANDARD_SAMPLE_RATES.contains(&rate)
1219}
1220
1221// ============================================================================
1222// HDMI Audio Constants
1223// ============================================================================
1224
1225/// HDMI audio infoframe type code (CEA-861)
1226pub const HDMI_AUDIO_INFOFRAME_TYPE: u8 = 0x84;
1227
1228/// HDMI audio infoframe version
1229pub const HDMI_AUDIO_INFOFRAME_VERSION: u8 = 0x01;
1230
1231/// HDMI audio infoframe length (fixed at 10 bytes)
1232pub const HDMI_AUDIO_INFOFRAME_LENGTH: u8 = 0x0A;
1233
1234/// HDMI audio coding types
1235#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1236#[repr(u8)]
1237pub enum HdmiAudioCoding {
1238    /// Refer to stream header
1239    StreamHeader = 0,
1240    /// IEC 60958 PCM (L-PCM)
1241    Pcm = 1,
1242    /// AC-3
1243    Ac3 = 2,
1244    /// MPEG-1 (layers 1 & 2)
1245    Mpeg1 = 3,
1246    /// MP3 (MPEG-1 layer 3)
1247    Mp3 = 4,
1248    /// MPEG-2 multichannel
1249    Mpeg2 = 5,
1250    /// AAC-LC
1251    AacLc = 6,
1252    /// DTS
1253    Dts = 7,
1254    /// ATRAC
1255    Atrac = 8,
1256    /// One Bit Audio (DSD)
1257    OneBitAudio = 9,
1258    /// Enhanced AC-3 (Dolby Digital Plus)
1259    EnhancedAc3 = 10,
1260    /// DTS-HD
1261    DtsHd = 11,
1262    /// MAT (MLP / Dolby TrueHD)
1263    Mat = 12,
1264    /// DST
1265    Dst = 13,
1266    /// WMA Pro
1267    WmaPro = 14,
1268}
1269
1270/// HDMI channel allocation codes (CEA-861-D Table 20)
1271#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1272pub enum HdmiChannelAllocation {
1273    /// 2.0 Stereo: FL, FR
1274    Stereo = 0x00,
1275    /// 2.1: FL, FR, LFE
1276    Stereo21 = 0x01,
1277    /// 3.0: FL, FR, FC
1278    Surround30 = 0x02,
1279    /// 3.1: FL, FR, LFE, FC
1280    Surround31 = 0x03,
1281    /// 5.0: FL, FR, FC, RL, RR
1282    Surround50 = 0x0A,
1283    /// 5.1: FL, FR, LFE, FC, RL, RR
1284    Surround51 = 0x0B,
1285    /// 7.0: FL, FR, FC, RL, RR, FLC, FRC
1286    Surround70 = 0x12,
1287    /// 7.1: FL, FR, LFE, FC, RL, RR, FLC, FRC
1288    Surround71 = 0x13,
1289}
1290
1291impl HdmiChannelAllocation {
1292    /// Get the number of audio channels for this allocation
1293    pub(crate) fn channel_count(&self) -> u8 {
1294        match self {
1295            HdmiChannelAllocation::Stereo => 2,
1296            HdmiChannelAllocation::Stereo21 => 3,
1297            HdmiChannelAllocation::Surround30 => 3,
1298            HdmiChannelAllocation::Surround31 => 4,
1299            HdmiChannelAllocation::Surround50 => 5,
1300            HdmiChannelAllocation::Surround51 => 6,
1301            HdmiChannelAllocation::Surround70 => 7,
1302            HdmiChannelAllocation::Surround71 => 8,
1303        }
1304    }
1305
1306    /// Get the allocation code byte
1307    pub(crate) fn code(&self) -> u8 {
1308        *self as u8
1309    }
1310}
1311
1312/// HDMI audio sample rate encoding for infoframe
1313#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1314#[repr(u8)]
1315pub enum HdmiSampleRate {
1316    /// Refer to stream header
1317    StreamHeader = 0,
1318    /// 32 kHz
1319    Rate32000 = 1,
1320    /// 44.1 kHz
1321    Rate44100 = 2,
1322    /// 48 kHz
1323    Rate48000 = 3,
1324    /// 88.2 kHz
1325    Rate88200 = 4,
1326    /// 96 kHz
1327    Rate96000 = 5,
1328    /// 176.4 kHz
1329    Rate176400 = 6,
1330    /// 192 kHz
1331    Rate192000 = 7,
1332}
1333
1334impl HdmiSampleRate {
1335    /// Convert from Hz to HDMI sample rate code
1336    pub(crate) fn from_hz(hz: u32) -> Self {
1337        match hz {
1338            32000 => HdmiSampleRate::Rate32000,
1339            44100 => HdmiSampleRate::Rate44100,
1340            48000 => HdmiSampleRate::Rate48000,
1341            88200 => HdmiSampleRate::Rate88200,
1342            96000 => HdmiSampleRate::Rate96000,
1343            176400 => HdmiSampleRate::Rate176400,
1344            192000 => HdmiSampleRate::Rate192000,
1345            _ => HdmiSampleRate::StreamHeader,
1346        }
1347    }
1348
1349    /// Convert to Hz
1350    #[allow(clippy::wrong_self_convention)]
1351    pub(crate) fn to_hz(&self) -> u32 {
1352        match self {
1353            HdmiSampleRate::StreamHeader => 0,
1354            HdmiSampleRate::Rate32000 => 32000,
1355            HdmiSampleRate::Rate44100 => 44100,
1356            HdmiSampleRate::Rate48000 => 48000,
1357            HdmiSampleRate::Rate88200 => 88200,
1358            HdmiSampleRate::Rate96000 => 96000,
1359            HdmiSampleRate::Rate176400 => 176400,
1360            HdmiSampleRate::Rate192000 => 192000,
1361        }
1362    }
1363}
1364
1365/// HDMI audio sample size encoding for infoframe
1366#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1367#[repr(u8)]
1368pub enum HdmiSampleSize {
1369    /// Refer to stream header
1370    StreamHeader = 0,
1371    /// 16 bits per sample
1372    Bits16 = 1,
1373    /// 20 bits per sample
1374    Bits20 = 2,
1375    /// 24 bits per sample
1376    Bits24 = 3,
1377}
1378
1379impl HdmiSampleSize {
1380    /// Convert from bit depth
1381    pub(crate) fn from_bits(bits: u8) -> Self {
1382        match bits {
1383            16 => HdmiSampleSize::Bits16,
1384            20 => HdmiSampleSize::Bits20,
1385            24 => HdmiSampleSize::Bits24,
1386            _ => HdmiSampleSize::StreamHeader,
1387        }
1388    }
1389}
1390
1391// ============================================================================
1392// HDMI Audio Infoframe
1393// ============================================================================
1394
1395/// HDMI Audio InfoFrame packet (CEA-861)
1396///
1397/// Transmitted over HDMI to inform the sink (TV/receiver) about the audio
1398/// stream format. The infoframe is 10 bytes of payload after the header.
1399#[derive(Debug, Clone, PartialEq, Eq)]
1400pub struct HdmiAudioInfoframe {
1401    /// Audio coding type
1402    pub coding_type: HdmiAudioCoding,
1403    /// Channel count (actual count - 1, as stored in CEA)
1404    pub channel_count: u8,
1405    /// Sample rate
1406    pub sample_rate: HdmiSampleRate,
1407    /// Sample size (for PCM)
1408    pub sample_size: HdmiSampleSize,
1409    /// Channel allocation (speaker mapping)
1410    pub channel_allocation: HdmiChannelAllocation,
1411    /// Level shift value (0-15, in 1 dB steps)
1412    pub level_shift: u8,
1413    /// Downmix inhibit flag
1414    pub downmix_inhibit: bool,
1415}
1416
1417impl HdmiAudioInfoframe {
1418    /// Create a stereo PCM infoframe with common settings
1419    pub(crate) fn stereo_pcm(sample_rate: u32, bit_depth: u8) -> Self {
1420        Self {
1421            coding_type: HdmiAudioCoding::Pcm,
1422            channel_count: 2,
1423            sample_rate: HdmiSampleRate::from_hz(sample_rate),
1424            sample_size: HdmiSampleSize::from_bits(bit_depth),
1425            channel_allocation: HdmiChannelAllocation::Stereo,
1426            level_shift: 0,
1427            downmix_inhibit: false,
1428        }
1429    }
1430
1431    /// Create a 5.1 surround PCM infoframe
1432    pub(crate) fn surround51_pcm(sample_rate: u32, bit_depth: u8) -> Self {
1433        Self {
1434            coding_type: HdmiAudioCoding::Pcm,
1435            channel_count: 6,
1436            sample_rate: HdmiSampleRate::from_hz(sample_rate),
1437            sample_size: HdmiSampleSize::from_bits(bit_depth),
1438            channel_allocation: HdmiChannelAllocation::Surround51,
1439            level_shift: 0,
1440            downmix_inhibit: false,
1441        }
1442    }
1443
1444    /// Create a 7.1 surround PCM infoframe
1445    pub(crate) fn surround71_pcm(sample_rate: u32, bit_depth: u8) -> Self {
1446        Self {
1447            coding_type: HdmiAudioCoding::Pcm,
1448            channel_count: 8,
1449            sample_rate: HdmiSampleRate::from_hz(sample_rate),
1450            sample_size: HdmiSampleSize::from_bits(bit_depth),
1451            channel_allocation: HdmiChannelAllocation::Surround71,
1452            level_shift: 0,
1453            downmix_inhibit: false,
1454        }
1455    }
1456
1457    /// Serialize the infoframe to a byte array (header + payload)
1458    ///
1459    /// Returns a 13-byte array:
1460    /// - Byte 0: Type code (0x84)
1461    /// - Byte 1: Version (0x01)
1462    /// - Byte 2: Length (0x0A)
1463    /// - Byte 3: Checksum
1464    /// - Bytes 4-13: Payload (DB1-DB10)
1465    pub(crate) fn to_bytes(&self) -> [u8; 14] {
1466        let mut buf = [0u8; 14];
1467
1468        // Header
1469        buf[0] = HDMI_AUDIO_INFOFRAME_TYPE;
1470        buf[1] = HDMI_AUDIO_INFOFRAME_VERSION;
1471        buf[2] = HDMI_AUDIO_INFOFRAME_LENGTH;
1472
1473        // DB1: Coding Type (bits 7-4) | Channel Count - 1 (bits 2-0)
1474        let cc = if self.channel_count > 0 {
1475            self.channel_count - 1
1476        } else {
1477            0
1478        };
1479        buf[4] = ((self.coding_type as u8) << 4) | (cc & 0x07);
1480
1481        // DB2: Sample Rate (bits 4-2) | Sample Size (bits 1-0)
1482        buf[5] = ((self.sample_rate as u8) << 2) | (self.sample_size as u8);
1483
1484        // DB3: Reserved (0x00)
1485        buf[6] = 0x00;
1486
1487        // DB4: Channel Allocation
1488        buf[7] = self.channel_allocation.code();
1489
1490        // DB5: Level Shift (bits 6-3) | Downmix Inhibit (bit 7)
1491        buf[8] = (self.level_shift & 0x0F) << 3;
1492        if self.downmix_inhibit {
1493            buf[8] |= 0x80;
1494        }
1495
1496        // DB6-DB10: Reserved
1497        // buf[9..14] already zeroed
1498
1499        // Calculate checksum: sum of all bytes (header + payload) must be 0 mod 256
1500        let mut sum: u8 = 0;
1501        for (i, &byte) in buf.iter().enumerate().take(14) {
1502            if i != 3 {
1503                // Skip checksum byte itself
1504                sum = sum.wrapping_add(byte);
1505            }
1506        }
1507        buf[3] = 0u8.wrapping_sub(sum);
1508
1509        buf
1510    }
1511
1512    /// Verify an infoframe checksum
1513    pub(crate) fn verify_checksum(data: &[u8]) -> bool {
1514        if data.len() < 14 {
1515            return false;
1516        }
1517        let sum: u8 = data[..14].iter().fold(0u8, |acc, &b| acc.wrapping_add(b));
1518        sum == 0
1519    }
1520
1521    /// Parse an infoframe from raw bytes
1522    pub(crate) fn from_bytes(data: &[u8]) -> Result<Self, KernelError> {
1523        if data.len() < 14 {
1524            return Err(KernelError::InvalidArgument {
1525                name: "hdmi_infoframe",
1526                value: "too short",
1527            });
1528        }
1529
1530        if data[0] != HDMI_AUDIO_INFOFRAME_TYPE {
1531            return Err(KernelError::InvalidArgument {
1532                name: "hdmi_infoframe",
1533                value: "wrong type code",
1534            });
1535        }
1536
1537        if !Self::verify_checksum(data) {
1538            return Err(KernelError::InvalidArgument {
1539                name: "hdmi_infoframe",
1540                value: "bad checksum",
1541            });
1542        }
1543
1544        let coding_raw = (data[4] >> 4) & 0x0F;
1545        let coding_type = match coding_raw {
1546            0 => HdmiAudioCoding::StreamHeader,
1547            1 => HdmiAudioCoding::Pcm,
1548            2 => HdmiAudioCoding::Ac3,
1549            3 => HdmiAudioCoding::Mpeg1,
1550            4 => HdmiAudioCoding::Mp3,
1551            5 => HdmiAudioCoding::Mpeg2,
1552            6 => HdmiAudioCoding::AacLc,
1553            7 => HdmiAudioCoding::Dts,
1554            8 => HdmiAudioCoding::Atrac,
1555            9 => HdmiAudioCoding::OneBitAudio,
1556            10 => HdmiAudioCoding::EnhancedAc3,
1557            11 => HdmiAudioCoding::DtsHd,
1558            12 => HdmiAudioCoding::Mat,
1559            13 => HdmiAudioCoding::Dst,
1560            14 => HdmiAudioCoding::WmaPro,
1561            _ => HdmiAudioCoding::StreamHeader,
1562        };
1563
1564        let channel_count = (data[4] & 0x07) + 1;
1565
1566        let sr_raw = (data[5] >> 2) & 0x07;
1567        let sample_rate = match sr_raw {
1568            1 => HdmiSampleRate::Rate32000,
1569            2 => HdmiSampleRate::Rate44100,
1570            3 => HdmiSampleRate::Rate48000,
1571            4 => HdmiSampleRate::Rate88200,
1572            5 => HdmiSampleRate::Rate96000,
1573            6 => HdmiSampleRate::Rate176400,
1574            7 => HdmiSampleRate::Rate192000,
1575            _ => HdmiSampleRate::StreamHeader,
1576        };
1577
1578        let ss_raw = data[5] & 0x03;
1579        let sample_size = match ss_raw {
1580            1 => HdmiSampleSize::Bits16,
1581            2 => HdmiSampleSize::Bits20,
1582            3 => HdmiSampleSize::Bits24,
1583            _ => HdmiSampleSize::StreamHeader,
1584        };
1585
1586        let ca_code = data[7];
1587        let channel_allocation = match ca_code {
1588            0x00 => HdmiChannelAllocation::Stereo,
1589            0x01 => HdmiChannelAllocation::Stereo21,
1590            0x02 => HdmiChannelAllocation::Surround30,
1591            0x03 => HdmiChannelAllocation::Surround31,
1592            0x0A => HdmiChannelAllocation::Surround50,
1593            0x0B => HdmiChannelAllocation::Surround51,
1594            0x12 => HdmiChannelAllocation::Surround70,
1595            0x13 => HdmiChannelAllocation::Surround71,
1596            _ => HdmiChannelAllocation::Stereo, // Default fallback
1597        };
1598
1599        let level_shift = (data[8] >> 3) & 0x0F;
1600        let downmix_inhibit = data[8] & 0x80 != 0;
1601
1602        Ok(Self {
1603            coding_type,
1604            channel_count,
1605            sample_rate,
1606            sample_size,
1607            channel_allocation,
1608            level_shift,
1609            downmix_inhibit,
1610        })
1611    }
1612}
1613
1614// ============================================================================
1615// HDMI Audio Clock Regeneration (ACR)
1616// ============================================================================
1617
1618/// Audio Clock Regeneration (ACR) parameters
1619///
1620/// HDMI uses ACR packets to reconstruct the audio sampling clock at the sink.
1621/// The relationship is: Fs = N * TMDS_clock / (128 * CTS)
1622///
1623/// The N and CTS values for standard sample rates at standard TMDS clock
1624/// frequencies are defined in the HDMI specification.
1625#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1626pub struct AudioClockRegeneration {
1627    /// N value (audio clock numerator)
1628    pub n: u32,
1629    /// CTS value (Cycle Time Stamp, denominator)
1630    pub cts: u32,
1631}
1632
1633impl AudioClockRegeneration {
1634    /// Get recommended N value for a given sample rate
1635    ///
1636    /// N values from HDMI 1.4b specification, Table 7-1/7-2/7-3.
1637    /// These are the recommended values for 148.5 MHz TMDS clock (1080p60).
1638    pub(crate) fn recommended_n(sample_rate: u32) -> u32 {
1639        match sample_rate {
1640            32000 => 4096,
1641            44100 => 6272,
1642            48000 => 6144,
1643            88200 => 12544,
1644            96000 => 12288,
1645            176400 => 25088,
1646            192000 => 24576,
1647            _ => {
1648                // For non-standard rates, use the formula: N = 128 * Fs / 1000
1649                // This gives a reasonable default when CTS is computed from TMDS clock
1650                sample_rate.saturating_mul(128) / 1000
1651            }
1652        }
1653    }
1654
1655    /// Calculate CTS from N, sample rate, and TMDS clock
1656    ///
1657    /// CTS = N * TMDS_clock / (128 * Fs)
1658    ///
1659    /// Uses u64 intermediate to avoid overflow.
1660    pub(crate) fn calculate_cts(n: u32, sample_rate: u32, tmds_clock_khz: u32) -> u32 {
1661        if sample_rate == 0 {
1662            return 0;
1663        }
1664
1665        // CTS = N * (TMDS_clock_kHz * 1000) / (128 * Fs)
1666        // Rearrange to avoid overflow: CTS = (N * TMDS_clock_kHz) / (128 * Fs / 1000)
1667        // But simpler with u64: CTS = (N as u64 * TMDS_clock_kHz as u64 * 1000) / (128
1668        // * Fs as u64)
1669        let numerator = (n as u64)
1670            .checked_mul(tmds_clock_khz as u64)
1671            .and_then(|v| v.checked_mul(1000));
1672        let denominator = 128u64.checked_mul(sample_rate as u64);
1673
1674        match (numerator, denominator) {
1675            (Some(num), Some(den)) if den > 0 => (num / den) as u32,
1676            _ => 0,
1677        }
1678    }
1679
1680    /// Create ACR parameters for a standard configuration
1681    ///
1682    /// # Arguments
1683    /// * `sample_rate` - Audio sample rate in Hz
1684    /// * `tmds_clock_khz` - TMDS pixel clock in kHz (e.g., 148500 for 1080p60)
1685    pub fn new(sample_rate: u32, tmds_clock_khz: u32) -> Self {
1686        let n = Self::recommended_n(sample_rate);
1687        let cts = Self::calculate_cts(n, sample_rate, tmds_clock_khz);
1688        Self { n, cts }
1689    }
1690
1691    /// Standard ACR for 1080p60 (148.5 MHz TMDS clock)
1692    pub(crate) fn for_1080p60(sample_rate: u32) -> Self {
1693        Self::new(sample_rate, 148500)
1694    }
1695
1696    /// Standard ACR for 4K60 (594 MHz TMDS clock)
1697    pub(crate) fn for_4k60(sample_rate: u32) -> Self {
1698        Self::new(sample_rate, 594000)
1699    }
1700
1701    /// Standard ACR for 720p60 (74.25 MHz TMDS clock)
1702    pub(crate) fn for_720p60(sample_rate: u32) -> Self {
1703        Self::new(sample_rate, 74250)
1704    }
1705}
1706
1707// ============================================================================
1708// ELD (EDID-Like Data) Parsing
1709// ============================================================================
1710
1711/// ELD (EDID-Like Data) for HDMI audio sink capabilities
1712///
1713/// The ELD block is provided by the GPU driver after reading the EDID from
1714/// the connected display. It contains information about the audio capabilities
1715/// of the sink device (TV, receiver, soundbar).
1716#[derive(Debug, Clone)]
1717pub struct HdmiEld {
1718    /// ELD version (2 for CEA version compatible)
1719    pub eld_ver: u8,
1720    /// Baseline ELD length (in 32-bit words)
1721    pub baseline_len: u8,
1722    /// CEA EDID version
1723    pub cea_edid_ver: u8,
1724    /// Monitor name string
1725    pub monitor_name: String,
1726    /// Number of supported Short Audio Descriptors
1727    pub sad_count: u8,
1728    /// Short Audio Descriptors
1729    pub sads: Vec<ShortAudioDescriptor>,
1730    /// Connection type (0 = HDMI, 1 = DisplayPort)
1731    pub conn_type: u8,
1732    /// Supports audio return channel (ARC)
1733    pub supports_arc: bool,
1734    /// Supports AI (audio input)
1735    pub supports_ai: bool,
1736    /// Port ID for multi-output GPUs
1737    pub port_id: u64,
1738    /// Manufacturer vendor ID
1739    pub manufacturer_id: u16,
1740    /// Product code
1741    pub product_code: u16,
1742}
1743
1744/// CEA Short Audio Descriptor (SAD)
1745///
1746/// 3-byte structure from CEA-861-D defining audio format support.
1747#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1748pub struct ShortAudioDescriptor {
1749    /// Audio format code (matches HdmiAudioCoding values)
1750    pub format_code: u8,
1751    /// Maximum number of channels supported
1752    pub max_channels: u8,
1753    /// Supported sample rate bitmask:
1754    /// bit 0: 32 kHz, bit 1: 44.1 kHz, bit 2: 48 kHz,
1755    /// bit 3: 88.2 kHz, bit 4: 96 kHz, bit 5: 176.4 kHz, bit 6: 192 kHz
1756    pub sample_rate_mask: u8,
1757    /// For PCM: bit depth mask (bit 0: 16-bit, bit 1: 20-bit, bit 2: 24-bit)
1758    /// For compressed: max bitrate / 8000
1759    pub detail: u8,
1760}
1761
1762impl ShortAudioDescriptor {
1763    /// Parse a SAD from 3 raw bytes
1764    pub(crate) fn from_bytes(data: &[u8; 3]) -> Self {
1765        Self {
1766            format_code: (data[0] >> 3) & 0x0F,
1767            max_channels: (data[0] & 0x07) + 1,
1768            sample_rate_mask: data[1] & 0x7F,
1769            detail: data[2],
1770        }
1771    }
1772
1773    /// Check if this SAD supports a given sample rate
1774    pub(crate) fn supports_rate(&self, rate: u32) -> bool {
1775        let bit = match rate {
1776            32000 => 0,
1777            44100 => 1,
1778            48000 => 2,
1779            88200 => 3,
1780            96000 => 4,
1781            176400 => 5,
1782            192000 => 6,
1783            _ => return false,
1784        };
1785        self.sample_rate_mask & (1 << bit) != 0
1786    }
1787
1788    /// Check if this SAD supports PCM and a given bit depth
1789    pub(crate) fn supports_pcm_depth(&self, bits: u8) -> bool {
1790        if self.format_code != 1 {
1791            // Only PCM has bit depth info
1792            return false;
1793        }
1794        match bits {
1795            16 => self.detail & 0x01 != 0,
1796            20 => self.detail & 0x02 != 0,
1797            24 => self.detail & 0x04 != 0,
1798            _ => false,
1799        }
1800    }
1801
1802    /// Check if this is a PCM format descriptor
1803    pub(crate) fn is_pcm(&self) -> bool {
1804        self.format_code == 1
1805    }
1806
1807    /// Serialize to 3-byte representation
1808    #[allow(clippy::wrong_self_convention)]
1809    pub(crate) fn to_bytes(&self) -> [u8; 3] {
1810        let byte0 =
1811            ((self.format_code & 0x0F) << 3) | ((self.max_channels.saturating_sub(1)) & 0x07);
1812        [byte0, self.sample_rate_mask & 0x7F, self.detail]
1813    }
1814}
1815
1816impl HdmiEld {
1817    /// Parse an ELD block from raw bytes
1818    ///
1819    /// The ELD format is defined in the HDA specification and contains
1820    /// baseline and vendor-specific sections.
1821    pub(crate) fn parse(data: &[u8]) -> Result<Self, KernelError> {
1822        if data.len() < 16 {
1823            return Err(KernelError::InvalidArgument {
1824                name: "eld",
1825                value: "too short",
1826            });
1827        }
1828
1829        let eld_ver = (data[0] >> 3) & 0x1F;
1830        let baseline_len = data[2];
1831        let cea_edid_ver = (data[4] >> 5) & 0x07;
1832
1833        let sad_count = (data[4] & 0x0F).min(15); // Max 15 SADs
1834        let conn_type = (data[5] >> 2) & 0x03;
1835        let supports_arc = data[5] & 0x02 != 0;
1836        let supports_ai = data[5] & 0x01 != 0;
1837
1838        // Monitor name length (byte 4, bits 7-5 are CEA ver, bits 4-0 are MNL...
1839        // Actually in ELD v2: byte 4 bits 4-0 = SAD count, byte 6 = MNL
1840        let mnl = data[6] as usize;
1841
1842        let manufacturer_id = u16::from_le_bytes([data[8], data[9]]);
1843        let product_code = u16::from_le_bytes([data[10], data[11]]);
1844
1845        let port_id = if data.len() >= 20 {
1846            u64::from_le_bytes([
1847                data[12], data[13], data[14], data[15], data[16], data[17], data[18], data[19],
1848            ])
1849        } else {
1850            0
1851        };
1852
1853        // Monitor name starts at offset 20
1854        let name_start = 20;
1855        let name_end = (name_start + mnl).min(data.len());
1856        let monitor_name = if name_start < data.len() {
1857            let name_bytes = &data[name_start..name_end];
1858            String::from_utf8_lossy(name_bytes).into_owned()
1859        } else {
1860            String::new()
1861        };
1862
1863        // SADs start after monitor name
1864        let sad_start = name_start + mnl;
1865        let mut sads = Vec::new();
1866        for i in 0..sad_count as usize {
1867            let offset = sad_start + i * 3;
1868            if offset + 3 <= data.len() {
1869                let sad_bytes: [u8; 3] = [data[offset], data[offset + 1], data[offset + 2]];
1870                sads.push(ShortAudioDescriptor::from_bytes(&sad_bytes));
1871            }
1872        }
1873
1874        Ok(Self {
1875            eld_ver,
1876            baseline_len,
1877            cea_edid_ver,
1878            monitor_name,
1879            sad_count,
1880            sads,
1881            conn_type,
1882            supports_arc,
1883            supports_ai,
1884            port_id,
1885            manufacturer_id,
1886            product_code,
1887        })
1888    }
1889
1890    /// Check if the sink supports a specific audio format
1891    pub(crate) fn supports_format(&self, format_code: u8, rate: u32, channels: u8) -> bool {
1892        self.sads.iter().any(|sad| {
1893            sad.format_code == format_code
1894                && sad.max_channels >= channels
1895                && sad.supports_rate(rate)
1896        })
1897    }
1898
1899    /// Check if the sink supports stereo PCM at a given rate and depth
1900    pub(crate) fn supports_stereo_pcm(&self, rate: u32, bit_depth: u8) -> bool {
1901        self.sads.iter().any(|sad| {
1902            sad.is_pcm()
1903                && sad.max_channels >= 2
1904                && sad.supports_rate(rate)
1905                && sad.supports_pcm_depth(bit_depth)
1906        })
1907    }
1908
1909    /// Get the maximum number of PCM channels supported
1910    pub(crate) fn max_pcm_channels(&self) -> u8 {
1911        self.sads
1912            .iter()
1913            .filter(|sad| sad.is_pcm())
1914            .map(|sad| sad.max_channels)
1915            .max()
1916            .unwrap_or(0)
1917    }
1918
1919    /// Get all supported PCM sample rates
1920    pub(crate) fn supported_pcm_rates(&self) -> Vec<u32> {
1921        let mut rates = Vec::new();
1922        let rate_table: &[(u32, u8)] = &[
1923            (32000, 0),
1924            (44100, 1),
1925            (48000, 2),
1926            (88200, 3),
1927            (96000, 4),
1928            (176400, 5),
1929            (192000, 6),
1930        ];
1931
1932        for sad in &self.sads {
1933            if sad.is_pcm() {
1934                for &(rate, bit) in rate_table {
1935                    if sad.sample_rate_mask & (1 << bit) != 0 && !rates.contains(&rate) {
1936                        rates.push(rate);
1937                    }
1938                }
1939            }
1940        }
1941
1942        rates
1943    }
1944}
1945
1946// ============================================================================
1947// HDMI Audio HDA Codec Integration
1948// ============================================================================
1949
1950/// HDA codec widget types relevant to HDMI audio
1951#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1952pub enum HdaWidgetType {
1953    /// Audio output converter (DAC)
1954    AudioOutput,
1955    /// Pin complex (HDMI output pin)
1956    PinComplex,
1957}
1958
1959/// HDMI audio output configuration for HDA codec
1960#[derive(Debug, Clone)]
1961pub struct HdmiAudioOutput {
1962    /// HDA codec address (0-15)
1963    pub codec_address: u8,
1964    /// Audio output converter widget NID
1965    pub converter_nid: u16,
1966    /// Pin complex widget NID
1967    pub pin_nid: u16,
1968    /// ELD data from connected sink (if available)
1969    pub eld: Option<HdmiEld>,
1970    /// Current infoframe being transmitted
1971    pub infoframe: Option<HdmiAudioInfoframe>,
1972    /// Current ACR parameters
1973    pub acr: Option<AudioClockRegeneration>,
1974    /// Whether audio is currently enabled on this output
1975    pub enabled: bool,
1976}
1977
1978impl HdmiAudioOutput {
1979    /// Create a new HDMI audio output
1980    pub fn new(codec_address: u8, converter_nid: u16, pin_nid: u16) -> Self {
1981        Self {
1982            codec_address,
1983            converter_nid,
1984            pin_nid,
1985            eld: None,
1986            infoframe: None,
1987            acr: None,
1988            enabled: false,
1989        }
1990    }
1991
1992    /// Configure the output for stereo PCM at the given rate
1993    pub(crate) fn configure_stereo_pcm(
1994        &mut self,
1995        sample_rate: u32,
1996        bit_depth: u8,
1997        tmds_clock_khz: u32,
1998    ) -> Result<(), KernelError> {
1999        // Check if sink supports this format
2000        if let Some(ref eld) = self.eld {
2001            if !eld.supports_stereo_pcm(sample_rate, bit_depth) {
2002                return Err(KernelError::OperationNotSupported {
2003                    operation: "sink does not support requested format",
2004                });
2005            }
2006        }
2007
2008        // Build infoframe
2009        self.infoframe = Some(HdmiAudioInfoframe::stereo_pcm(sample_rate, bit_depth));
2010
2011        // Calculate ACR
2012        self.acr = Some(AudioClockRegeneration::new(sample_rate, tmds_clock_khz));
2013
2014        Ok(())
2015    }
2016
2017    /// Enable audio output
2018    pub(crate) fn enable(&mut self) {
2019        self.enabled = true;
2020    }
2021
2022    /// Disable audio output
2023    pub(crate) fn disable(&mut self) {
2024        self.enabled = false;
2025    }
2026
2027    /// Check if a sink is connected (ELD available)
2028    pub(crate) fn has_sink(&self) -> bool {
2029        self.eld.is_some()
2030    }
2031}
2032
2033// ============================================================================
2034// Tests
2035// ============================================================================
2036
2037#[cfg(test)]
2038mod tests {
2039    #[allow(unused_imports)]
2040    use alloc::vec;
2041
2042    use super::*;
2043
2044    // --- IsoSyncType tests ---
2045
2046    #[test]
2047    fn test_iso_sync_type_from_attributes() {
2048        assert_eq!(IsoSyncType::from_attributes(0x00), IsoSyncType::None);
2049        assert_eq!(
2050            IsoSyncType::from_attributes(0x04),
2051            IsoSyncType::Asynchronous
2052        );
2053        assert_eq!(IsoSyncType::from_attributes(0x08), IsoSyncType::Adaptive);
2054        assert_eq!(IsoSyncType::from_attributes(0x0C), IsoSyncType::Synchronous);
2055        // Extra bits should be masked off
2056        assert_eq!(
2057            IsoSyncType::from_attributes(0xF4),
2058            IsoSyncType::Asynchronous
2059        );
2060    }
2061
2062    #[test]
2063    fn test_iso_sync_type_roundtrip() {
2064        for sync in [
2065            IsoSyncType::None,
2066            IsoSyncType::Asynchronous,
2067            IsoSyncType::Adaptive,
2068            IsoSyncType::Synchronous,
2069        ] {
2070            assert_eq!(IsoSyncType::from_attributes(sync.to_attributes()), sync);
2071        }
2072    }
2073
2074    // --- VolumeDb tests ---
2075
2076    #[test]
2077    fn test_volume_db_from_db() {
2078        let vol = VolumeDb::from_db(0);
2079        assert_eq!(vol.raw(), 0);
2080        assert_eq!(vol.integer_db(), 0);
2081        assert_eq!(vol.fraction(), 0);
2082
2083        let vol_pos = VolumeDb::from_db(10);
2084        assert_eq!(vol_pos.integer_db(), 10);
2085
2086        let vol_neg = VolumeDb::from_db(-20);
2087        assert_eq!(vol_neg.integer_db(), -20);
2088    }
2089
2090    #[test]
2091    fn test_volume_db_to_linear() {
2092        // 0 dB should map to max (65535)
2093        assert_eq!(VOLUME_UNITY.to_linear_u16(), 65535);
2094
2095        // Positive dB should also saturate to max
2096        assert_eq!(VolumeDb::from_db(10).to_linear_u16(), 65535);
2097
2098        // Very negative dB should be silence
2099        assert_eq!(VOLUME_SILENCE.to_linear_u16(), 0);
2100        assert_eq!(VolumeDb::from_db(-96).to_linear_u16(), 0);
2101
2102        // -6 dB should be approximately half amplitude
2103        let half = VolumeDb::from_db(-6).to_linear_u16();
2104        assert!(
2105            half > 30000 && half < 35000,
2106            "Expected ~32767, got {}",
2107            half
2108        );
2109    }
2110
2111    #[test]
2112    fn test_volume_db_from_linear() {
2113        assert_eq!(VolumeDb::from_linear_u16(0), VOLUME_SILENCE);
2114        assert_eq!(VolumeDb::from_linear_u16(65535), VOLUME_UNITY);
2115    }
2116
2117    // --- SampleRateRange tests ---
2118
2119    #[test]
2120    fn test_sample_rate_range_discrete() {
2121        let rate = SampleRateRange::discrete(48000);
2122        assert!(rate.is_discrete());
2123        assert!(rate.contains(48000));
2124        assert!(!rate.contains(44100));
2125    }
2126
2127    #[test]
2128    fn test_sample_rate_range_continuous() {
2129        let range = SampleRateRange::range(8000, 96000);
2130        assert!(!range.is_discrete());
2131        assert!(range.contains(44100));
2132        assert!(range.contains(48000));
2133        assert!(range.contains(8000));
2134        assert!(range.contains(96000));
2135        assert!(!range.contains(192000));
2136    }
2137
2138    // --- AudioFormatDescriptor tests ---
2139
2140    #[test]
2141    fn test_audio_format_supports_rate() {
2142        let format = AudioFormatDescriptor {
2143            format_tag: UAC_FORMAT_PCM,
2144            nr_channels: 2,
2145            subframe_size: 2,
2146            bit_resolution: 16,
2147            sample_rates: vec![
2148                SampleRateRange::discrete(44100),
2149                SampleRateRange::discrete(48000),
2150            ],
2151        };
2152        assert!(format.supports_rate(44100));
2153        assert!(format.supports_rate(48000));
2154        assert!(!format.supports_rate(96000));
2155        assert!(format.is_pcm());
2156        assert_eq!(format.frame_size(), 4); // 2 channels * 2 bytes
2157    }
2158
2159    #[test]
2160    fn test_audio_format_empty_rates() {
2161        let format = AudioFormatDescriptor {
2162            format_tag: UAC_FORMAT_PCM,
2163            nr_channels: 1,
2164            subframe_size: 2,
2165            bit_resolution: 16,
2166            sample_rates: Vec::new(),
2167        };
2168        // Empty rates list means any rate is acceptable
2169        assert!(format.supports_rate(44100));
2170        assert!(format.supports_rate(192000));
2171    }
2172
2173    // --- Feature Unit tests ---
2174
2175    #[test]
2176    fn test_feature_unit_controls() {
2177        let fu = FeatureUnit {
2178            unit_id: 5,
2179            source_id: 1,
2180            controls: vec![0x03, 0x01, 0x01], // master: mute+volume, ch1: mute, ch2: mute
2181        };
2182        assert!(fu.has_mute(0));
2183        assert!(fu.has_volume(0));
2184        assert!(fu.has_mute(1));
2185        assert!(!fu.has_volume(1));
2186        assert!(!fu.has_mute(10)); // Out of range
2187    }
2188
2189    // --- Descriptor parser tests ---
2190
2191    #[test]
2192    fn test_parse_ac_header() {
2193        // UAC 1.0 AC header
2194        let data = [
2195            9,             // bLength
2196            CS_INTERFACE,  // bDescriptorType
2197            UAC_AC_HEADER, // bDescriptorSubtype
2198            0x00,
2199            0x01, // bcdADC = 0x0100 (UAC 1.0)
2200            0x40,
2201            0x00, // wTotalLength = 64
2202            0x01, // bInCollection = 1
2203            0x01, // baInterfaceNr[0] = 1
2204        ];
2205        let (version, total_len) = parse_ac_header(&data).unwrap();
2206        assert_eq!(version, UacVersion::Uac10);
2207        assert_eq!(total_len, 64);
2208    }
2209
2210    #[test]
2211    fn test_parse_ac_header_uac20() {
2212        let data = [
2213            9,
2214            CS_INTERFACE,
2215            UAC_AC_HEADER,
2216            0x00,
2217            0x02, // bcdADC = 0x0200 (UAC 2.0)
2218            0x80,
2219            0x00,
2220            0x01,
2221            0x01,
2222        ];
2223        let (version, _) = parse_ac_header(&data).unwrap();
2224        assert_eq!(version, UacVersion::Uac20);
2225    }
2226
2227    #[test]
2228    fn test_parse_input_terminal() {
2229        let data = [
2230            12,                 // bLength
2231            CS_INTERFACE,       // bDescriptorType
2232            UAC_INPUT_TERMINAL, // bDescriptorSubtype
2233            0x01,               // bTerminalID
2234            0x01,
2235            0x02, // wTerminalType = 0x0201 (microphone)
2236            0x00, // bAssocTerminal
2237            0x02, // bNrChannels
2238            0x03,
2239            0x00, // wChannelConfig (front left + front right)
2240            0x00, // iChannelNames
2241            0x00, // iTerminal
2242        ];
2243        let terminal = parse_input_terminal(&data).unwrap();
2244        assert_eq!(terminal.terminal_id, 1);
2245        assert_eq!(terminal.terminal_type, 0x0201);
2246        assert_eq!(terminal.nr_channels, 2);
2247        assert_eq!(terminal.channel_config, 0x0003);
2248    }
2249
2250    #[test]
2251    fn test_parse_output_terminal() {
2252        let data = [
2253            9,                   // bLength
2254            CS_INTERFACE,        // bDescriptorType
2255            UAC_OUTPUT_TERMINAL, // bDescriptorSubtype
2256            0x03,                // bTerminalID
2257            0x01,
2258            0x03, // wTerminalType = 0x0301 (speaker)
2259            0x00, // bAssocTerminal
2260            0x02, // bSourceID (from unit 2)
2261            0x00, // iTerminal
2262        ];
2263        let terminal = parse_output_terminal(&data).unwrap();
2264        assert_eq!(terminal.terminal_id, 3);
2265        assert_eq!(terminal.terminal_type, 0x0301);
2266        assert_eq!(terminal.source_id, 2);
2267    }
2268
2269    #[test]
2270    fn test_parse_feature_unit() {
2271        let data = [
2272            10,               // bLength
2273            CS_INTERFACE,     // bDescriptorType
2274            UAC_FEATURE_UNIT, // bDescriptorSubtype
2275            0x02,             // bUnitID
2276            0x01,             // bSourceID
2277            0x01,             // bControlSize = 1
2278            0x03,             // bmaControls(0) master: mute + volume
2279            0x01,             // bmaControls(1) ch1: mute
2280            0x01,             // bmaControls(2) ch2: mute
2281            0x00,             // iFeature
2282        ];
2283        let fu = parse_feature_unit(&data).unwrap();
2284        assert_eq!(fu.unit_id, 2);
2285        assert_eq!(fu.source_id, 1);
2286        assert_eq!(fu.controls.len(), 3);
2287        assert!(fu.has_mute(0));
2288        assert!(fu.has_volume(0));
2289    }
2290
2291    #[test]
2292    fn test_parse_format_type_i_discrete() {
2293        let data = [
2294            14,                 // bLength
2295            CS_INTERFACE,       // bDescriptorType
2296            UAC_AS_FORMAT_TYPE, // bDescriptorSubtype
2297            0x01,               // bFormatType = FORMAT_TYPE_I
2298            0x02,               // bNrChannels
2299            0x02,               // bSubframeSize
2300            16,                 // bBitResolution
2301            0x02,               // bSamFreqType = 2 (discrete)
2302            0x44,
2303            0xAC,
2304            0x00, // tSamFreq[0] = 44100
2305            0x80,
2306            0xBB,
2307            0x00, // tSamFreq[1] = 48000
2308        ];
2309        let fmt = parse_format_type_i(&data).unwrap();
2310        assert_eq!(fmt.nr_channels, 2);
2311        assert_eq!(fmt.subframe_size, 2);
2312        assert_eq!(fmt.bit_resolution, 16);
2313        assert_eq!(fmt.sample_rates.len(), 2);
2314        assert!(fmt.supports_rate(44100));
2315        assert!(fmt.supports_rate(48000));
2316        assert!(!fmt.supports_rate(96000));
2317    }
2318
2319    #[test]
2320    fn test_parse_format_type_i_continuous() {
2321        let data = [
2322            14,
2323            CS_INTERFACE,
2324            UAC_AS_FORMAT_TYPE,
2325            0x01, // FORMAT_TYPE_I
2326            0x02, // 2 channels
2327            0x02, // 2 bytes/sample
2328            16,   // 16 bit
2329            0x00, // continuous
2330            0x40,
2331            0x1F,
2332            0x00, // lower = 8000
2333            0x00,
2334            0xEE,
2335            0x02, // upper = 192000
2336        ];
2337        let fmt = parse_format_type_i(&data).unwrap();
2338        assert_eq!(fmt.sample_rates.len(), 1);
2339        assert!(!fmt.sample_rates[0].is_discrete());
2340        assert!(fmt.supports_rate(44100));
2341        assert!(fmt.supports_rate(192000));
2342    }
2343
2344    // --- Sample rate request tests ---
2345
2346    #[test]
2347    fn test_sample_rate_request_bytes() {
2348        let req = build_set_sample_rate_request(0x01, 48000);
2349        let bytes = req.rate_bytes();
2350        assert_eq!(bytes, [0x80, 0xBB, 0x00]);
2351        assert_eq!(SampleRateRequest::decode_rate(&bytes), 48000);
2352    }
2353
2354    #[test]
2355    fn test_sample_rate_44100() {
2356        let req = build_set_sample_rate_request(0x01, 44100);
2357        let bytes = req.rate_bytes();
2358        assert_eq!(SampleRateRequest::decode_rate(&bytes), 44100);
2359    }
2360
2361    // --- Volume control request tests ---
2362
2363    #[test]
2364    fn test_volume_control_request() {
2365        let req = build_set_volume_request(2, 0, 0, VolumeDb::from_db(-10));
2366        assert_eq!(req.request, UAC_SET_CUR);
2367        let bytes = req.volume_bytes();
2368        let decoded = VolumeControlRequest::decode_volume(&bytes);
2369        assert_eq!(decoded.integer_db(), -10);
2370    }
2371
2372    // --- HDMI infoframe tests ---
2373
2374    #[test]
2375    fn test_hdmi_infoframe_stereo_pcm() {
2376        let frame = HdmiAudioInfoframe::stereo_pcm(48000, 16);
2377        assert_eq!(frame.coding_type, HdmiAudioCoding::Pcm);
2378        assert_eq!(frame.channel_count, 2);
2379        assert_eq!(frame.sample_rate, HdmiSampleRate::Rate48000);
2380        assert_eq!(frame.sample_size, HdmiSampleSize::Bits16);
2381        assert_eq!(frame.channel_allocation, HdmiChannelAllocation::Stereo);
2382    }
2383
2384    #[test]
2385    fn test_hdmi_infoframe_serialize_parse_roundtrip() {
2386        let original = HdmiAudioInfoframe::stereo_pcm(48000, 16);
2387        let bytes = original.to_bytes();
2388
2389        // Verify checksum
2390        assert!(HdmiAudioInfoframe::verify_checksum(&bytes));
2391
2392        // Verify type and version
2393        assert_eq!(bytes[0], HDMI_AUDIO_INFOFRAME_TYPE);
2394        assert_eq!(bytes[1], HDMI_AUDIO_INFOFRAME_VERSION);
2395
2396        // Parse back
2397        let parsed = HdmiAudioInfoframe::from_bytes(&bytes).unwrap();
2398        assert_eq!(parsed, original);
2399    }
2400
2401    #[test]
2402    fn test_hdmi_infoframe_51_roundtrip() {
2403        let original = HdmiAudioInfoframe::surround51_pcm(44100, 24);
2404        let bytes = original.to_bytes();
2405        assert!(HdmiAudioInfoframe::verify_checksum(&bytes));
2406        let parsed = HdmiAudioInfoframe::from_bytes(&bytes).unwrap();
2407        assert_eq!(parsed, original);
2408    }
2409
2410    #[test]
2411    fn test_hdmi_infoframe_bad_checksum() {
2412        let frame = HdmiAudioInfoframe::stereo_pcm(48000, 16);
2413        let mut bytes = frame.to_bytes();
2414        bytes[3] = bytes[3].wrapping_add(1); // Corrupt checksum
2415        assert!(!HdmiAudioInfoframe::verify_checksum(&bytes));
2416        assert!(HdmiAudioInfoframe::from_bytes(&bytes).is_err());
2417    }
2418
2419    // --- HDMI ACR tests ---
2420
2421    #[test]
2422    fn test_acr_recommended_n() {
2423        assert_eq!(AudioClockRegeneration::recommended_n(48000), 6144);
2424        assert_eq!(AudioClockRegeneration::recommended_n(44100), 6272);
2425        assert_eq!(AudioClockRegeneration::recommended_n(32000), 4096);
2426        assert_eq!(AudioClockRegeneration::recommended_n(96000), 12288);
2427        assert_eq!(AudioClockRegeneration::recommended_n(192000), 24576);
2428    }
2429
2430    #[test]
2431    fn test_acr_cts_calculation() {
2432        // For 48 kHz at 148.5 MHz TMDS:
2433        // CTS = N * TMDS / (128 * Fs) = 6144 * 148500000 / (128 * 48000)
2434        //     = 6144 * 148500 * 1000 / (128 * 48000) = 148500
2435        let cts = AudioClockRegeneration::calculate_cts(6144, 48000, 148500);
2436        assert_eq!(cts, 148500);
2437    }
2438
2439    #[test]
2440    fn test_acr_1080p60() {
2441        let acr = AudioClockRegeneration::for_1080p60(48000);
2442        assert_eq!(acr.n, 6144);
2443        assert_eq!(acr.cts, 148500);
2444    }
2445
2446    // --- HDMI channel allocation tests ---
2447
2448    #[test]
2449    fn test_channel_allocation_count() {
2450        assert_eq!(HdmiChannelAllocation::Stereo.channel_count(), 2);
2451        assert_eq!(HdmiChannelAllocation::Surround51.channel_count(), 6);
2452        assert_eq!(HdmiChannelAllocation::Surround71.channel_count(), 8);
2453    }
2454
2455    // --- Short Audio Descriptor tests ---
2456
2457    #[test]
2458    fn test_sad_from_bytes() {
2459        // PCM, 2 channels, 48 kHz + 44.1 kHz, 16-bit + 24-bit
2460        let bytes: [u8; 3] = [
2461            (1 << 3) | 0x01, // format=PCM, max_channels=2
2462            0x06,            // 44.1 kHz (bit 1) + 48 kHz (bit 2)
2463            0x05,            // 16-bit (bit 0) + 24-bit (bit 2)
2464        ];
2465        let sad = ShortAudioDescriptor::from_bytes(&bytes);
2466        assert_eq!(sad.format_code, 1);
2467        assert_eq!(sad.max_channels, 2);
2468        assert!(sad.is_pcm());
2469        assert!(sad.supports_rate(44100));
2470        assert!(sad.supports_rate(48000));
2471        assert!(!sad.supports_rate(96000));
2472        assert!(sad.supports_pcm_depth(16));
2473        assert!(!sad.supports_pcm_depth(20));
2474        assert!(sad.supports_pcm_depth(24));
2475    }
2476
2477    #[test]
2478    fn test_sad_roundtrip() {
2479        let original = ShortAudioDescriptor {
2480            format_code: 1,
2481            max_channels: 6,
2482            sample_rate_mask: 0x07, // 32k, 44.1k, 48k
2483            detail: 0x07,           // 16, 20, 24 bit
2484        };
2485        let bytes = original.to_bytes();
2486        let parsed = ShortAudioDescriptor::from_bytes(&bytes);
2487        assert_eq!(parsed.format_code, original.format_code);
2488        assert_eq!(parsed.max_channels, original.max_channels);
2489        assert_eq!(parsed.sample_rate_mask, original.sample_rate_mask);
2490        assert_eq!(parsed.detail, original.detail);
2491    }
2492
2493    // --- HdmiSampleRate tests ---
2494
2495    #[test]
2496    fn test_hdmi_sample_rate_from_hz() {
2497        assert_eq!(HdmiSampleRate::from_hz(48000), HdmiSampleRate::Rate48000);
2498        assert_eq!(HdmiSampleRate::from_hz(44100), HdmiSampleRate::Rate44100);
2499        assert_eq!(HdmiSampleRate::from_hz(192000), HdmiSampleRate::Rate192000);
2500        assert_eq!(HdmiSampleRate::from_hz(12345), HdmiSampleRate::StreamHeader);
2501    }
2502
2503    #[test]
2504    fn test_hdmi_sample_rate_to_hz() {
2505        assert_eq!(HdmiSampleRate::Rate48000.to_hz(), 48000);
2506        assert_eq!(HdmiSampleRate::Rate44100.to_hz(), 44100);
2507        assert_eq!(HdmiSampleRate::StreamHeader.to_hz(), 0);
2508    }
2509
2510    // --- Standard sample rates ---
2511
2512    #[test]
2513    fn test_standard_sample_rates() {
2514        assert!(is_standard_sample_rate(44100));
2515        assert!(is_standard_sample_rate(48000));
2516        assert!(is_standard_sample_rate(96000));
2517        assert!(!is_standard_sample_rate(12345));
2518    }
2519
2520    // --- UsbAudioDevice tests ---
2521
2522    #[test]
2523    fn test_usb_audio_device_find_units() {
2524        let mut dev = UsbAudioDevice::new(1, UacVersion::Uac10);
2525
2526        dev.units.push(AudioUnit::InputTerminal(InputTerminal {
2527            terminal_id: 1,
2528            terminal_type: UAC_TERMINAL_MICROPHONE,
2529            assoc_terminal: 0,
2530            nr_channels: 1,
2531            channel_config: 0x01,
2532        }));
2533
2534        dev.units.push(AudioUnit::FeatureUnit(FeatureUnit {
2535            unit_id: 2,
2536            source_id: 1,
2537            controls: vec![0x03],
2538        }));
2539
2540        dev.units.push(AudioUnit::OutputTerminal(OutputTerminal {
2541            terminal_id: 3,
2542            terminal_type: UAC_TERMINAL_SPEAKER,
2543            assoc_terminal: 0,
2544            source_id: 2,
2545        }));
2546
2547        assert!(dev.find_input_terminal(1).is_some());
2548        assert!(dev.find_input_terminal(99).is_none());
2549        assert!(dev.find_output_terminal(3).is_some());
2550        assert!(dev.find_feature_unit(2).is_some());
2551        assert_eq!(dev.feature_units().len(), 1);
2552        assert_eq!(dev.unit_count(), 3);
2553    }
2554
2555    // --- AudioUnit ID test ---
2556
2557    #[test]
2558    fn test_audio_unit_id() {
2559        let unit = AudioUnit::FeatureUnit(FeatureUnit {
2560            unit_id: 42,
2561            source_id: 1,
2562            controls: Vec::new(),
2563        });
2564        assert_eq!(unit.id(), 42);
2565
2566        let unit2 = AudioUnit::ClockSource(ClockSource {
2567            clock_id: 7,
2568            attributes: 0x01,
2569            assoc_terminal: 0,
2570        });
2571        assert_eq!(unit2.id(), 7);
2572    }
2573
2574    // --- Clock source tests ---
2575
2576    #[test]
2577    fn test_clock_source_attributes() {
2578        let clock = ClockSource {
2579            clock_id: 1,
2580            attributes: 0x03, // external + SOF synced
2581            assoc_terminal: 0,
2582        };
2583        assert!(clock.is_external());
2584        assert!(clock.is_sof_synced());
2585
2586        let internal = ClockSource {
2587            clock_id: 2,
2588            attributes: 0x00,
2589            assoc_terminal: 0,
2590        };
2591        assert!(!internal.is_external());
2592        assert!(!internal.is_sof_synced());
2593    }
2594
2595    // --- Streaming interface tests ---
2596
2597    #[test]
2598    fn test_streaming_interface_direction() {
2599        let output = AudioStreamingInterface {
2600            interface_num: 1,
2601            alternate_setting: 1,
2602            terminal_link: 1,
2603            format: AudioFormatDescriptor {
2604                format_tag: UAC_FORMAT_PCM,
2605                nr_channels: 2,
2606                subframe_size: 2,
2607                bit_resolution: 16,
2608                sample_rates: Vec::new(),
2609            },
2610            endpoint_address: 0x01, // OUT endpoint
2611            max_packet_size: 192,
2612            sync_type: IsoSyncType::Adaptive,
2613            sync_endpoint: 0,
2614        };
2615        assert!(output.is_output());
2616        assert!(!output.is_input());
2617
2618        let input = AudioStreamingInterface {
2619            endpoint_address: 0x82, // IN endpoint (bit 7 set)
2620            ..output.clone()
2621        };
2622        assert!(input.is_input());
2623        assert!(!input.is_output());
2624    }
2625
2626    #[test]
2627    fn test_streaming_bytes_per_frame() {
2628        let stream = AudioStreamingInterface {
2629            interface_num: 1,
2630            alternate_setting: 1,
2631            terminal_link: 1,
2632            format: AudioFormatDescriptor {
2633                format_tag: UAC_FORMAT_PCM,
2634                nr_channels: 2,
2635                subframe_size: 2,
2636                bit_resolution: 16,
2637                sample_rates: Vec::new(),
2638            },
2639            endpoint_address: 0x01,
2640            max_packet_size: 192,
2641            sync_type: IsoSyncType::Adaptive,
2642            sync_endpoint: 0,
2643        };
2644        // 48000 Hz * 4 bytes/frame / 1000 = 192 bytes per USB frame
2645        assert_eq!(stream.bytes_per_usb_frame(48000), 192);
2646        // 44100 Hz * 4 / 1000 = 176 (truncated)
2647        assert_eq!(stream.bytes_per_usb_frame(44100), 176);
2648    }
2649
2650    // --- HdmiAudioOutput tests ---
2651
2652    #[test]
2653    fn test_hdmi_audio_output_lifecycle() {
2654        let mut output = HdmiAudioOutput::new(0, 0x02, 0x05);
2655        assert!(!output.has_sink());
2656        assert!(!output.enabled);
2657
2658        output.enable();
2659        assert!(output.enabled);
2660        output.disable();
2661        assert!(!output.enabled);
2662    }
2663
2664    // --- Mute control request tests ---
2665
2666    #[test]
2667    fn test_mute_control_request() {
2668        let req = build_set_mute_request(2, 0, 0, true);
2669        assert_eq!(req.request, UAC_SET_CUR);
2670        assert!(req.muted);
2671        assert_eq!(req.value >> 8, UAC_FU_MUTE_CONTROL as u16);
2672    }
2673
2674    // --- read_u24_le tests ---
2675
2676    #[test]
2677    fn test_read_u24_le() {
2678        assert_eq!(read_u24_le(&[0x44, 0xAC, 0x00]), 44100);
2679        assert_eq!(read_u24_le(&[0x80, 0xBB, 0x00]), 48000);
2680        assert_eq!(read_u24_le(&[0x00, 0xEE, 0x02]), 192000);
2681    }
2682}