firewire_dice_protocols/focusrite/
liquids56.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol specific to Focusrite Liquid Saffire 56.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by Focusrite for Liquid Saffire 56.
8//!
9//! ## Diagram of internal signal flow for Liquid Saffire 56.
10//!
11//! I note that optical input interface is available exclusively for ADAT input and S/PDIF input.
12//!
13//! ```text
14//!
15//! XLR input 1 ------+---------+------------------> analog-input-1/2
16//! Phone input 1-----+         |
17//!                             |
18//! XLR input 2 ------+---------+
19//! Phone input 2 ----+
20//!
21//! XLR input 3 ------+---------+------------------> analog-input-3/4
22//! Phone input 3-----+         |
23//!                             |
24//! XLR input 4 ------+---------+
25//! Phone input 4 ----+
26//!
27//! XLR input 5 ------+---------+------------------> analog-input-5/6
28//! Phone input 5-----+         |
29//!                             |
30//! XLR input 6 ------+---------+
31//! Phone input 6 ----+
32//!
33//! XLR input 7 ------+---------+------------------> analog-input-7/8
34//! Phone input 7-----+         |
35//!                             |
36//! XLR input 8 ------+---------+
37//! Phone input 8 ----+
38//!
39//! Coaxial input 1/2 -----------------------------> spdif-input-1/2
40//! Optical input A -------------------------------> adat-input-1..8
41//! Optical input B ------------or-----------------> spdif-input-3/4
42//!                             +------------------> adat-input-9..16
43//!
44//!                          ++=============++
45//! analog-input-1/2 ------> ||   72 x 76   || ----> analog-output-1/2
46//! analog-input-3/4 ------> ||   router    || ----> analog-output-3/4
47//! analog-input-5/6 ------> ||   up to     || ----> analog-output-5/6
48//! analog-input-7/8 ------> || 128 entries || ----> analog-output-7/8
49//!                          ||             || ----> analog-output-9/10
50//! spdif-input-1/2 -------> ||             || ----> spdif-output-1/2
51//! spdif-input-3/4 -------> ||             || ----> spdif-output-3/4
52//! adat-input-1/2 --------> ||             || ----> adat-input-1/2
53//! adat-input-3/4 --------> ||             || ----> adat-input-3/4
54//! adat-input-5/6 --------> ||             || ----> adat-input-5/6
55//! adat-input-7/8 --------> ||             || ----> adat-input-7/8
56//! adat-input-9/10 -------> ||             || ----> adat-input-9/10
57//! adat-input-11/12 ------> ||             || ----> adat-input-11/12
58//! adat-input-13/14 ------> ||             || ----> adat-input-13/14
59//! adat-input-15/16 ------> ||             || ----> adat-input-15/16
60//!                          ||             ||
61//! stream-input-A-1/2 ----> ||             || ----> stream-output-A-1/2
62//! stream-input-A-3/4 ----> ||             || ----> stream-output-A-3/4
63//! stream-input-A-5/6 ----> ||             || ----> stream-output-A-5/6
64//! stream-input-A-7/8 ----> ||             || ----> stream-output-A-7/8
65//! stream-input-A-9/10 ---> ||             || ----> stream-output-A-9/10
66//! stream-input-A-11/12 --> ||             || ----> stream-output-A-11/12
67//! stream-input-A-13/14 --> ||             || ----> stream-output-A-13/14
68//! stream-input-A-15/16 --> ||             || ----> stream-output-A-15/16
69//!                          ||             ||
70//! stream-input-B-1/2 ----> ||             || ----> stream-output-B-1/2
71//! stream-input-B-3/4 ----> ||             || ----> stream-output-B-3/4
72//! stream-input-B-5/6 ----> ||             || ----> stream-output-B-5/6
73//! stream-input-B-7/8 ----> ||             || ----> stream-output-B-7/8
74//! stream-input-B-9/10 ---> ||             || ----> stream-output-B-9/10
75//! stream-input-B-11/12 --> ||             || ----> stream-output-B-11/12
76//!                          ||             ||
77//! mixer-output-1/2 ------> ||             || ----> mixer-input-1/2
78//! mixer-output-3/4 ------> ||             || ----> mixer-input-3/4
79//! mixer-output-5/6 ------> ||             || ----> mixer-input-5/6
80//! mixer-output-7/8 ------> ||             || ----> mixer-input-7/8
81//! mixer-output-9/10 -----> ||             || ----> mixer-input-9/10
82//! mixer-output-11/12 ----> ||             || ----> mixer-input-11/12
83//! mixer-output-13/14 ----> ||             || ----> mixer-input-13/14
84//! mixer-output-15/16 ----> ||             || ----> mixer-input-15/16
85//!                          ||             || ----> mixer-input-17/18
86//!                          ++=============++
87//!
88//!                          ++=============++
89//!                          ||             || ----> Phone output 1/2
90//!                          ||             || ----> Phone output 3/4
91//! analog-output-1/2 -----> ||             || ----> Phone output 5/6
92//! analog-output-3/4 -----> ||   output    ||
93//! analog-output-5/6 -----> ||             || --+-> Phone output 7/8
94//! analog-output-7/8 -----> ||   group     ||   +-> Headphone output 1/2
95//! analog-output-9/10 ----> ||             ||
96//!                          ||             || --+-> Phone output 9/10
97//!                          ||             ||   +-> Headphone output 3/4
98//!                          ||             ||
99//!                          ++=============++
100//!
101//! spdif-output-1/2 ------------------------------> Coaxial output 1/2
102//!
103//! adat-output-1..8 ------------------------------> Optical output A
104//!
105//! spdif-output-3/4 -------------or---------------> Optical output B
106//! adat-output-1..8 -------------+
107//!
108//! ```
109
110use super::{tcat::tcd22xx_spec::*, *};
111
112// const EMULATION_TYPE_OFFSET: usize = 0x0278;
113// const HARMONICS_OFFSET: usize = 0x0280;
114// const POLARITY_OFFSET: usize = 0x0288;
115// const METER_DISPLAY_TARGET_OFFSET: usize = 0x029c;
116// const ANALOG_INPUT_LEVEL_OFFSET: usize = 0x02b4;
117// const LED_OFFSET: usize = 0x02bc;
118
119/// Protocol implementation specific to Liquid Saffire 56.
120#[derive(Default, Debug)]
121pub struct LiquidS56Protocol;
122
123impl TcatOperation for LiquidS56Protocol {}
124
125impl TcatGlobalSectionSpecification for LiquidS56Protocol {}
126
127impl TcatExtensionOperation for LiquidS56Protocol {}
128
129impl Tcd22xxSpecification for LiquidS56Protocol {
130    const INPUTS: &'static [Input] = &[
131        Input {
132            id: SrcBlkId::Ins0,
133            offset: 0,
134            count: 2,
135            label: None,
136        },
137        Input {
138            id: SrcBlkId::Ins1,
139            offset: 2,
140            count: 6,
141            label: None,
142        },
143        Input {
144            id: SrcBlkId::Adat,
145            offset: 0,
146            count: 8,
147            label: None,
148        },
149        Input {
150            id: SrcBlkId::Aes,
151            offset: 0,
152            count: 2,
153            label: Some("S/PDIF-coax"),
154        },
155        // NOTE: share the same optical interface.
156        Input {
157            id: SrcBlkId::Adat,
158            offset: 8,
159            count: 8,
160            label: None,
161        },
162        Input {
163            id: SrcBlkId::Aes,
164            offset: 6,
165            count: 2,
166            label: Some("S/PDIF-opt"),
167        },
168    ];
169    const OUTPUTS: &'static [Output] = &[
170        Output {
171            id: DstBlkId::Ins0,
172            offset: 0,
173            count: 2,
174            label: None,
175        },
176        Output {
177            id: DstBlkId::Ins1,
178            offset: 0,
179            count: 8,
180            label: None,
181        },
182        Output {
183            id: DstBlkId::Adat,
184            offset: 0,
185            count: 8,
186            label: None,
187        },
188        Output {
189            id: DstBlkId::Aes,
190            offset: 0,
191            count: 2,
192            label: Some("S/PDIF-coax"),
193        },
194        // NOTE: share the same optical interface.
195        Output {
196            id: DstBlkId::Adat,
197            offset: 8,
198            count: 8,
199            label: None,
200        },
201        Output {
202            id: DstBlkId::Aes,
203            offset: 6,
204            count: 2,
205            label: Some("S/PDIF-opt"),
206        },
207    ];
208    // NOTE: The 8 entries are selected by unique protocol from the first 26 entries in router
209    // section are used to display hardware metering.
210    const FIXED: &'static [SrcBlk] = &[
211        SrcBlk {
212            id: SrcBlkId::Ins1,
213            ch: 0,
214        },
215        SrcBlk {
216            id: SrcBlkId::Ins1,
217            ch: 1,
218        },
219        SrcBlk {
220            id: SrcBlkId::Ins1,
221            ch: 2,
222        },
223        SrcBlk {
224            id: SrcBlkId::Ins1,
225            ch: 3,
226        },
227        SrcBlk {
228            id: SrcBlkId::Ins1,
229            ch: 4,
230        },
231        SrcBlk {
232            id: SrcBlkId::Ins1,
233            ch: 5,
234        },
235        SrcBlk {
236            id: SrcBlkId::Ins1,
237            ch: 6,
238        },
239        SrcBlk {
240            id: SrcBlkId::Ins1,
241            ch: 7,
242        },
243        SrcBlk {
244            id: SrcBlkId::Aes,
245            ch: 0,
246        },
247        SrcBlk {
248            id: SrcBlkId::Aes,
249            ch: 1,
250        },
251        SrcBlk {
252            id: SrcBlkId::Adat,
253            ch: 0,
254        },
255        SrcBlk {
256            id: SrcBlkId::Adat,
257            ch: 1,
258        },
259        SrcBlk {
260            id: SrcBlkId::Adat,
261            ch: 2,
262        },
263        SrcBlk {
264            id: SrcBlkId::Adat,
265            ch: 3,
266        },
267        SrcBlk {
268            id: SrcBlkId::Adat,
269            ch: 4,
270        },
271        SrcBlk {
272            id: SrcBlkId::Adat,
273            ch: 5,
274        },
275        SrcBlk {
276            id: SrcBlkId::Adat,
277            ch: 6,
278        },
279        SrcBlk {
280            id: SrcBlkId::Adat,
281            ch: 7,
282        },
283        SrcBlk {
284            id: SrcBlkId::Adat,
285            ch: 8,
286        },
287        SrcBlk {
288            id: SrcBlkId::Adat,
289            ch: 9,
290        },
291        SrcBlk {
292            id: SrcBlkId::Adat,
293            ch: 10,
294        },
295        SrcBlk {
296            id: SrcBlkId::Adat,
297            ch: 11,
298        },
299        SrcBlk {
300            id: SrcBlkId::Adat,
301            ch: 12,
302        },
303        SrcBlk {
304            id: SrcBlkId::Adat,
305            ch: 13,
306        },
307        SrcBlk {
308            id: SrcBlkId::Adat,
309            ch: 14,
310        },
311        SrcBlk {
312            id: SrcBlkId::Adat,
313            ch: 15,
314        },
315    ];
316}
317
318impl SaffireproSwNoticeOperation for LiquidS56Protocol {
319    const SW_NOTICE_OFFSET: usize = 0x02c8;
320}
321
322const SRC_SW_NOTICE: u32 = 0x00000001;
323const DIM_MUTE_SW_NOTICE: u32 = 0x00000003;
324const MIC_AMP_1_HARMONICS_SW_NOTICE: u32 = 0x00000006;
325const MIC_AMP_2_HARMONICS_SW_NOTICE: u32 = 0x00000007;
326const MIC_AMP_1_EMULATION_SW_NOTICE: u32 = 0x00000008;
327const MIC_AMP_2_EMULATION_SW_NOTICE: u32 = 0x00000009;
328const MIC_AMP_POLARITY_SW_NOTICE: u32 = 0x0000000a;
329const INPUT_LEVEL_SW_NOTICE: u32 = 0x0000000b;
330
331impl SaffireproOutGroupSpecification for LiquidS56Protocol {
332    const OUT_GROUP_STATE_OFFSET: usize = 0x000c;
333
334    const ENTRY_COUNT: usize = 10;
335    const HAS_VOL_HWCTL: bool = true;
336
337    const SRC_NOTICE: u32 = SRC_SW_NOTICE;
338    const DIM_MUTE_NOTICE: u32 = DIM_MUTE_SW_NOTICE;
339}
340
341impl SaffireproIoParamsSpecification for LiquidS56Protocol {
342    const AESEBU_IS_SUPPORTED: bool = true;
343    const MIC_PREAMP_TRANSFORMER_IS_SUPPORTED: bool = true;
344}
345
346/// Emulation type of mic pre amp.
347#[derive(Debug, Copy, Clone, PartialEq, Eq)]
348pub enum MicAmpEmulationType {
349    Flat,
350    Trany1h,
351    Silver2,
352    FfRed1h,
353    Savillerow,
354    Dunk,
355    ClassA2a,
356    OldTube,
357    Deutsch72,
358    Stellar1b,
359    NewAge,
360}
361
362impl Default for MicAmpEmulationType {
363    fn default() -> Self {
364        Self::Flat
365    }
366}
367
368impl MicAmpEmulationType {
369    const FLAT_VALUE: u32 = 0;
370    const TRANY1H_VALUE: u32 = 1;
371    const SILVER2_VALUE: u32 = 2;
372    const FFRED1H_VALUE: u32 = 3;
373    const SAVILLEROW_VALUE: u32 = 4;
374    const DUNK_VALUE: u32 = 5;
375    const CLASSA2A_VALUE: u32 = 6;
376    const OLDTUBE_VALUE: u32 = 7;
377    const DEUTSCH72_VALUE: u32 = 8;
378    const STELLAR1B_VALUE: u32 = 9;
379    const NEWAGE_VALUE: u32 = 10;
380}
381
382fn serialize_mic_amp_emulation_types(
383    emulation_types: &[MicAmpEmulationType; 2],
384    raw: &mut [u8],
385) -> Result<(), String> {
386    assert!(raw.len() >= 8);
387
388    emulation_types
389        .iter()
390        .enumerate()
391        .for_each(|(i, emulation_type)| {
392            let pos = i * 4;
393            let val = match emulation_type {
394                MicAmpEmulationType::Flat => MicAmpEmulationType::FLAT_VALUE,
395                MicAmpEmulationType::Trany1h => MicAmpEmulationType::TRANY1H_VALUE,
396                MicAmpEmulationType::Silver2 => MicAmpEmulationType::SILVER2_VALUE,
397                MicAmpEmulationType::FfRed1h => MicAmpEmulationType::FFRED1H_VALUE,
398                MicAmpEmulationType::Savillerow => MicAmpEmulationType::SAVILLEROW_VALUE,
399                MicAmpEmulationType::Dunk => MicAmpEmulationType::DUNK_VALUE,
400                MicAmpEmulationType::ClassA2a => MicAmpEmulationType::CLASSA2A_VALUE,
401                MicAmpEmulationType::OldTube => MicAmpEmulationType::OLDTUBE_VALUE,
402                MicAmpEmulationType::Deutsch72 => MicAmpEmulationType::DEUTSCH72_VALUE,
403                MicAmpEmulationType::Stellar1b => MicAmpEmulationType::STELLAR1B_VALUE,
404                MicAmpEmulationType::NewAge => MicAmpEmulationType::NEWAGE_VALUE,
405            };
406            serialize_u32(&val, &mut raw[pos..(pos + 4)]);
407        });
408
409    Ok(())
410}
411
412fn deserialize_mic_amp_emulation_types(
413    emulation_types: &mut [MicAmpEmulationType; 2],
414    raw: &[u8],
415) -> Result<(), String> {
416    assert!(raw.len() >= 8);
417
418    let mut val = 0u32;
419
420    emulation_types
421        .iter_mut()
422        .enumerate()
423        .try_for_each(|(i, emulation_type)| {
424            let pos = i * 4;
425            deserialize_u32(&mut val, &raw[pos..(pos + 4)]);
426
427            *emulation_type = match val {
428                MicAmpEmulationType::FLAT_VALUE => MicAmpEmulationType::Flat,
429                MicAmpEmulationType::TRANY1H_VALUE => MicAmpEmulationType::Trany1h,
430                MicAmpEmulationType::SILVER2_VALUE => MicAmpEmulationType::Silver2,
431                MicAmpEmulationType::FFRED1H_VALUE => MicAmpEmulationType::FfRed1h,
432                MicAmpEmulationType::SAVILLEROW_VALUE => MicAmpEmulationType::Savillerow,
433                MicAmpEmulationType::DUNK_VALUE => MicAmpEmulationType::Dunk,
434                MicAmpEmulationType::CLASSA2A_VALUE => MicAmpEmulationType::ClassA2a,
435                MicAmpEmulationType::OLDTUBE_VALUE => MicAmpEmulationType::OldTube,
436                MicAmpEmulationType::DEUTSCH72_VALUE => MicAmpEmulationType::Deutsch72,
437                MicAmpEmulationType::STELLAR1B_VALUE => MicAmpEmulationType::Stellar1b,
438                MicAmpEmulationType::NEWAGE_VALUE => MicAmpEmulationType::NewAge,
439                _ => Err(format!(
440                    "Mic amplifier emulation type not found for value: {}",
441                    val
442                ))?,
443            };
444
445            Ok(())
446        })
447}
448
449fn serialize_mic_amp_harmonics(harmonics: &[u8; 2], raw: &mut [u8]) -> Result<(), String> {
450    assert!(raw.len() >= 8);
451
452    harmonics.iter().enumerate().for_each(|(i, h)| {
453        let pos = i * 4;
454        serialize_u8(h, &mut raw[pos..(pos + 4)]);
455    });
456
457    Ok(())
458}
459
460fn deserialize_mic_amp_harmonics(harmonics: &mut [u8; 2], raw: &[u8]) -> Result<(), String> {
461    assert!(raw.len() >= 8);
462
463    harmonics.iter_mut().enumerate().for_each(|(i, h)| {
464        let pos = i * 4;
465        deserialize_u8(h, &raw[pos..(pos + 4)]);
466    });
467
468    Ok(())
469}
470
471fn serialize_mic_amp_polarities(polarities: &[bool; 2], raw: &mut [u8]) -> Result<(), String> {
472    assert!(raw.len() >= 8);
473
474    polarities.iter().enumerate().for_each(|(i, polarity)| {
475        let pos = i * 4;
476        serialize_bool(polarity, &mut raw[pos..(pos + 4)]);
477    });
478
479    Ok(())
480}
481
482fn deserialize_mic_amp_polarities(polarities: &mut [bool; 2], raw: &[u8]) -> Result<(), String> {
483    assert!(raw.len() >= 8);
484
485    polarities.iter_mut().enumerate().for_each(|(i, polarity)| {
486        let pos = i * 4;
487        deserialize_bool(polarity, &raw[pos..(pos + 4)]);
488    });
489
490    Ok(())
491}
492
493/// Level of analog input.
494#[derive(Debug, Copy, Clone, PartialEq, Eq)]
495pub enum AnalogInputLevel {
496    Line,
497    Mic,
498    /// Available for Analog input 3 and 4 only.
499    Inst,
500}
501
502impl Default for AnalogInputLevel {
503    fn default() -> Self {
504        Self::Line
505    }
506}
507
508impl AnalogInputLevel {
509    const LINE_VALUE: u8 = 0;
510    const MIC_VALUE: u8 = 1;
511    const INST_VALUE: u8 = 2;
512}
513
514fn serialize_analog_input_levels(
515    levels: &[AnalogInputLevel; 8],
516    raw: &mut [u8],
517) -> Result<(), String> {
518    assert!(raw.len() >= 8);
519
520    (0..levels.len()).step_by(4).for_each(|pos| {
521        let mut val = 0u32;
522        levels[pos..(pos + 4)]
523            .iter()
524            .enumerate()
525            .for_each(|(j, level)| {
526                let v = match level {
527                    AnalogInputLevel::Line => AnalogInputLevel::LINE_VALUE,
528                    AnalogInputLevel::Mic => AnalogInputLevel::MIC_VALUE,
529                    AnalogInputLevel::Inst => AnalogInputLevel::INST_VALUE,
530                };
531                val |= (v as u32) << (j * 8);
532            });
533        serialize_u32(&val, &mut raw[pos..(pos + 4)]);
534    });
535
536    Ok(())
537}
538
539fn deserialize_analog_input_levels(
540    levels: &mut [AnalogInputLevel; 8],
541    raw: &[u8],
542) -> Result<(), String> {
543    assert!(raw.len() >= 8);
544
545    let mut val = 0u32;
546
547    (0..levels.len()).step_by(4).try_for_each(|pos| {
548        deserialize_u32(&mut val, &raw[pos..(pos + 4)]);
549
550        levels[pos..(pos + 4)]
551            .iter_mut()
552            .enumerate()
553            .try_for_each(|(j, level)| {
554                let v = ((val >> (j * 8)) & 0x000000ff) as u8;
555                *level = match v {
556                    AnalogInputLevel::LINE_VALUE => AnalogInputLevel::Line,
557                    AnalogInputLevel::MIC_VALUE => AnalogInputLevel::Mic,
558                    AnalogInputLevel::INST_VALUE => AnalogInputLevel::Inst,
559                    _ => Err(format!("Analog input level not found for value: {}", val))?,
560                };
561                Ok(())
562            })
563    })
564}
565
566/// Target of meter display.
567#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
568pub struct LedState {
569    /// For `ADAT 1` LED.
570    pub adat1: bool,
571    /// For `ADAT 2` LED.
572    pub adat2: bool,
573    /// For SPDIF LED.
574    pub spdif: bool,
575    /// For `MIDI In` LED.
576    pub midi_in: bool,
577}
578
579impl LedState {
580    const ADAT1_FLAG: u32 = 0x00000001;
581    const ADAT2_FLAG: u32 = 0x00000002;
582    const SPDIF_FLAG: u32 = 0x00000004;
583    const MIDI_IN_FLAG: u32 = 0x00000008;
584}
585
586fn serialize_led_state(state: &LedState, raw: &mut [u8]) -> Result<(), String> {
587    assert!(raw.len() >= 4);
588
589    let mut val = 0u32;
590
591    [
592        (&state.adat1, LedState::ADAT1_FLAG),
593        (&state.adat2, LedState::ADAT2_FLAG),
594        (&state.spdif, LedState::SPDIF_FLAG),
595        (&state.midi_in, LedState::MIDI_IN_FLAG),
596    ]
597    .iter_mut()
598    .filter(|(&on, _)| on)
599    .for_each(|(_, flag)| val |= *flag);
600
601    serialize_u32(&val, raw);
602
603    Ok(())
604}
605
606fn deserialize_led_state(state: &mut LedState, raw: &[u8]) -> Result<(), String> {
607    assert!(raw.len() >= 4);
608
609    let mut val = 0u32;
610    deserialize_u32(&mut val, raw);
611
612    [
613        (&mut state.adat1, LedState::ADAT1_FLAG),
614        (&mut state.adat2, LedState::ADAT2_FLAG),
615        (&mut state.spdif, LedState::SPDIF_FLAG),
616        (&mut state.midi_in, LedState::MIDI_IN_FLAG),
617    ]
618    .iter_mut()
619    .for_each(|(on, flag)| **on = val & *flag > 0);
620
621    Ok(())
622}
623
624/// The target to display meter.
625#[derive(Debug, Copy, Clone, PartialEq, Eq)]
626pub enum MeterDisplayTarget {
627    AnalogInput0,
628    AnalogInput1,
629    AnalogInput2,
630    AnalogInput3,
631    AnalogInput4,
632    AnalogInput5,
633    AnalogInput6,
634    AnalogInput7,
635    SpdifInput0,
636    SpdifInput1,
637    AdatInput0,
638    AdatInput1,
639    AdatInput2,
640    AdatInput3,
641    AdatInput4,
642    AdatInput5,
643    AdatInput6,
644    AdatInput7,
645    AdatInput8,
646    AdatInput9,
647    AdatInput10,
648    AdatInput11,
649    AdatInput12,
650    AdatInput13,
651    AdatInput14,
652    AdatInput15,
653}
654
655impl Default for MeterDisplayTarget {
656    fn default() -> Self {
657        MeterDisplayTarget::AnalogInput0
658    }
659}
660
661const METER_DISPLAY_TARGETS: &[MeterDisplayTarget] = &[
662    MeterDisplayTarget::AnalogInput0,
663    MeterDisplayTarget::AnalogInput1,
664    MeterDisplayTarget::AnalogInput2,
665    MeterDisplayTarget::AnalogInput3,
666    MeterDisplayTarget::AnalogInput4,
667    MeterDisplayTarget::AnalogInput5,
668    MeterDisplayTarget::AnalogInput6,
669    MeterDisplayTarget::AnalogInput7,
670    MeterDisplayTarget::SpdifInput0,
671    MeterDisplayTarget::SpdifInput1,
672    MeterDisplayTarget::AdatInput0,
673    MeterDisplayTarget::AdatInput1,
674    MeterDisplayTarget::AdatInput2,
675    MeterDisplayTarget::AdatInput3,
676    MeterDisplayTarget::AdatInput4,
677    MeterDisplayTarget::AdatInput5,
678    MeterDisplayTarget::AdatInput6,
679    MeterDisplayTarget::AdatInput7,
680    MeterDisplayTarget::AdatInput8,
681    MeterDisplayTarget::AdatInput9,
682    MeterDisplayTarget::AdatInput10,
683    MeterDisplayTarget::AdatInput11,
684    MeterDisplayTarget::AdatInput12,
685    MeterDisplayTarget::AdatInput13,
686    MeterDisplayTarget::AdatInput14,
687    MeterDisplayTarget::AdatInput15,
688];
689
690fn serialize_meter_display_targets(
691    targets: &[MeterDisplayTarget; 8],
692    raw: &mut [u8],
693) -> Result<(), String> {
694    assert!(raw.len() >= 8);
695
696    (0..targets.len()).step_by(4).for_each(|pos| {
697        let mut val = 0u32;
698
699        targets[pos..(pos + 4)]
700            .iter()
701            .enumerate()
702            .for_each(|(j, target)| {
703                let pos = METER_DISPLAY_TARGETS
704                    .iter()
705                    .position(|t| target.eq(t))
706                    .unwrap();
707                val |= (pos as u32) << (j * 8);
708            });
709
710        serialize_u32(&val, &mut raw[pos..(pos + 4)]);
711    });
712
713    Ok(())
714}
715
716fn deserialize_meter_display_targets(
717    targets: &mut [MeterDisplayTarget; 8],
718    raw: &[u8],
719) -> Result<(), String> {
720    assert!(raw.len() >= 8);
721
722    let mut val = 0u32;
723
724    (0..targets.len()).step_by(4).try_for_each(|pos| {
725        deserialize_u32(&mut val, &raw[pos..(pos + 4)]);
726
727        targets[pos..(pos + 4)]
728            .iter_mut()
729            .enumerate()
730            .try_for_each(|(j, target)| {
731                let pos = ((val >> (j * 8)) & 0x000000ff) as usize;
732                METER_DISPLAY_TARGETS
733                    .iter()
734                    .nth(pos)
735                    .ok_or_else(|| format!("Meter display target not found for position {}", pos))
736                    .map(|&t| *target = t)
737            })
738    })
739}
740
741/// Parameters specific to liquid Saffire 56.
742#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
743pub struct LiquidS56SpecificParams {
744    /// Emulation type of microphone amplifiers.
745    pub mic_amp_emulation_types: [MicAmpEmulationType; 2],
746    /// Harmonics of microphone amplifiers.
747    pub mic_amp_harmonics: [u8; 2],
748    /// Polarities of microphone amplifiers.
749    pub mic_amp_polarities: [bool; 2],
750    /// Nominal level of analog inputs.
751    pub analog_input_levels: [AnalogInputLevel; 8],
752    /// State of LEDs.
753    pub led_states: LedState,
754    /// Targets of meter display.
755    pub meter_display_targets: [MeterDisplayTarget; 8],
756}
757
758const SPECIFIC_PARAMS_OFFSET: usize = 0x0278;
759const SPECIFIC_PARAMS_SIZE: usize = 0x48;
760
761fn serialize_specific_params(
762    params: &LiquidS56SpecificParams,
763    raw: &mut [u8],
764) -> Result<(), String> {
765    assert!(raw.len() >= SPECIFIC_PARAMS_SIZE);
766
767    serialize_mic_amp_emulation_types(&params.mic_amp_emulation_types, &mut raw[..0x08])?;
768    serialize_mic_amp_harmonics(&params.mic_amp_harmonics, &mut raw[0x08..0x10])?;
769    serialize_mic_amp_polarities(&params.mic_amp_polarities, &mut raw[0x10..0x18])?;
770    serialize_meter_display_targets(&params.meter_display_targets, &mut raw[0x24..0x3c])?;
771    serialize_analog_input_levels(&params.analog_input_levels, &mut raw[0x3c..0x44])?;
772    serialize_led_state(&params.led_states, &mut raw[0x44..0x48])?;
773
774    Ok(())
775}
776
777fn deserialize_specific_params(
778    params: &mut LiquidS56SpecificParams,
779    raw: &[u8],
780) -> Result<(), String> {
781    assert!(raw.len() >= SPECIFIC_PARAMS_SIZE);
782
783    deserialize_mic_amp_emulation_types(&mut params.mic_amp_emulation_types, &raw[..0x08])?;
784    deserialize_mic_amp_harmonics(&mut params.mic_amp_harmonics, &raw[0x08..0x10])?;
785    deserialize_mic_amp_polarities(&mut params.mic_amp_polarities, &raw[0x10..0x18])?;
786    deserialize_meter_display_targets(&mut params.meter_display_targets, &raw[0x24..0x3c])?;
787    deserialize_analog_input_levels(&mut params.analog_input_levels, &raw[0x3c..0x44])?;
788    deserialize_led_state(&mut params.led_states, &raw[0x44..0x48])?;
789
790    Ok(())
791}
792
793/// Protocol specific to Saffire Pro 26.
794impl LiquidS56Protocol {
795    pub const MIC_AMP_HARMONICS_MIN: u8 = 0;
796    pub const MIC_AMP_HARMONICS_MAX: u8 = 21;
797}
798
799impl TcatExtensionSectionParamsOperation<LiquidS56SpecificParams> for LiquidS56Protocol {
800    fn cache_extension_whole_params(
801        req: &FwReq,
802        node: &FwNode,
803        sections: &ExtensionSections,
804        _: &ExtensionCaps,
805        params: &mut LiquidS56SpecificParams,
806        timeout_ms: u32,
807    ) -> Result<(), Error> {
808        let mut raw = vec![0u8; SPECIFIC_PARAMS_SIZE];
809        Self::read_extension(
810            req,
811            node,
812            &sections.application,
813            SPECIFIC_PARAMS_OFFSET,
814            &mut raw,
815            timeout_ms,
816        )?;
817        deserialize_specific_params(params, &raw)
818            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
819    }
820}
821
822impl TcatExtensionSectionPartialMutableParamsOperation<LiquidS56SpecificParams>
823    for LiquidS56Protocol
824{
825    fn update_extension_partial_params(
826        req: &FwReq,
827        node: &FwNode,
828        sections: &ExtensionSections,
829        _: &ExtensionCaps,
830        params: &LiquidS56SpecificParams,
831        prev: &mut LiquidS56SpecificParams,
832        timeout_ms: u32,
833    ) -> Result<(), Error> {
834        let mut new = vec![0u8; SPECIFIC_PARAMS_SIZE];
835        serialize_specific_params(params, &mut new)
836            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
837
838        let mut old = vec![0u8; SPECIFIC_PARAMS_SIZE];
839        serialize_specific_params(prev, &mut old)
840            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
841
842        (0..SPECIFIC_PARAMS_SIZE).step_by(4).try_for_each(|pos| {
843            if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
844                Self::write_extension(
845                    req,
846                    node,
847                    &sections.application,
848                    SPECIFIC_PARAMS_OFFSET + pos,
849                    &mut new[pos..(pos + 4)],
850                    timeout_ms,
851                )
852            } else {
853                Ok(())
854            }
855        })?;
856
857        [
858            (0x00, MIC_AMP_1_EMULATION_SW_NOTICE),
859            (0x04, MIC_AMP_2_EMULATION_SW_NOTICE),
860            (0x08, MIC_AMP_1_HARMONICS_SW_NOTICE),
861            (0x0c, MIC_AMP_2_HARMONICS_SW_NOTICE),
862            (0x10, MIC_AMP_POLARITY_SW_NOTICE),
863            (0x14, MIC_AMP_POLARITY_SW_NOTICE),
864            (0x38, INPUT_LEVEL_SW_NOTICE),
865            (0x3c, INPUT_LEVEL_SW_NOTICE),
866        ]
867        .iter()
868        .filter(|(pos, _)| &new[*pos..(*pos + 4)] != &old[*pos..(*pos + 4)])
869        .try_for_each(|(_, msg)| Self::write_sw_notice(req, node, sections, *msg, timeout_ms))?;
870
871        deserialize_specific_params(prev, &new)
872            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
873    }
874}
875
876#[cfg(test)]
877mod test {
878    use super::*;
879
880    #[test]
881    fn serdes_specific_params() {
882        let params = LiquidS56SpecificParams {
883            mic_amp_emulation_types: [MicAmpEmulationType::Savillerow, MicAmpEmulationType::Dunk],
884            mic_amp_harmonics: [0x59, 0xb2],
885            mic_amp_polarities: [false, true],
886            analog_input_levels: [
887                AnalogInputLevel::Line,
888                AnalogInputLevel::Mic,
889                AnalogInputLevel::Inst,
890                AnalogInputLevel::Line,
891                AnalogInputLevel::Mic,
892                AnalogInputLevel::Inst,
893                AnalogInputLevel::Line,
894                AnalogInputLevel::Mic,
895            ],
896            led_states: LedState {
897                adat1: false,
898                adat2: true,
899                spdif: false,
900                midi_in: true,
901            },
902            meter_display_targets: [
903                MeterDisplayTarget::AdatInput8,
904                MeterDisplayTarget::AdatInput11,
905                MeterDisplayTarget::AdatInput2,
906                MeterDisplayTarget::AdatInput5,
907                MeterDisplayTarget::AnalogInput5,
908                MeterDisplayTarget::SpdifInput1,
909                MeterDisplayTarget::AnalogInput0,
910                MeterDisplayTarget::AnalogInput3,
911            ],
912        };
913        let mut raw = vec![0u8; SPECIFIC_PARAMS_SIZE];
914        serialize_specific_params(&params, &mut raw).unwrap();
915
916        let mut p = LiquidS56SpecificParams::default();
917        deserialize_specific_params(&mut p, &raw).unwrap();
918
919        assert_eq!(params, p);
920    }
921}