firewire_fireface_protocols/former/
ff400.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol defined by RME GmbH for Fireface 400.
5
6use super::*;
7
8/// The protocol implementation for Fireface 400.
9#[derive(Default, Debug)]
10pub struct Ff400Protocol;
11
12const MIXER_OFFSET: u64 = 0x000080080000;
13const OUTPUT_OFFSET: u64 = 0x000080080f80;
14const METER_OFFSET: u64 = 0x000080100000;
15const CFG_OFFSET: u64 = 0x000080100514;
16const STATUS_OFFSET: u64 = 0x0000801c0000;
17const AMP_OFFSET: u64 = 0x0000801c0180;
18
19// TODO: 12 quadlets are read at once for 6 octuple of timecode detected from line input 3.
20#[allow(dead_code)]
21const LTC_STATUS_OFFSET: usize = 0x0000801f0000;
22
23const AMP_MIC_IN_CH_OFFSET: u8 = 0;
24const AMP_LINE_IN_CH_OFFSET: u8 = 2;
25const AMP_OUT_CH_OFFSET: u8 = 4;
26
27impl RmeFfFormerSpecification for Ff400Protocol {
28    const ANALOG_INPUT_COUNT: usize = 8;
29    const SPDIF_INPUT_COUNT: usize = 2;
30    const ADAT_INPUT_COUNT: usize = 8;
31    const STREAM_INPUT_COUNT: usize = 18;
32
33    const ANALOG_OUTPUT_COUNT: usize = 8;
34    const SPDIF_OUTPUT_COUNT: usize = 2;
35    const ADAT_OUTPUT_COUNT: usize = 8;
36}
37
38impl RmeFfFormerMeterSpecification for Ff400Protocol {
39    const METER_OFFSET: u64 = METER_OFFSET;
40}
41
42fn write_amp_cmd(
43    req: &mut FwReq,
44    node: &mut FwNode,
45    ch: u8,
46    level: i8,
47    timeout_ms: u32,
48) -> Result<(), Error> {
49    let cmd = ((ch as u32) << 16) | ((level as u32) & 0xff);
50    let mut raw = [0; 4];
51    raw.copy_from_slice(&cmd.to_le_bytes());
52    req.transaction_sync(
53        node,
54        FwTcode::WriteQuadletRequest,
55        AMP_OFFSET,
56        raw.len(),
57        &mut raw,
58        timeout_ms,
59    )
60}
61
62/// Status of input gains of Fireface 400.
63#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
64pub struct Ff400InputGainStatus {
65    /// The level of gain for input 1 and 2. The value is between 0 and 65 by step 1 to represent
66    /// the range from 0 to 65 dB.
67    pub mic: [i8; 2],
68    /// The level of gain for input 3 and 4. The value is between 0 and 36 by step 1 to represent
69    /// the range from 0 to 18 dB.
70    pub line: [i8; 2],
71}
72
73const KNOB_IS_SIGNAL_LEVEL_FLAG: u32 = 0x04000000;
74const KNOB_IS_STEREO_PAIRED_FLAG: u32 = 0x02000000;
75const KNOB_IS_RIGHT_CHANNEL_FLAG: u32 = 0x08000000;
76
77const MIDI_PORT_0_FLAG: u32 = 0x00000100;
78const MIDI_PORT_0_BYTE_MASK: u32 = 0x000000ff;
79const MIDI_PORT_0_BYTE_SHIFT: usize = 0;
80const MIDI_PORT_1_FLAG: u32 = 0x01000000;
81const MIDI_PORT_1_BYTE_MASK: u32 = 0x00ff0000;
82const MIDI_PORT_1_BYTE_SHIFT: usize = 16;
83
84const KNOB_TARGET_MASK: u32 = 0xf0000000;
85const KNOB_TARGET_MIC_INPUT_PAIR_0: u32 = 0x00000000;
86const KNOB_TARGET_LINE_INPUT_PAIR_1: u32 = 0x10000000;
87const KNOB_TARGET_LINE_OUTPUT_PAIR_0: u32 = 0x20000000;
88const KNOB_TARGET_LINE_OUTPUT_PAIR_1: u32 = 0x30000000;
89const KNOB_TARGET_LINE_OUTPUT_PAIR_2: u32 = 0x40000000;
90const KNOB_TARGET_HP_OUTPUT_PAIR: u32 = 0x50000000;
91const KNOB_TARGET_SPDIF_OUTPUT_PAIR: u32 = 0x60000000;
92const KNOB_TARGET_ADAT_OUTPUT_PAIR_0: u32 = 0x70000000;
93const KNOB_TARGET_ADAT_OUTPUT_PAIR_1: u32 = 0x80000000;
94const KNOB_TARGET_ADAT_OUTPUT_PAIR_2: u32 = 0x90000000;
95const KNOB_TARGET_ADAT_OUTPUT_PAIR_3: u32 = 0xa0000000;
96
97const KNOB_SIGNAL_LEVEL_MASK: u32 = 0x00fffc00;
98const KNOB_SIGNAL_LEVEL_SHIFT: u32 = 10;
99
100/// The inbound MIDI message to physical port in Fireface 400.
101#[derive(Debug, Copy, Clone, PartialEq, Eq)]
102pub struct Ff400MidiMessage {
103    pub port: u8,
104    pub byte: u8,
105}
106
107impl Default for Ff400MidiMessage {
108    fn default() -> Self {
109        Self {
110            port: u8::MAX,
111            byte: u8::MAX,
112        }
113    }
114}
115
116/// Deserializer for messages from Fireface 400.
117pub trait Ff400MessageParse<T> {
118    /// Return false if no event is found. If found, deserialize parameters and return true.
119    fn parse_message(params: &mut T, message: u32) -> bool;
120}
121
122impl Ff400MessageParse<Ff400MidiMessage> for Ff400Protocol {
123    fn parse_message(params: &mut Ff400MidiMessage, message: u32) -> bool {
124        if message & KNOB_IS_SIGNAL_LEVEL_FLAG == 0 {
125            [
126                (
127                    MIDI_PORT_0_FLAG,
128                    MIDI_PORT_0_BYTE_MASK,
129                    MIDI_PORT_0_BYTE_SHIFT,
130                ),
131                (
132                    MIDI_PORT_1_FLAG,
133                    MIDI_PORT_1_BYTE_MASK,
134                    MIDI_PORT_1_BYTE_SHIFT,
135                ),
136            ]
137            .iter()
138            .enumerate()
139            .find(|(_, (flag, _, _))| message & flag > 0)
140            .map_or(false, |(p, (_, mask, shift))| {
141                params.port = p as u8;
142                params.byte = ((message & mask) >> shift) as u8;
143                true
144            })
145        } else {
146            false
147        }
148    }
149}
150
151impl Ff400MessageParse<Ff400InputGainStatus> for Ff400Protocol {
152    fn parse_message(params: &mut Ff400InputGainStatus, message: u32) -> bool {
153        if message & KNOB_IS_SIGNAL_LEVEL_FLAG > 0 {
154            let target = message & KNOB_TARGET_MASK;
155            [
156                (&mut params.mic[..], KNOB_TARGET_MIC_INPUT_PAIR_0),
157                (&mut params.line[..], KNOB_TARGET_LINE_INPUT_PAIR_1),
158            ]
159            .iter_mut()
160            .find(|(_, t)| target.eq(t))
161            .map_or(false, |(gains, _)| {
162                let val = ((message & KNOB_SIGNAL_LEVEL_MASK) >> KNOB_SIGNAL_LEVEL_SHIFT) as i8;
163                if message & KNOB_IS_STEREO_PAIRED_FLAG > 0 {
164                    gains.fill(val);
165                } else {
166                    let ch = if message & KNOB_IS_RIGHT_CHANNEL_FLAG > 0 {
167                        1
168                    } else {
169                        0
170                    };
171                    gains[ch] = val;
172                }
173                true
174            })
175        } else {
176            false
177        }
178    }
179}
180
181impl Ff400MessageParse<FormerOutputVolumeState> for Ff400Protocol {
182    fn parse_message(params: &mut FormerOutputVolumeState, message: u32) -> bool {
183        assert_eq!(
184            params.0.len(),
185            Self::ANALOG_OUTPUT_COUNT + Self::SPDIF_OUTPUT_COUNT + Self::ADAT_OUTPUT_COUNT
186        );
187
188        if message & KNOB_IS_SIGNAL_LEVEL_FLAG > 0 {
189            let target = message & KNOB_TARGET_MASK;
190
191            [
192                KNOB_TARGET_LINE_OUTPUT_PAIR_0,
193                KNOB_TARGET_LINE_OUTPUT_PAIR_1,
194                KNOB_TARGET_LINE_OUTPUT_PAIR_2,
195                KNOB_TARGET_HP_OUTPUT_PAIR,
196                KNOB_TARGET_SPDIF_OUTPUT_PAIR,
197                KNOB_TARGET_ADAT_OUTPUT_PAIR_0,
198                KNOB_TARGET_ADAT_OUTPUT_PAIR_1,
199                KNOB_TARGET_ADAT_OUTPUT_PAIR_2,
200                KNOB_TARGET_ADAT_OUTPUT_PAIR_3,
201            ]
202            .iter()
203            .position(|t| target.eq(t))
204            .map_or(false, |pos| {
205                let val = ((message & KNOB_SIGNAL_LEVEL_MASK) >> KNOB_SIGNAL_LEVEL_SHIFT) as i8;
206                let vol = amp_to_vol_value(val);
207                let mut ch = pos * 2;
208
209                if message & KNOB_IS_STEREO_PAIRED_FLAG > 0 {
210                    params.0[ch] = vol;
211                    params.0[ch + 1] = vol;
212                } else {
213                    if message & KNOB_IS_RIGHT_CHANNEL_FLAG > 0 {
214                        ch += 1;
215                    }
216                    params.0[ch] = vol;
217                }
218                true
219            })
220        } else {
221            false
222        }
223    }
224}
225
226impl Ff400Protocol {
227    /// The minimum value of gain for microphone input.
228    pub const MIC_INPUT_GAIN_MIN: i8 = 0;
229    /// The maximum value of gain for microphone input.
230    pub const MIC_INPUT_GAIN_MAX: i8 = 65;
231    /// The step value of gain for microphone input.
232    pub const MIC_INPUT_GAIN_STEP: i8 = 1;
233
234    /// The minimum value of gain for line input.
235    pub const LINE_INPUT_GAIN_MIN: i8 = 0;
236    /// The maximum value of gain for line input.
237    pub const LINE_INPUT_GAIN_MAX: i8 = 36;
238    /// The step value of gain for line input.
239    pub const LINE_INPUT_GAIN_STEP: i8 = 1;
240}
241
242impl RmeFfWhollyUpdatableParamsOperation<Ff400InputGainStatus> for Ff400Protocol {
243    fn update_wholly(
244        req: &mut FwReq,
245        node: &mut FwNode,
246        params: &Ff400InputGainStatus,
247        timeout_ms: u32,
248    ) -> Result<(), Error> {
249        [
250            (&params.mic, AMP_MIC_IN_CH_OFFSET),
251            (&params.line, AMP_LINE_IN_CH_OFFSET),
252        ]
253        .iter()
254        .try_for_each(|(gains, offset)| {
255            gains.iter().enumerate().try_for_each(|(i, &gain)| {
256                write_amp_cmd(req, node, offset + i as u8, gain, timeout_ms)
257            })
258        })
259    }
260}
261
262impl RmeFfPartiallyUpdatableParamsOperation<Ff400InputGainStatus> for Ff400Protocol {
263    fn update_partially(
264        req: &mut FwReq,
265        node: &mut FwNode,
266        params: &mut Ff400InputGainStatus,
267        update: Ff400InputGainStatus,
268        timeout_ms: u32,
269    ) -> Result<(), Error> {
270        [
271            (&mut params.mic, update.mic, AMP_MIC_IN_CH_OFFSET),
272            (&mut params.line, update.line, AMP_LINE_IN_CH_OFFSET),
273        ]
274        .iter_mut()
275        .try_for_each(|(states, changes, offset)| {
276            states
277                .iter_mut()
278                .zip(changes.iter())
279                .enumerate()
280                .filter(|(_, (s, c))| !s.eq(c))
281                .try_for_each(|(i, (s, c))| {
282                    write_amp_cmd(req, node, *offset + i as u8, *c, timeout_ms).map(|_| *s = *c)
283                })
284        })
285    }
286}
287
288const OUTPUT_AMP_MAX: i8 = 0x3f;
289
290// The value for amp value is between 0x3f to 0x00 by step 1 to represent -57 dB (=mute) to +6 dB.
291fn vol_to_amp_value(vol: i32) -> i8 {
292    ((OUTPUT_AMP_MAX as u64) * ((Ff400Protocol::VOL_MAX - vol) as u64)
293        / (Ff400Protocol::VOL_MAX as u64)) as i8
294}
295
296fn amp_to_vol_value(amp: i8) -> i32 {
297    ((Ff400Protocol::VOL_MAX as u64) * ((OUTPUT_AMP_MAX - amp) as u64) / (OUTPUT_AMP_MAX as u64))
298        as i32
299}
300
301fn write_output_amp_cmd(
302    req: &mut FwReq,
303    node: &mut FwNode,
304    ch: usize,
305    raw: &[u8],
306    timeout_ms: u32,
307) -> Result<(), Error> {
308    assert_eq!(raw.len(), 4);
309
310    let mut quadlet = [0; 4];
311    quadlet.copy_from_slice(&raw);
312    let vol = i32::from_le_bytes(quadlet);
313
314    let val = vol_to_amp_value(vol);
315    let amp_offset = AMP_OUT_CH_OFFSET + ch as u8;
316    write_amp_cmd(req, node, amp_offset, val, timeout_ms)
317}
318
319impl RmeFfWhollyUpdatableParamsOperation<FormerOutputVolumeState> for Ff400Protocol {
320    fn update_wholly(
321        req: &mut FwReq,
322        node: &mut FwNode,
323        params: &FormerOutputVolumeState,
324        timeout_ms: u32,
325    ) -> Result<(), Error> {
326        let mut raw = Self::serialize_offsets(params);
327        req.transaction_sync(
328            node,
329            FwTcode::WriteBlockRequest,
330            OUTPUT_OFFSET,
331            raw.len(),
332            &mut raw,
333            timeout_ms,
334        )?;
335
336        (0..Self::PHYS_OUTPUT_COUNT).try_for_each(|i| {
337            let pos = i * 4;
338            write_output_amp_cmd(req, node, i, &raw[pos..(pos + 4)], timeout_ms)
339        })
340    }
341}
342
343impl RmeFfPartiallyUpdatableParamsOperation<FormerOutputVolumeState> for Ff400Protocol {
344    fn update_partially(
345        req: &mut FwReq,
346        node: &mut FwNode,
347        params: &mut FormerOutputVolumeState,
348        update: FormerOutputVolumeState,
349        timeout_ms: u32,
350    ) -> Result<(), Error> {
351        let old = Self::serialize_offsets(params);
352        let mut new = Self::serialize_offsets(&update);
353
354        (0..(new.len() / 4))
355            .try_for_each(|i| {
356                let pos = i * 4;
357                if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
358                    req.transaction_sync(
359                        node,
360                        FwTcode::WriteBlockRequest,
361                        OUTPUT_OFFSET + pos as u64,
362                        4,
363                        &mut new[pos..(pos + 4)],
364                        timeout_ms,
365                    )
366                    .and_then(|_| {
367                        write_output_amp_cmd(req, node, i, &new[pos..(pos + 4)], timeout_ms)
368                    })
369                } else {
370                    Ok(())
371                }
372            })
373            .map(|_| *params = update)
374    }
375}
376
377impl RmeFormerMixerSpecification for Ff400Protocol {
378    const MIXER_OFFSET: u64 = MIXER_OFFSET;
379    const AVAIL_COUNT: usize = 18;
380}
381
382/// Signal source of sampling clock.
383#[derive(Debug, Copy, Clone, PartialEq, Eq)]
384pub enum Ff400ClkSrc {
385    Internal,
386    WordClock,
387    Adat,
388    Spdif,
389    Ltc,
390}
391
392impl Default for Ff400ClkSrc {
393    fn default() -> Self {
394        Self::Internal
395    }
396}
397
398// NOTE: for first quadlet of status quadlets.
399const Q0_SYNC_WORD_CLOCK_MASK: u32 = 0x40000000;
400const Q0_LOCK_WORD_CLOCK_MASK: u32 = 0x20000000;
401const Q0_EXT_CLK_RATE_MASK: u32 = 0x1e000000;
402const Q0_EXT_CLK_RATE_192000_FLAG: u32 = 0x12000000;
403const Q0_EXT_CLK_RATE_176400_FLAG: u32 = 0x10000000;
404const Q0_EXT_CLK_RATE_128000_FLAG: u32 = 0x0c000000;
405const Q0_EXT_CLK_RATE_96000_FLAG: u32 = 0x0e000000;
406const Q0_EXT_CLK_RATE_88200_FLAG: u32 = 0x0a000000;
407const Q0_EXT_CLK_RATE_64000_FLAG: u32 = 0x08000000;
408const Q0_EXT_CLK_RATE_48000_FLAG: u32 = 0x06000000;
409const Q0_EXT_CLK_RATE_44100_FLAG: u32 = 0x04000000;
410const Q0_EXT_CLK_RATE_32000_FLAG: u32 = 0x02000000;
411const Q0_ACTIVE_CLK_SRC_MASK: u32 = 0x01c00000;
412const Q0_ACTIVE_CLK_SRC_INTERNAL_FLAG: u32 = 0x01c00000;
413const Q0_ACTIVE_CLK_SRC_LTC_FLAG: u32 = 0x01400000;
414const Q0_ACTIVE_CLK_SRC_WORD_CLK_FLAG: u32 = 0x01000000;
415const Q0_ACTIVE_CLK_SRC_SPDIF_FLAG: u32 = 0x00c00000;
416const Q0_ACTIVE_CLK_SRC_ADAT_FLAG: u32 = 0x00000000;
417const Q0_SYNC_SPDIF_MASK: u32 = 0x00100000;
418const Q0_LOCK_SPDIF_MASK: u32 = 0x00040000;
419const Q0_SPDIF_RATE_MASK: u32 = 0x0003c000;
420const Q0_SPDIF_RATE_192000_FLAG: u32 = 0x00024000;
421const Q0_SPDIF_RATE_176400_FLAG: u32 = 0x00020000;
422const Q0_SPDIF_RATE_128000_FLAG: u32 = 0x0001c000;
423const Q0_SPDIF_RATE_96000_FLAG: u32 = 0x00018000;
424const Q0_SPDIF_RATE_88200_FLAG: u32 = 0x00014000;
425const Q0_SPDIF_RATE_64000_FLAG: u32 = 0x00010000;
426const Q0_SPDIF_RATE_48000_FLAG: u32 = 0x0000c000;
427const Q0_SPDIF_RATE_44100_FLAG: u32 = 0x00008000;
428const Q0_SPDIF_RATE_32000_FLAG: u32 = 0x00004000;
429const Q0_LOCK_ADAT_MASK: u32 = 0x00001000;
430const Q0_SYNC_ADAT_MASK: u32 = 0x00000400;
431
432// NOTE: for second quadlet of status quadlets.
433const Q1_WORD_OUT_SINGLE_MASK: u32 = 0x00002000;
434const Q1_CONF_CLK_SRC_MASK: u32 = 0x00001c01;
435const Q1_CONF_CLK_SRC_LTC_FLAG: u32 = 0x00001400;
436const Q1_CONF_CLK_SRC_WORD_CLK_FLAG: u32 = 0x00001000;
437const Q1_CONF_CLK_SRC_SPDIF_FLAG: u32 = 0x00000c00;
438const Q1_CONF_CLK_SRC_INTERNAL_FLAG: u32 = 0x00000001;
439const Q1_CONF_CLK_SRC_ADAT_FLAG: u32 = 0x00000000;
440const Q1_SPDIF_IN_IFACE_MASK: u32 = 0x00000200;
441const Q1_OPT_OUT_SIGNAL_MASK: u32 = 0x00000100;
442const Q1_SPDIF_OUT_EMPHASIS_MASK: u32 = 0x00000040;
443const Q1_SPDIF_OUT_FMT_MASK: u32 = 0x00000020;
444const Q1_CONF_CLK_RATE_MASK: u32 = 0x0000001e;
445const Q1_CONF_CLK_RATE_192000_FLAG: u32 = 0x00000016;
446const Q1_CONF_CLK_RATE_176400_FLAG: u32 = 0x00000010;
447const Q1_CONF_CLK_RATE_128000_FLAG: u32 = 0x00000012;
448const Q1_CONF_CLK_RATE_96000_FLAG: u32 = 0x0000000e;
449const Q1_CONF_CLK_RATE_88200_FLAG: u32 = 0x00000008;
450const Q1_CONF_CLK_RATE_64000_FLAG: u32 = 0x0000000a;
451const Q1_CONF_CLK_RATE_48000_FLAG: u32 = 0x00000006;
452const Q1_CONF_CLK_RATE_44100_FLAG: u32 = 0x00000000;
453const Q1_CONF_CLK_RATE_32000_FLAG: u32 = 0x00000002;
454
455/// Status of clock locking.
456#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
457pub struct Ff400ClkLockStatus {
458    pub adat: bool,
459    pub spdif: bool,
460    pub word_clock: bool,
461}
462
463impl Ff400ClkLockStatus {
464    const QUADLET_COUNT: usize = 1;
465}
466
467fn serialize_lock_status(status: &Ff400ClkLockStatus, quads: &mut [u32]) {
468    assert!(quads.len() >= Ff400ClkLockStatus::QUADLET_COUNT);
469
470    quads[0] &= !Q0_LOCK_ADAT_MASK;
471    if status.adat {
472        quads[0] |= Q0_LOCK_ADAT_MASK;
473    }
474
475    quads[0] &= !Q0_LOCK_SPDIF_MASK;
476    if status.spdif {
477        quads[0] |= Q0_LOCK_SPDIF_MASK;
478    }
479
480    quads[0] &= !Q0_LOCK_WORD_CLOCK_MASK;
481    if status.word_clock {
482        quads[0] |= Q0_LOCK_WORD_CLOCK_MASK;
483    }
484}
485
486fn deserialize_lock_status(status: &mut Ff400ClkLockStatus, quads: &[u32]) {
487    assert!(quads.len() >= Ff400ClkLockStatus::QUADLET_COUNT);
488
489    status.adat = quads[0] & Q0_LOCK_ADAT_MASK > 0;
490    status.spdif = quads[0] & Q0_LOCK_SPDIF_MASK > 0;
491    status.word_clock = quads[0] & Q0_LOCK_WORD_CLOCK_MASK > 0;
492}
493
494/// Status of clock synchronization.
495#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
496pub struct Ff400ClkSyncStatus {
497    pub adat: bool,
498    pub spdif: bool,
499    pub word_clock: bool,
500}
501
502impl Ff400ClkSyncStatus {
503    const QUADLET_COUNT: usize = 1;
504}
505
506fn serialize_sync_status(status: &Ff400ClkSyncStatus, quads: &mut [u32]) {
507    assert!(quads.len() >= Ff400ClkSyncStatus::QUADLET_COUNT);
508
509    quads[0] &= !Q0_SYNC_ADAT_MASK;
510    if status.adat {
511        quads[0] |= Q0_SYNC_ADAT_MASK;
512    }
513
514    quads[0] &= !Q0_SYNC_SPDIF_MASK;
515    if status.spdif {
516        quads[0] |= Q0_SYNC_SPDIF_MASK;
517    }
518
519    quads[0] &= !Q0_SYNC_WORD_CLOCK_MASK;
520    if status.word_clock {
521        quads[0] |= Q0_SYNC_WORD_CLOCK_MASK;
522    }
523}
524
525fn deserialize_sync_status(status: &mut Ff400ClkSyncStatus, quads: &[u32]) {
526    assert!(quads.len() >= Ff400ClkSyncStatus::QUADLET_COUNT);
527
528    status.adat = quads[0] & Q0_SYNC_ADAT_MASK > 0;
529    status.spdif = quads[0] & Q0_SYNC_SPDIF_MASK > 0;
530    status.word_clock = quads[0] & Q0_SYNC_WORD_CLOCK_MASK > 0;
531}
532
533/// Status of clock synchronization.
534#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
535pub struct Ff400Status {
536    /// For S/PDIF input.
537    pub spdif_in: SpdifInput,
538    /// For S/PDIF output.
539    pub spdif_out: FormerSpdifOutput,
540    /// The type of signal to optical output interface.
541    pub opt_out_signal: OpticalOutputSignal,
542    /// Whether to fix speed to single even if at double/quadruple rate.
543    pub word_out_single: bool,
544    /// For status of synchronization to external clocks.
545    pub sync: Ff400ClkSyncStatus,
546    /// For status of locking to external clocks.
547    pub lock: Ff400ClkLockStatus,
548
549    pub spdif_rate: Option<ClkNominalRate>,
550    pub active_clk_src: Ff400ClkSrc,
551    pub external_clk_rate: Option<ClkNominalRate>,
552    pub configured_clk_src: Ff400ClkSrc,
553    pub configured_clk_rate: ClkNominalRate,
554}
555
556impl Ff400Status {
557    const QUADLET_COUNT: usize = FORMER_STATUS_SIZE / 4;
558}
559
560impl RmeFfOffsetParamsSerialize<Ff400Status> for Ff400Protocol {
561    fn serialize_offsets(params: &Ff400Status) -> Vec<u8> {
562        let mut quads = [0; Ff400Status::QUADLET_COUNT];
563
564        serialize_lock_status(&params.lock, &mut quads);
565        serialize_sync_status(&params.sync, &mut quads);
566
567        quads[0] &= !Q0_SPDIF_RATE_MASK;
568        if let Some(rate) = &params.spdif_rate {
569            let flag = match rate {
570                ClkNominalRate::R32000 => Q0_SPDIF_RATE_32000_FLAG,
571                ClkNominalRate::R44100 => Q0_SPDIF_RATE_44100_FLAG,
572                ClkNominalRate::R48000 => Q0_SPDIF_RATE_48000_FLAG,
573                ClkNominalRate::R64000 => Q0_SPDIF_RATE_64000_FLAG,
574                ClkNominalRate::R88200 => Q0_SPDIF_RATE_88200_FLAG,
575                ClkNominalRate::R96000 => Q0_SPDIF_RATE_96000_FLAG,
576                ClkNominalRate::R128000 => Q0_SPDIF_RATE_128000_FLAG,
577                ClkNominalRate::R176400 => Q0_SPDIF_RATE_176400_FLAG,
578                ClkNominalRate::R192000 => Q0_SPDIF_RATE_192000_FLAG,
579            };
580            quads[0] |= flag;
581        }
582
583        quads[0] &= !Q0_ACTIVE_CLK_SRC_MASK;
584        let flag = match params.active_clk_src {
585            Ff400ClkSrc::Adat => Q0_ACTIVE_CLK_SRC_ADAT_FLAG,
586            Ff400ClkSrc::Spdif => Q0_ACTIVE_CLK_SRC_SPDIF_FLAG,
587            Ff400ClkSrc::WordClock => Q0_ACTIVE_CLK_SRC_WORD_CLK_FLAG,
588            Ff400ClkSrc::Ltc => Q0_ACTIVE_CLK_SRC_LTC_FLAG,
589            Ff400ClkSrc::Internal => Q0_ACTIVE_CLK_SRC_INTERNAL_FLAG,
590        };
591        quads[0] |= flag;
592
593        quads[0] &= !Q0_EXT_CLK_RATE_MASK;
594        if let Some(rate) = &params.external_clk_rate {
595            let flag = match rate {
596                ClkNominalRate::R32000 => Q0_EXT_CLK_RATE_32000_FLAG,
597                ClkNominalRate::R44100 => Q0_EXT_CLK_RATE_44100_FLAG,
598                ClkNominalRate::R48000 => Q0_EXT_CLK_RATE_48000_FLAG,
599                ClkNominalRate::R64000 => Q0_EXT_CLK_RATE_64000_FLAG,
600                ClkNominalRate::R88200 => Q0_EXT_CLK_RATE_88200_FLAG,
601                ClkNominalRate::R96000 => Q0_EXT_CLK_RATE_96000_FLAG,
602                ClkNominalRate::R128000 => Q0_EXT_CLK_RATE_128000_FLAG,
603                ClkNominalRate::R176400 => Q0_EXT_CLK_RATE_176400_FLAG,
604                ClkNominalRate::R192000 => Q0_EXT_CLK_RATE_192000_FLAG,
605            };
606            quads[0] |= flag;
607        }
608
609        quads[1] &= !Q1_SPDIF_IN_IFACE_MASK;
610        if params.spdif_in.iface == SpdifIface::Optical {
611            quads[1] |= Q1_SPDIF_IN_IFACE_MASK;
612        }
613
614        quads[1] &= !Q1_SPDIF_OUT_FMT_MASK;
615        if params.spdif_out.format == SpdifFormat::Professional {
616            quads[1] |= Q1_SPDIF_OUT_FMT_MASK;
617        }
618
619        quads[1] &= !Q1_SPDIF_OUT_EMPHASIS_MASK;
620        if params.spdif_out.emphasis {
621            quads[1] |= Q1_SPDIF_OUT_EMPHASIS_MASK;
622        }
623
624        quads[1] &= !Q1_OPT_OUT_SIGNAL_MASK;
625        if params.opt_out_signal == OpticalOutputSignal::Spdif {
626            quads[1] |= Q1_OPT_OUT_SIGNAL_MASK;
627        }
628
629        quads[1] &= !Q1_WORD_OUT_SINGLE_MASK;
630        if params.word_out_single {
631            quads[1] |= Q1_WORD_OUT_SINGLE_MASK;
632        }
633
634        quads[1] &= !Q1_CONF_CLK_SRC_MASK;
635        let flag = match params.configured_clk_src {
636            Ff400ClkSrc::Internal => Q1_CONF_CLK_SRC_INTERNAL_FLAG,
637            Ff400ClkSrc::Spdif => Q1_CONF_CLK_SRC_SPDIF_FLAG,
638            Ff400ClkSrc::WordClock => Q1_CONF_CLK_SRC_WORD_CLK_FLAG,
639            Ff400ClkSrc::Ltc => Q1_CONF_CLK_SRC_LTC_FLAG,
640            Ff400ClkSrc::Adat => Q1_CONF_CLK_SRC_ADAT_FLAG,
641        };
642        quads[1] |= flag;
643
644        quads[1] &= !Q1_CONF_CLK_RATE_MASK;
645        let flag = match params.configured_clk_rate {
646            ClkNominalRate::R32000 => Q1_CONF_CLK_RATE_32000_FLAG,
647            ClkNominalRate::R48000 => Q1_CONF_CLK_RATE_48000_FLAG,
648            ClkNominalRate::R64000 => Q1_CONF_CLK_RATE_64000_FLAG,
649            ClkNominalRate::R88200 => Q1_CONF_CLK_RATE_88200_FLAG,
650            ClkNominalRate::R96000 => Q1_CONF_CLK_RATE_96000_FLAG,
651            ClkNominalRate::R128000 => Q1_CONF_CLK_RATE_128000_FLAG,
652            ClkNominalRate::R176400 => Q1_CONF_CLK_RATE_176400_FLAG,
653            ClkNominalRate::R192000 => Q1_CONF_CLK_RATE_192000_FLAG,
654            ClkNominalRate::R44100 => Q1_CONF_CLK_RATE_44100_FLAG,
655        };
656        quads[1] |= flag;
657
658        quads.iter().flat_map(|quad| quad.to_le_bytes()).collect()
659    }
660}
661
662impl RmeFfOffsetParamsDeserialize<Ff400Status> for Ff400Protocol {
663    fn deserialize_offsets(params: &mut Ff400Status, raw: &[u8]) {
664        assert!(raw.len() >= FORMER_STATUS_SIZE);
665
666        let mut quads = [0; Ff400Status::QUADLET_COUNT];
667        let mut quadlet = [0; 4];
668        quads.iter_mut().enumerate().for_each(|(i, quad)| {
669            let pos = i * 4;
670            quadlet.copy_from_slice(&raw[pos..(pos + 4)]);
671            *quad = u32::from_le_bytes(quadlet);
672        });
673
674        deserialize_lock_status(&mut params.lock, &mut quads);
675        deserialize_sync_status(&mut params.sync, &mut quads);
676
677        params.spdif_rate = match quads[0] & Q0_SPDIF_RATE_MASK {
678            Q0_SPDIF_RATE_32000_FLAG => Some(ClkNominalRate::R32000),
679            Q0_SPDIF_RATE_44100_FLAG => Some(ClkNominalRate::R44100),
680            Q0_SPDIF_RATE_48000_FLAG => Some(ClkNominalRate::R48000),
681            Q0_SPDIF_RATE_64000_FLAG => Some(ClkNominalRate::R64000),
682            Q0_SPDIF_RATE_88200_FLAG => Some(ClkNominalRate::R88200),
683            Q0_SPDIF_RATE_96000_FLAG => Some(ClkNominalRate::R96000),
684            Q0_SPDIF_RATE_128000_FLAG => Some(ClkNominalRate::R128000),
685            Q0_SPDIF_RATE_176400_FLAG => Some(ClkNominalRate::R176400),
686            Q0_SPDIF_RATE_192000_FLAG => Some(ClkNominalRate::R192000),
687            _ => None,
688        };
689
690        params.active_clk_src = match quads[0] & Q0_ACTIVE_CLK_SRC_MASK {
691            Q0_ACTIVE_CLK_SRC_ADAT_FLAG => Ff400ClkSrc::Adat,
692            Q0_ACTIVE_CLK_SRC_SPDIF_FLAG => Ff400ClkSrc::Spdif,
693            Q0_ACTIVE_CLK_SRC_WORD_CLK_FLAG => Ff400ClkSrc::WordClock,
694            Q0_ACTIVE_CLK_SRC_LTC_FLAG => Ff400ClkSrc::Ltc,
695            Q0_ACTIVE_CLK_SRC_INTERNAL_FLAG => Ff400ClkSrc::Internal,
696            _ => unreachable!(),
697        };
698
699        params.external_clk_rate = match quads[0] & Q0_EXT_CLK_RATE_MASK {
700            Q0_EXT_CLK_RATE_32000_FLAG => Some(ClkNominalRate::R32000),
701            Q0_EXT_CLK_RATE_44100_FLAG => Some(ClkNominalRate::R44100),
702            Q0_EXT_CLK_RATE_48000_FLAG => Some(ClkNominalRate::R48000),
703            Q0_EXT_CLK_RATE_64000_FLAG => Some(ClkNominalRate::R64000),
704            Q0_EXT_CLK_RATE_88200_FLAG => Some(ClkNominalRate::R88200),
705            Q0_EXT_CLK_RATE_96000_FLAG => Some(ClkNominalRate::R96000),
706            Q0_EXT_CLK_RATE_128000_FLAG => Some(ClkNominalRate::R128000),
707            Q0_EXT_CLK_RATE_176400_FLAG => Some(ClkNominalRate::R176400),
708            Q0_EXT_CLK_RATE_192000_FLAG => Some(ClkNominalRate::R192000),
709            _ => None,
710        };
711
712        params.spdif_in.iface = if quads[1] & Q1_SPDIF_IN_IFACE_MASK > 0 {
713            SpdifIface::Optical
714        } else {
715            SpdifIface::Coaxial
716        };
717
718        params.spdif_out.format = if quads[1] & Q1_SPDIF_OUT_FMT_MASK > 0 {
719            SpdifFormat::Professional
720        } else {
721            SpdifFormat::Consumer
722        };
723
724        params.spdif_out.emphasis = quads[1] & Q1_SPDIF_OUT_EMPHASIS_MASK > 0;
725
726        params.opt_out_signal = if quads[1] & Q1_OPT_OUT_SIGNAL_MASK > 0 {
727            OpticalOutputSignal::Spdif
728        } else {
729            OpticalOutputSignal::Adat
730        };
731
732        params.word_out_single = quads[1] & Q1_WORD_OUT_SINGLE_MASK > 0;
733
734        params.configured_clk_src = match quads[1] & Q1_CONF_CLK_SRC_MASK {
735            Q1_CONF_CLK_SRC_INTERNAL_FLAG => Ff400ClkSrc::Internal,
736            Q1_CONF_CLK_SRC_SPDIF_FLAG => Ff400ClkSrc::Spdif,
737            Q1_CONF_CLK_SRC_WORD_CLK_FLAG => Ff400ClkSrc::WordClock,
738            Q1_CONF_CLK_SRC_LTC_FLAG => Ff400ClkSrc::Ltc,
739            Q1_CONF_CLK_SRC_ADAT_FLAG | _ => Ff400ClkSrc::Adat,
740        };
741
742        params.configured_clk_rate = match quads[1] & Q1_CONF_CLK_RATE_MASK {
743            Q1_CONF_CLK_RATE_32000_FLAG => ClkNominalRate::R32000,
744            Q1_CONF_CLK_RATE_48000_FLAG => ClkNominalRate::R48000,
745            Q1_CONF_CLK_RATE_64000_FLAG => ClkNominalRate::R64000,
746            Q1_CONF_CLK_RATE_88200_FLAG => ClkNominalRate::R88200,
747            Q1_CONF_CLK_RATE_96000_FLAG => ClkNominalRate::R96000,
748            Q1_CONF_CLK_RATE_128000_FLAG => ClkNominalRate::R128000,
749            Q1_CONF_CLK_RATE_176400_FLAG => ClkNominalRate::R176400,
750            Q1_CONF_CLK_RATE_192000_FLAG => ClkNominalRate::R192000,
751            Q1_CONF_CLK_RATE_44100_FLAG | _ => ClkNominalRate::R44100,
752        };
753    }
754}
755
756impl RmeFfCacheableParamsOperation<Ff400Status> for Ff400Protocol {
757    fn cache_wholly(
758        req: &mut FwReq,
759        node: &mut FwNode,
760        params: &mut Ff400Status,
761        timeout_ms: u32,
762    ) -> Result<(), Error> {
763        read_status::<Ff400Protocol, Ff400Status>(req, node, STATUS_OFFSET, params, timeout_ms)
764    }
765}
766
767// NOTE: for first quadlet of configuration quadlets.
768const Q0_HP_OUT_LEVEL_MASK: u32 = 0x00060000;
769const Q0_HP_OUT_LEVEL_HIGH_FLAG: u32 = 0x00040000;
770const Q0_HP_OUT_LEVEL_CON_FLAG: u32 = 0x00020000;
771const Q0_HP_OUT_LEVEL_PRO_FLAG: u32 = 0x00000000;
772const Q0_LINE_OUT_LEVEL_MASK: u32 = 0x00001c00;
773const Q0_LINE_OUT_LEVEL_CON_FLAG: u32 = 0x00001000;
774const Q0_LINE_OUT_LEVEL_PRO_FLAG: u32 = 0x00000800;
775const Q0_LINE_OUT_LEVEL_HIGH_FLAG: u32 = 0x00000400;
776const Q0_INPUT_2_INST_MASK: u32 = 0x00000200;
777const Q0_INPUT_2_PAD_MASK: u32 = 0x00000100;
778const Q0_INPUT_1_POWERING_MASK: u32 = 0x00000080;
779const Q0_LINE_IN_LEVEL_MASK: u32 = 0x00000038;
780const Q0_LINE_IN_LEVEL_CON_FLAG: u32 = 0x00000020;
781const Q0_LINE_IN_LEVEL_LOW_FLAG: u32 = 0x00000010;
782const Q0_LINE_IN_LEVEL_PRO_FLAG: u32 = 0x00000008;
783const Q0_INPUT_3_INST_MASK: u32 = 0x00000004;
784const Q0_INPUT_3_PAD_MASK: u32 = 0x00000002;
785const Q0_INPUT_0_POWERING_MASK: u32 = 0x00000001;
786
787// NOTE: for second quadlet of configuration quadlets.
788const Q1_LINE_OUT_LEVEL_MASK: u32 = 0x00000018;
789const Q1_LINE_OUT_LEVEL_PRO_FLAG: u32 = 0x00000018;
790const Q1_LINE_OUT_LEVEL_HIGH_FLAG: u32 = 0x00000010;
791const Q1_LINE_OUT_LEVEL_CON_FLAG: u32 = 0x00000008;
792const Q1_LINE_IN_LEVEL_MASK: u32 = 0x00000003;
793const Q1_LINE_IN_LEVEL_CON_FLAG: u32 = 0x00000003;
794const Q1_LINE_IN_LEVEL_PRO_FLAG: u32 = 0x00000002;
795const Q1_LINE_IN_LEVEL_LOW_FLAG: u32 = 0x00000000;
796
797// NOTE: for third quadlet of configuration quadlets.
798const Q2_CONTINUE_AT_ERRORS: u32 = 0x80000000;
799const Q2_SPDIF_IN_USE_PREEMBLE: u32 = 0x40000000;
800const Q2_MIDI_TX_LOW_OFFSET_MASK: u32 = 0x3c000000;
801const Q2_MIDI_TX_LOW_OFFSET_0180_FLAG: u32 = 0x20000000;
802const Q2_MIDI_TX_LOW_OFFSET_0100_FLAG: u32 = 0x10000000;
803const Q2_MIDI_TX_LOW_OFFSET_0080_FLAG: u32 = 0x08000000;
804const Q2_MIDI_TX_LOW_OFFSET_0000_FLAG: u32 = 0x04000000;
805const Q2_MIDI_TX_SUPPRESS_MASK: u32 = 0x03000000;
806const Q2_WORD_OUT_SINGLE_SPEED_MASK: u32 = 0x00002000;
807const Q2_CLK_SRC_MASK: u32 = 0x00001c01;
808const Q2_CLK_SRC_LTC_FLAG: u32 = 0x00001400;
809const Q2_CLK_SRC_WORD_CLK_FLAG: u32 = 0x00001000;
810const Q2_CLK_SRC_SPDIF_FLAG: u32 = 0x00000c00;
811const Q2_CLK_SRC_INTERNAL_FLAG: u32 = 0x00000001;
812const Q2_CLK_SRC_ADAT_FLAG: u32 = 0x00000000;
813const Q2_SPDIF_IN_IFACE_OPT_MASK: u32 = 0x00000200;
814const Q2_OPT_OUT_SIGNAL_MASK: u32 = 0x00000100;
815const Q2_SPDIF_OUT_NON_AUDIO_MASK: u32 = 0x00000080;
816const Q2_SPDIF_OUT_EMPHASIS_MASK: u32 = 0x00000040;
817const Q2_SPDIF_OUT_FMT_PRO_MASK: u32 = 0x00000020;
818const Q2_CLK_AVAIL_RATE_QUADRUPLE_MASK: u32 = 0x00000010;
819const Q2_CLK_AVAIL_RATE_DOUBLE_MASK: u32 = 0x00000008;
820const Q2_CLK_AVAIL_RATE_BASE_48000_MASK: u32 = 0x00000004;
821const Q2_CLK_AVAIL_RATE_BASE_44100_MASK: u32 = 0x00000002;
822
823/// Configurations of sampling clock.
824#[derive(Debug, Copy, Clone, PartialEq, Eq)]
825pub struct Ff400ClkConfig {
826    pub primary_src: Ff400ClkSrc,
827    avail_rate_44100: bool,
828    avail_rate_48000: bool,
829    avail_rate_double: bool,
830    avail_rate_quadruple: bool,
831}
832
833impl Default for Ff400ClkConfig {
834    fn default() -> Self {
835        Self {
836            primary_src: Ff400ClkSrc::default(),
837            avail_rate_44100: true,
838            avail_rate_48000: true,
839            avail_rate_double: true,
840            avail_rate_quadruple: true,
841        }
842    }
843}
844
845impl Ff400ClkConfig {
846    const QUADLET_COUNT: usize = 3;
847}
848
849fn serialize_clock_config(config: &Ff400ClkConfig, quads: &mut [u32]) {
850    assert!(quads.len() >= Ff400ClkConfig::QUADLET_COUNT);
851
852    quads[2] &= !Q2_CLK_SRC_MASK;
853    let flag = match config.primary_src {
854        Ff400ClkSrc::Internal => Q2_CLK_SRC_INTERNAL_FLAG,
855        Ff400ClkSrc::Ltc => Q2_CLK_SRC_LTC_FLAG,
856        Ff400ClkSrc::WordClock => Q2_CLK_SRC_WORD_CLK_FLAG,
857        Ff400ClkSrc::Adat => Q2_CLK_SRC_ADAT_FLAG,
858        Ff400ClkSrc::Spdif => Q2_CLK_SRC_SPDIF_FLAG,
859    };
860    quads[2] |= flag;
861
862    quads[2] &= !Q2_CLK_AVAIL_RATE_BASE_44100_MASK;
863    if config.avail_rate_44100 {
864        quads[2] |= Q2_CLK_AVAIL_RATE_BASE_44100_MASK;
865    }
866
867    quads[2] &= !Q2_CLK_AVAIL_RATE_BASE_48000_MASK;
868    if config.avail_rate_48000 {
869        quads[2] |= Q2_CLK_AVAIL_RATE_BASE_48000_MASK;
870    }
871
872    quads[2] &= !Q2_CLK_AVAIL_RATE_DOUBLE_MASK;
873    if config.avail_rate_double {
874        quads[2] |= Q2_CLK_AVAIL_RATE_DOUBLE_MASK;
875    }
876
877    quads[2] &= !Q2_CLK_AVAIL_RATE_QUADRUPLE_MASK;
878    if config.avail_rate_quadruple {
879        quads[2] |= Q2_CLK_AVAIL_RATE_QUADRUPLE_MASK;
880    }
881}
882
883fn deserialize_clock_config(config: &mut Ff400ClkConfig, quads: &[u32]) {
884    assert!(quads.len() >= Ff400ClkConfig::QUADLET_COUNT);
885
886    config.primary_src = match quads[2] & Q2_CLK_SRC_MASK {
887        Q2_CLK_SRC_INTERNAL_FLAG => Ff400ClkSrc::Internal,
888        Q2_CLK_SRC_LTC_FLAG => Ff400ClkSrc::Ltc,
889        Q2_CLK_SRC_WORD_CLK_FLAG => Ff400ClkSrc::WordClock,
890        Q2_CLK_SRC_SPDIF_FLAG => Ff400ClkSrc::Spdif,
891        Q2_CLK_SRC_ADAT_FLAG | _ => Ff400ClkSrc::Adat,
892    };
893
894    config.avail_rate_44100 = quads[2] & Q2_CLK_AVAIL_RATE_BASE_44100_MASK > 0;
895    config.avail_rate_48000 = quads[2] & Q2_CLK_AVAIL_RATE_BASE_48000_MASK > 0;
896    config.avail_rate_double = quads[2] & Q2_CLK_AVAIL_RATE_DOUBLE_MASK > 0;
897    config.avail_rate_quadruple = quads[2] & Q2_CLK_AVAIL_RATE_QUADRUPLE_MASK > 0;
898}
899
900/// Configuration for analog inputs.
901#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
902pub struct Ff400AnalogInConfig {
903    /// The nominal level of audio signal for input 5, 6, 7 and 8.
904    pub line_level: FormerLineInNominalLevel,
905    /// Whether to deliver +48 V powering for input 1 and 2.
906    pub phantom_powering: [bool; 2],
907    /// Whether to use input 3 and 4 for instrument.
908    pub insts: [bool; 2],
909    /// Whether to attenuate signal level from input 3 and 4.
910    pub pad: [bool; 2],
911}
912
913impl Ff400AnalogInConfig {
914    const QUADLET_COUNT: usize = 2;
915}
916
917fn serialize_analog_input_config(config: &Ff400AnalogInConfig, quads: &mut [u32]) {
918    assert!(quads.len() >= Ff400AnalogInConfig::QUADLET_COUNT);
919
920    quads[0] &= !Q0_LINE_IN_LEVEL_MASK;
921    quads[1] &= !Q1_LINE_IN_LEVEL_MASK;
922    match config.line_level {
923        FormerLineInNominalLevel::Low => {
924            quads[0] |= Q0_LINE_IN_LEVEL_LOW_FLAG;
925            quads[1] |= Q1_LINE_IN_LEVEL_LOW_FLAG;
926        }
927        FormerLineInNominalLevel::Consumer => {
928            quads[0] |= Q0_LINE_IN_LEVEL_CON_FLAG;
929            quads[1] |= Q1_LINE_IN_LEVEL_CON_FLAG;
930        }
931        FormerLineInNominalLevel::Professional => {
932            quads[0] |= Q0_LINE_IN_LEVEL_PRO_FLAG;
933            quads[1] |= Q1_LINE_IN_LEVEL_PRO_FLAG;
934        }
935    }
936
937    if config.phantom_powering[0] {
938        quads[0] |= Q0_INPUT_0_POWERING_MASK;
939    }
940    if config.phantom_powering[1] {
941        quads[0] |= Q0_INPUT_1_POWERING_MASK;
942    }
943
944    if config.insts[0] {
945        quads[0] |= Q0_INPUT_2_INST_MASK;
946    }
947    if config.insts[1] {
948        quads[0] |= Q0_INPUT_3_INST_MASK;
949    }
950
951    if config.pad[0] {
952        quads[0] |= Q0_INPUT_2_PAD_MASK;
953    }
954    if config.pad[1] {
955        quads[0] |= Q0_INPUT_3_PAD_MASK;
956    }
957}
958
959fn deserialize_analog_input_config(config: &mut Ff400AnalogInConfig, quads: &[u32]) {
960    assert!(quads.len() >= Ff400AnalogInConfig::QUADLET_COUNT);
961
962    let pair = (
963        quads[0] & Q0_LINE_IN_LEVEL_MASK,
964        quads[1] & Q1_LINE_IN_LEVEL_MASK,
965    );
966    config.line_level = match pair {
967        (Q0_LINE_IN_LEVEL_LOW_FLAG, Q1_LINE_IN_LEVEL_LOW_FLAG) => FormerLineInNominalLevel::Low,
968        (Q0_LINE_IN_LEVEL_CON_FLAG, Q1_LINE_IN_LEVEL_CON_FLAG) => {
969            FormerLineInNominalLevel::Consumer
970        }
971        (Q0_LINE_IN_LEVEL_PRO_FLAG, Q1_LINE_IN_LEVEL_PRO_FLAG) => {
972            FormerLineInNominalLevel::Professional
973        }
974        _ => unreachable!(),
975    };
976
977    config.phantom_powering[0] = quads[0] & Q0_INPUT_0_POWERING_MASK > 0;
978    config.phantom_powering[1] = quads[0] & Q0_INPUT_1_POWERING_MASK > 0;
979
980    config.insts[0] = quads[0] & Q0_INPUT_2_INST_MASK > 0;
981    config.insts[1] = quads[0] & Q0_INPUT_3_INST_MASK > 0;
982
983    config.pad[0] = quads[0] & Q0_INPUT_2_PAD_MASK > 0;
984    config.pad[1] = quads[0] & Q0_INPUT_3_PAD_MASK > 0;
985}
986
987/// Low offset of destination address for MIDI messages.
988#[derive(Debug, Clone, Copy, PartialEq, Eq)]
989#[allow(dead_code)]
990enum Ff400MidiTxLowOffset {
991    /// Between 0x0000 to 0x007c.
992    A0000,
993    /// Between 0x0080 to 0x00fc.
994    A0080,
995    /// Between 0x0100 to 0x017c.
996    A0100,
997    /// Between 0x0180 to 0x01fc.
998    A0180,
999}
1000
1001impl Default for Ff400MidiTxLowOffset {
1002    fn default() -> Self {
1003        Self::A0000
1004    }
1005}
1006
1007impl Ff400MidiTxLowOffset {
1008    const QUADLET_COUNT: usize = 3;
1009}
1010
1011fn serialize_midi_tx_low_offset(offset: &Ff400MidiTxLowOffset, quads: &mut [u32]) {
1012    assert!(quads.len() >= Ff400MidiTxLowOffset::QUADLET_COUNT);
1013
1014    quads[2] &= !Q2_MIDI_TX_LOW_OFFSET_MASK;
1015    quads[2] |= match offset {
1016        Ff400MidiTxLowOffset::A0000 => Q2_MIDI_TX_LOW_OFFSET_0000_FLAG,
1017        Ff400MidiTxLowOffset::A0080 => Q2_MIDI_TX_LOW_OFFSET_0080_FLAG,
1018        Ff400MidiTxLowOffset::A0100 => Q2_MIDI_TX_LOW_OFFSET_0100_FLAG,
1019        Ff400MidiTxLowOffset::A0180 => Q2_MIDI_TX_LOW_OFFSET_0180_FLAG,
1020    };
1021}
1022
1023fn deserialize_midi_tx_low_offset(offset: &mut Ff400MidiTxLowOffset, quads: &[u32]) {
1024    assert!(quads.len() >= Ff400MidiTxLowOffset::QUADLET_COUNT);
1025
1026    *offset = match quads[2] & Q2_MIDI_TX_LOW_OFFSET_MASK {
1027        Q2_MIDI_TX_LOW_OFFSET_0180_FLAG => Ff400MidiTxLowOffset::A0180,
1028        Q2_MIDI_TX_LOW_OFFSET_0100_FLAG => Ff400MidiTxLowOffset::A0100,
1029        Q2_MIDI_TX_LOW_OFFSET_0080_FLAG => Ff400MidiTxLowOffset::A0080,
1030        Q2_MIDI_TX_LOW_OFFSET_0000_FLAG => Ff400MidiTxLowOffset::A0000,
1031        _ => unreachable!(),
1032    }
1033}
1034
1035/// Configurations for Fireface 400.
1036#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1037pub struct Ff400Config {
1038    /// The low offset of destination address for MIDI messages.
1039    midi_tx_low_offset: Ff400MidiTxLowOffset,
1040    /// Whether to enable transaction for MIDI messages.
1041    midi_tx_enable: bool,
1042    /// For sampling clock.
1043    pub clk: Ff400ClkConfig,
1044    /// For analog inputs.
1045    pub analog_in: Ff400AnalogInConfig,
1046    /// The nominal level of audio signal for output 1, 2, 3, 4, 5, and 6.
1047    pub line_out_level: LineOutNominalLevel,
1048    /// The nominal level of audio signal for headphone output.
1049    pub hp_out_level: LineOutNominalLevel,
1050    /// For S/PDIF input.
1051    pub spdif_in: SpdifInput,
1052    /// For S/PDIF output.
1053    pub spdif_out: FormerSpdifOutput,
1054    /// The type of signal to optical output interface.
1055    pub opt_out_signal: OpticalOutputSignal,
1056    /// Whether to fix speed to single even if at double/quadruple rate.
1057    pub word_out_single: bool,
1058    /// Whether to continue audio processing against any synchronization corruption.
1059    continue_at_errors: bool,
1060}
1061
1062impl Default for Ff400Config {
1063    fn default() -> Self {
1064        Self {
1065            midi_tx_low_offset: Default::default(),
1066            midi_tx_enable: true,
1067            clk: Default::default(),
1068            analog_in: Default::default(),
1069            line_out_level: Default::default(),
1070            hp_out_level: Default::default(),
1071            spdif_in: Default::default(),
1072            spdif_out: Default::default(),
1073            opt_out_signal: Default::default(),
1074            word_out_single: Default::default(),
1075            continue_at_errors: true,
1076        }
1077    }
1078}
1079
1080impl Ff400Config {
1081    const QUADLET_COUNT: usize = FORMER_CONFIG_SIZE / 4;
1082
1083    /// Although the configuration registers are write-only, some of them are available in status
1084    /// registers.
1085    pub fn init(&mut self, status: &Ff400Status) {
1086        self.clk.primary_src = status.configured_clk_src;
1087        self.spdif_in = status.spdif_in;
1088        self.spdif_out = status.spdif_out;
1089        self.opt_out_signal = status.opt_out_signal;
1090        self.word_out_single = status.word_out_single;
1091    }
1092}
1093
1094impl RmeFfOffsetParamsSerialize<Ff400Config> for Ff400Protocol {
1095    fn serialize_offsets(params: &Ff400Config) -> Vec<u8> {
1096        let mut quads = [0; Ff400Config::QUADLET_COUNT];
1097
1098        serialize_midi_tx_low_offset(&params.midi_tx_low_offset, &mut quads);
1099
1100        quads[2] &= !Q2_MIDI_TX_SUPPRESS_MASK;
1101        if !params.midi_tx_enable {
1102            quads[2] |= Q2_MIDI_TX_SUPPRESS_MASK;
1103        }
1104
1105        serialize_clock_config(&params.clk, &mut quads);
1106        serialize_analog_input_config(&params.analog_in, &mut quads);
1107
1108        quads[0] &= !Q0_LINE_OUT_LEVEL_MASK;
1109        quads[1] &= !Q1_LINE_OUT_LEVEL_MASK;
1110        match &params.line_out_level {
1111            LineOutNominalLevel::High => {
1112                quads[0] |= Q0_LINE_OUT_LEVEL_HIGH_FLAG;
1113                quads[1] |= Q1_LINE_OUT_LEVEL_HIGH_FLAG;
1114            }
1115            LineOutNominalLevel::Consumer => {
1116                quads[0] |= Q0_LINE_OUT_LEVEL_CON_FLAG;
1117                quads[1] |= Q1_LINE_OUT_LEVEL_CON_FLAG;
1118            }
1119            LineOutNominalLevel::Professional => {
1120                quads[0] |= Q0_LINE_OUT_LEVEL_PRO_FLAG;
1121                quads[1] |= Q1_LINE_OUT_LEVEL_PRO_FLAG;
1122            }
1123        }
1124
1125        quads[0] &= !Q0_HP_OUT_LEVEL_MASK;
1126        match &params.hp_out_level {
1127            LineOutNominalLevel::High => {
1128                quads[0] |= Q0_HP_OUT_LEVEL_HIGH_FLAG;
1129            }
1130            LineOutNominalLevel::Consumer => {
1131                quads[0] |= Q0_HP_OUT_LEVEL_CON_FLAG;
1132            }
1133            LineOutNominalLevel::Professional => {
1134                quads[0] |= Q0_HP_OUT_LEVEL_PRO_FLAG;
1135            }
1136        }
1137
1138        if params.spdif_in.iface == SpdifIface::Optical {
1139            quads[2] |= Q2_SPDIF_IN_IFACE_OPT_MASK;
1140        }
1141        if params.spdif_in.use_preemble {
1142            quads[2] |= Q2_SPDIF_IN_USE_PREEMBLE;
1143        }
1144
1145        if params.opt_out_signal == OpticalOutputSignal::Spdif {
1146            quads[2] |= Q2_OPT_OUT_SIGNAL_MASK;
1147        }
1148        if params.spdif_out.format == SpdifFormat::Professional {
1149            quads[2] |= Q2_SPDIF_OUT_FMT_PRO_MASK;
1150        }
1151        if params.spdif_out.emphasis {
1152            quads[2] |= Q2_SPDIF_OUT_EMPHASIS_MASK;
1153        }
1154        if params.spdif_out.non_audio {
1155            quads[2] |= Q2_SPDIF_OUT_NON_AUDIO_MASK;
1156        }
1157
1158        if params.word_out_single {
1159            quads[2] |= Q2_WORD_OUT_SINGLE_SPEED_MASK;
1160        }
1161
1162        if params.continue_at_errors {
1163            quads[2] |= Q2_CONTINUE_AT_ERRORS;
1164        }
1165
1166        quads.iter().flat_map(|quad| quad.to_le_bytes()).collect()
1167    }
1168}
1169
1170impl RmeFfOffsetParamsDeserialize<Ff400Config> for Ff400Protocol {
1171    fn deserialize_offsets(params: &mut Ff400Config, raw: &[u8]) {
1172        assert!(raw.len() >= FORMER_CONFIG_SIZE);
1173
1174        let mut quads = [0; Ff400Config::QUADLET_COUNT];
1175        let mut quadlet = [0; 4];
1176        quads.iter_mut().enumerate().for_each(|(i, quad)| {
1177            let pos = i * 4;
1178            quadlet.copy_from_slice(&raw[pos..(pos + 4)]);
1179            *quad = u32::from_le_bytes(quadlet);
1180        });
1181
1182        deserialize_midi_tx_low_offset(&mut params.midi_tx_low_offset, &quads);
1183        params.midi_tx_enable = quads[2] & Q2_MIDI_TX_SUPPRESS_MASK == 0;
1184
1185        deserialize_clock_config(&mut params.clk, &quads);
1186        deserialize_analog_input_config(&mut params.analog_in, &quads);
1187
1188        let pair = (
1189            quads[0] & Q0_LINE_OUT_LEVEL_MASK,
1190            quads[1] & Q1_LINE_OUT_LEVEL_MASK,
1191        );
1192        params.line_out_level = match pair {
1193            (Q0_LINE_OUT_LEVEL_HIGH_FLAG, Q1_LINE_OUT_LEVEL_HIGH_FLAG) => LineOutNominalLevel::High,
1194            (Q0_LINE_OUT_LEVEL_CON_FLAG, Q1_LINE_OUT_LEVEL_CON_FLAG) => {
1195                LineOutNominalLevel::Consumer
1196            }
1197            (Q0_LINE_OUT_LEVEL_PRO_FLAG, Q1_LINE_OUT_LEVEL_PRO_FLAG) => {
1198                LineOutNominalLevel::Professional
1199            }
1200            _ => unreachable!(),
1201        };
1202
1203        params.hp_out_level = match quads[0] & Q0_HP_OUT_LEVEL_MASK {
1204            Q0_HP_OUT_LEVEL_HIGH_FLAG => LineOutNominalLevel::High,
1205            Q0_HP_OUT_LEVEL_CON_FLAG => LineOutNominalLevel::Consumer,
1206            Q0_HP_OUT_LEVEL_PRO_FLAG => LineOutNominalLevel::Professional,
1207            _ => unreachable!(),
1208        };
1209
1210        params.spdif_in.iface = if quads[2] & Q2_SPDIF_IN_IFACE_OPT_MASK > 0 {
1211            SpdifIface::Optical
1212        } else {
1213            SpdifIface::Coaxial
1214        };
1215        params.spdif_in.use_preemble = quads[2] & Q2_SPDIF_IN_USE_PREEMBLE > 0;
1216
1217        params.spdif_out.format = if quads[2] & Q2_SPDIF_OUT_FMT_PRO_MASK > 0 {
1218            SpdifFormat::Professional
1219        } else {
1220            SpdifFormat::Consumer
1221        };
1222        params.spdif_out.emphasis = quads[2] & Q2_SPDIF_OUT_EMPHASIS_MASK > 0;
1223        params.spdif_out.non_audio = quads[2] & Q2_SPDIF_OUT_NON_AUDIO_MASK > 0;
1224
1225        params.opt_out_signal = if quads[2] & Q2_OPT_OUT_SIGNAL_MASK > 0 {
1226            OpticalOutputSignal::Spdif
1227        } else {
1228            OpticalOutputSignal::Adat
1229        };
1230
1231        params.word_out_single = quads[2] & Q2_WORD_OUT_SINGLE_SPEED_MASK > 0;
1232        params.continue_at_errors = quads[2] & Q2_CONTINUE_AT_ERRORS > 0;
1233    }
1234}
1235
1236impl RmeFfWhollyUpdatableParamsOperation<Ff400Config> for Ff400Protocol {
1237    fn update_wholly(
1238        req: &mut FwReq,
1239        node: &mut FwNode,
1240        params: &Ff400Config,
1241        timeout_ms: u32,
1242    ) -> Result<(), Error> {
1243        write_config::<Ff400Protocol, Ff400Config>(req, node, CFG_OFFSET, params, timeout_ms)
1244    }
1245}
1246
1247#[cfg(test)]
1248mod test {
1249    use super::*;
1250
1251    #[test]
1252    fn lock_status_serdes() {
1253        let orig = Ff400ClkLockStatus {
1254            adat: true,
1255            spdif: true,
1256            word_clock: true,
1257        };
1258        let mut quads = [0; Ff400ClkLockStatus::QUADLET_COUNT];
1259        serialize_lock_status(&orig, &mut quads);
1260        let mut target = Ff400ClkLockStatus::default();
1261        deserialize_lock_status(&mut target, &quads);
1262
1263        assert_eq!(target, orig);
1264    }
1265
1266    #[test]
1267    fn sync_status_serdes() {
1268        let orig = Ff400ClkSyncStatus {
1269            adat: true,
1270            spdif: true,
1271            word_clock: true,
1272        };
1273        let mut quads = [0; Ff400ClkSyncStatus::QUADLET_COUNT];
1274        serialize_sync_status(&orig, &mut quads);
1275        let mut target = Ff400ClkSyncStatus::default();
1276        deserialize_sync_status(&mut target, &quads);
1277
1278        assert_eq!(target, orig);
1279    }
1280
1281    #[test]
1282    fn status_serdes() {
1283        let orig = Ff400Status {
1284            spdif_in: SpdifInput {
1285                iface: SpdifIface::Optical,
1286                // Not readable.
1287                use_preemble: false,
1288            },
1289            spdif_out: FormerSpdifOutput {
1290                format: SpdifFormat::Professional,
1291                emphasis: true,
1292                // Not readable.
1293                non_audio: false,
1294            },
1295            opt_out_signal: OpticalOutputSignal::Spdif,
1296            word_out_single: true,
1297            spdif_rate: Some(ClkNominalRate::R96000),
1298            active_clk_src: Ff400ClkSrc::Ltc,
1299            external_clk_rate: Some(ClkNominalRate::R88200),
1300            configured_clk_src: Ff400ClkSrc::Spdif,
1301            configured_clk_rate: ClkNominalRate::R176400,
1302            ..Default::default()
1303        };
1304        let raw = Ff400Protocol::serialize_offsets(&orig);
1305        let mut target = Ff400Status::default();
1306        Ff400Protocol::deserialize_offsets(&mut target, &raw);
1307
1308        assert_eq!(target, orig);
1309    }
1310
1311    #[test]
1312    fn clock_config_serdes() {
1313        let orig = Ff400ClkConfig {
1314            primary_src: Ff400ClkSrc::Adat,
1315            avail_rate_44100: true,
1316            avail_rate_48000: true,
1317            avail_rate_double: true,
1318            avail_rate_quadruple: true,
1319        };
1320        let mut quads = [0; Ff400ClkConfig::QUADLET_COUNT];
1321        serialize_clock_config(&orig, &mut quads);
1322        let mut target = Ff400ClkConfig::default();
1323        deserialize_clock_config(&mut target, &quads);
1324
1325        assert_eq!(target, orig);
1326    }
1327
1328    #[test]
1329    fn analog_input_config_serdes() {
1330        let orig = Ff400AnalogInConfig {
1331            line_level: FormerLineInNominalLevel::Professional,
1332            phantom_powering: [true; 2],
1333            insts: [true; 2],
1334            pad: [true; 2],
1335        };
1336        let mut quads = [0; Ff400AnalogInConfig::QUADLET_COUNT];
1337        serialize_analog_input_config(&orig, &mut quads);
1338        let mut target = Ff400AnalogInConfig::default();
1339        deserialize_analog_input_config(&mut target, &quads);
1340
1341        assert_eq!(target, orig);
1342    }
1343
1344    #[test]
1345    fn midi_tx_low_offset_serdes() {
1346        let orig = Ff400MidiTxLowOffset::A0180;
1347        let mut quads = [0; Ff400MidiTxLowOffset::QUADLET_COUNT];
1348        serialize_midi_tx_low_offset(&orig, &mut quads);
1349        let mut target = Ff400MidiTxLowOffset::default();
1350        deserialize_midi_tx_low_offset(&mut target, &quads);
1351
1352        assert_eq!(target, orig);
1353    }
1354
1355    #[test]
1356    fn config_serdes() {
1357        let orig = Ff400Config::default();
1358        let raw = Ff400Protocol::serialize_offsets(&orig);
1359        let mut target = Ff400Config::default();
1360        Ff400Protocol::deserialize_offsets(&mut target, &raw);
1361
1362        assert_eq!(target, orig);
1363    }
1364
1365    #[test]
1366    fn message_parse() {
1367        let expected = Ff400InputGainStatus {
1368            mic: [0, 0],
1369            line: [0x1c, 0x1c],
1370        };
1371        let msg = KNOB_IS_SIGNAL_LEVEL_FLAG
1372            | KNOB_IS_STEREO_PAIRED_FLAG
1373            | KNOB_TARGET_LINE_INPUT_PAIR_1
1374            | ((0x1c << KNOB_SIGNAL_LEVEL_SHIFT) & KNOB_SIGNAL_LEVEL_MASK);
1375        let mut target = Ff400InputGainStatus::default();
1376        let res = Ff400Protocol::parse_message(&mut target, msg);
1377        assert_eq!(res, true);
1378        assert_eq!(target, expected);
1379
1380        let expected = FormerOutputVolumeState(vec![
1381            0,
1382            0,
1383            0,
1384            0,
1385            0,
1386            0, // Analog.
1387            0,
1388            amp_to_vol_value(0x32), // Headphone.
1389            0,
1390            0, // S/PDIF.
1391            0,
1392            0,
1393            0,
1394            0,
1395            0,
1396            0,
1397            0,
1398            0, // ADAT.
1399        ]);
1400        let msg = KNOB_IS_SIGNAL_LEVEL_FLAG
1401            | KNOB_IS_RIGHT_CHANNEL_FLAG
1402            | KNOB_TARGET_HP_OUTPUT_PAIR
1403            | ((0x32 << KNOB_SIGNAL_LEVEL_SHIFT) & KNOB_SIGNAL_LEVEL_MASK);
1404        let mut target = FormerOutputVolumeState(vec![0; 18]);
1405        let res = Ff400Protocol::parse_message(&mut target, msg);
1406        assert_eq!(res, true);
1407        assert_eq!(target, expected);
1408
1409        let expected = Ff400MidiMessage {
1410            port: 1,
1411            byte: 0x5a,
1412        };
1413        let msg = MIDI_PORT_1_FLAG | ((0x5a << MIDI_PORT_1_BYTE_SHIFT) & MIDI_PORT_1_BYTE_MASK);
1414        let mut target = Ff400MidiMessage::default();
1415        let res = Ff400Protocol::parse_message(&mut target, msg);
1416        assert_eq!(res, true);
1417        assert_eq!(target, expected);
1418    }
1419}