firewire_bebob_protocols/maudio/
pfl.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol implementation for M-Audio ProFire Lightbridge.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by M-Audio ProFire Lightbridge
8//!
9//! DM1500 is used for M-Audio ProFire Lightbridge.
10//!
11//! ## Diagram of internal signal flow
12//!
13//! ```text
14//! adat-input-1/2 ----> stream-output-1/2
15//! adat-input-3/4 ----> stream-output-3/4
16//! adat-input-5/6 ----> stream-output-5/6
17//! adat-input-7/8 ----> stream-output-7/8
18//! adat-input-9/10 ---> stream-output-9/10
19//! adat-input-11/12 --> stream-output-11/12
20//! adat-input-13/14 --> stream-output-13/14
21//! adat-input-15/16 --> stream-output-15/16
22//! adat-input-17/18 --> stream-output-17/18
23//! adat-input-19/20 --> stream-output-19/20
24//! adat-input-21/22 --> stream-output-21/22
25//! adat-input-23/24 --> stream-output-23/24
26//! adat-input-25/26 --> stream-output-25/26
27//! adat-input-27/28 --> stream-output-27/28
28//! adat-input-29/30 --> stream-output-29/30
29//! adat-input-31/32 --> stream-output-31/32
30//! spdif-input-1/2 ---> stream-output-33/34
31//!
32//! stream-input-1/2 ----> adat-output-1/2
33//! stream-input-3/4 ----> adat-output-3/4
34//! stream-input-5/6 ----> adat-output-5/6
35//! stream-input-7/8 ----> adat-output-7/8
36//! stream-input-9/10 ---> adat-output-9/10
37//! stream-input-11/12 --> adat-output-11/12
38//! stream-input-13/14 --> adat-output-13/14
39//! stream-input-15/16 --> adat-output-15/16
40//! stream-input-17/18 --> adat-output-17/18
41//! stream-input-19/20 --> adat-output-19/20
42//! stream-input-21/22 --> adat-output-21/22
43//! stream-input-23/24 --> adat-output-23/24
44//! stream-input-25/26 --> adat-output-25/26
45//! stream-input-27/28 --> adat-output-27/28
46//! stream-input-29/30 --> adat-output-29/30
47//! stream-input-31/32 --> adat-output-31/32
48//! stream-input-33/34 --> spdif-output-1/2
49//! stream-input-35/36 --> analog-output-1/2
50//! ```
51//!
52//! The protocol implementation for M-Audio ProFire Lightbridge was written with firmware version
53//! below:
54//!
55//! ```sh
56//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
57//! protocol:
58//!   version: 3
59//! bootloader:
60//!   timestamp: 2006-03-14T05:51:18+0000
61//!   version: 0.0.0
62//! hardware:
63//!   GUID: 0x00418b63000d6c08
64//!   model ID: 0x000008
65//!   revision: 0.0.0
66//! software:
67//!   timestamp: 2006-09-14T02:54:30+0000
68//!   ID: 0x000100a1
69//!   revision: 0.0.5747
70//! image:
71//!   base address: 0x400c0080
72//!   maximum size: 0x12fb24
73//! ```
74
75use super::*;
76
77/// The protocol implementation for media and sampling clock of ProFire Lightbridge.
78#[derive(Default, Debug)]
79pub struct PflClkProtocol;
80
81impl MediaClockFrequencyOperation for PflClkProtocol {
82    const FREQ_LIST: &'static [u32] = &[44100, 48000, 88200, 96000];
83}
84
85impl SamplingClockSourceOperation for PflClkProtocol {
86    const DST: SignalAddr = SignalAddr::Subunit(SignalSubunitAddr {
87        subunit: MUSIC_SUBUNIT_0,
88        plug_id: 0x07,
89    });
90
91    const SRC_LIST: &'static [SignalAddr] = &[
92        // Internal
93        SignalAddr::Subunit(SignalSubunitAddr {
94            subunit: MUSIC_SUBUNIT_0,
95            plug_id: 0x08,
96        }),
97        // S/PDIF
98        SignalAddr::Unit(SignalUnitAddr::Ext(0x01)),
99        // Optical iface 1
100        SignalAddr::Unit(SignalUnitAddr::Ext(0x02)),
101        // Optical iface 2
102        SignalAddr::Unit(SignalUnitAddr::Ext(0x03)),
103        // Optical iface 3
104        SignalAddr::Unit(SignalUnitAddr::Ext(0x04)),
105        // Optical iface 4
106        SignalAddr::Unit(SignalUnitAddr::Ext(0x05)),
107        // Word clock
108        SignalAddr::Unit(SignalUnitAddr::Ext(0x06)),
109    ];
110}
111
112/// The protocol implementation for meter information.
113#[derive(Default, Debug)]
114pub struct PflMeterProtocol;
115
116const METER_SIZE: usize = 56;
117
118/// The protocol implementation for input parameters.
119#[derive(Default, Debug)]
120pub struct PflInputParametersProtocol;
121
122/// Nominal frequency detected for any external input.
123#[derive(Debug, Copy, Clone, Eq, PartialEq)]
124pub enum PflDetectedInputFreq {
125    Unavailable,
126    R44100,
127    R48000,
128    R88200,
129    R96000,
130}
131
132impl Default for PflDetectedInputFreq {
133    fn default() -> Self {
134        Self::Unavailable
135    }
136}
137
138/// Information of hardware metering.
139#[derive(Debug, Copy, Clone, PartialEq, Eq)]
140pub struct PflMeterState {
141    /// Detected input frequency.
142    pub detected_input_freq: PflDetectedInputFreq,
143    /// Detected signal level of outputs.
144    pub phys_outputs: [i32; 2],
145    /// The status of sampling clock.
146    pub sync_status: bool,
147    cache: [u8; METER_SIZE],
148}
149
150impl Default for PflMeterState {
151    fn default() -> Self {
152        Self {
153            detected_input_freq: Default::default(),
154            phys_outputs: Default::default(),
155            sync_status: Default::default(),
156            cache: [0; METER_SIZE],
157        }
158    }
159}
160
161impl PflMeterProtocol {
162    /// The minimum value of detected signal level.
163    pub const METER_MIN: i32 = 0;
164    /// The maximum value of detected signal level.
165    pub const METER_MAX: i32 = 0x007fffff;
166    /// The step value of detected signal level.
167    pub const METER_STEP: i32 = 0x100;
168
169    /// Cache state of hardware for the parameters.
170    pub fn cache(
171        req: &FwReq,
172        node: &FwNode,
173        meter: &mut PflMeterState,
174        timeout_ms: u32,
175    ) -> Result<(), Error> {
176        let frame = &mut meter.cache;
177
178        req.transaction_sync(
179            node,
180            FwTcode::ReadBlockRequest,
181            DM_APPL_METER_OFFSET,
182            frame.len(),
183            frame,
184            timeout_ms,
185        )?;
186
187        let mut quadlet = [0; 4];
188
189        quadlet.copy_from_slice(&frame[..4]);
190        let val = u32::from_be_bytes(quadlet);
191        meter.detected_input_freq = match val {
192            4 => PflDetectedInputFreq::R96000,
193            3 => PflDetectedInputFreq::R88200,
194            2 => PflDetectedInputFreq::R48000,
195            1 => PflDetectedInputFreq::R44100,
196            _ => PflDetectedInputFreq::Unavailable,
197        };
198
199        meter
200            .phys_outputs
201            .iter_mut()
202            .enumerate()
203            .for_each(|(i, m)| {
204                let pos = 4 + i * 4;
205                quadlet.copy_from_slice(&frame[pos..(pos + 4)]);
206                *m = i32::from_be_bytes(quadlet);
207            });
208
209        quadlet.copy_from_slice(&frame[20..24]);
210        let val = u32::from_be_bytes(quadlet);
211        meter.sync_status = val != 2;
212
213        Ok(())
214    }
215}
216
217const CACHE_SIZE: usize = 24;
218
219/// Parameters of input configuration.
220#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
221pub struct PflInputParameters {
222    /// Whether to mute ADAT inputs.
223    pub adat_mute: [bool; 4],
224    /// Whether to mute S/PDIF inputs.
225    pub spdif_mute: bool,
226    /// Use ADAT S/MUX at higher sampling rate.
227    pub force_smux: bool,
228    cache: [u8; CACHE_SIZE],
229}
230
231impl PflInputParametersProtocol {
232    pub fn update(
233        req: &FwReq,
234        node: &FwNode,
235        params: &mut PflInputParameters,
236        timeout_ms: u32,
237    ) -> Result<(), Error> {
238        let cache = &mut params.cache;
239
240        params.adat_mute.iter().enumerate().for_each(|(i, m)| {
241            let pos = i * 4;
242            let val = *m as u32;
243            cache[pos..(pos + 4)].copy_from_slice(&val.to_be_bytes());
244        });
245
246        let val = params.spdif_mute as u32;
247        cache[16..20].copy_from_slice(&val.to_be_bytes());
248
249        let val = params.force_smux as u32;
250        cache[20..24].copy_from_slice(&val.to_be_bytes());
251
252        req.transaction_sync(
253            node,
254            hinawa::FwTcode::WriteBlockRequest,
255            DM_APPL_PARAM_OFFSET,
256            cache.len(),
257            cache,
258            timeout_ms,
259        )
260    }
261}