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}