firewire_dice_protocols/
focusrite.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol specific to Focusrite Saffire series.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by Focusrite for Saffire series.
8
9pub mod liquids56;
10pub mod spro14;
11pub mod spro24;
12pub mod spro24dsp;
13pub mod spro26;
14pub mod spro40;
15
16use super::{
17    tcat::{extension::*, *},
18    *,
19};
20
21/// Software notice protocol to update hardware parameter.
22pub trait SaffireproSwNoticeOperation: TcatExtensionOperation {
23    const SW_NOTICE_OFFSET: usize;
24
25    fn write_sw_notice(
26        req: &FwReq,
27        node: &FwNode,
28        sections: &ExtensionSections,
29        notice: u32,
30        timeout_ms: u32,
31    ) -> Result<(), Error> {
32        let mut raw = [0; 4];
33        serialize_u32(&notice, &mut raw);
34        Self::write_extension(
35            req,
36            node,
37            &sections.application,
38            Self::SW_NOTICE_OFFSET,
39            &mut raw,
40            timeout_ms,
41        )
42    }
43}
44
45/// A set of entries for output control. Output volumes corresponding to the entries are
46/// controlled by single software/hardware operation if enabled.
47#[derive(Default, Debug, Clone, PartialEq, Eq)]
48pub struct OutGroupState {
49    /// Volume of each analog output, between 0x00 and 0x7f.
50    pub vols: Vec<i8>,
51
52    /// Whether to mute each analog output.
53    pub vol_mutes: Vec<bool>,
54
55    /// Whether to control volume of each analog output by hardware `monitor` knob.
56    pub vol_hwctls: Vec<bool>,
57
58    /// Whether mute is enabled or not.
59    pub mute_enabled: bool,
60
61    /// Whether to control volume of each analog output by hardware `mute` button.
62    pub mute_hwctls: Vec<bool>,
63
64    /// Whether dim is enabled or not.
65    pub dim_enabled: bool,
66
67    /// Whether to control volume of each analog output by hardware `dim` button.
68    pub dim_hwctls: Vec<bool>,
69
70    /// Current value of hardware `monitor` knob, supported by Liquid Saffire 56 and
71    /// Saffire Pro 40.
72    pub hw_knob_value: i8,
73}
74
75const OUT_GROUP_STATE_SIZE: usize = 0x50;
76
77const VOL_MIN: i8 = 0;
78const VOL_MAX: i8 = i8::MAX;
79
80fn serialize_out_group_state(state: &OutGroupState, raw: &mut [u8]) -> Result<(), String> {
81    assert!(raw.len() >= OUT_GROUP_STATE_SIZE);
82
83    serialize_bool(&state.mute_enabled, &mut raw[0x00..0x04]);
84    serialize_bool(&state.dim_enabled, &mut raw[0x04..0x08]);
85
86    (0..(state.vols.len() / 2)).for_each(|i| {
87        let mut val = 0u32;
88        state.vols[(i * 2)..(i * 2 + 2)]
89            .iter()
90            .enumerate()
91            .for_each(|(j, &vol)| {
92                // NOTE: inverted.
93                let v = VOL_MAX - vol;
94                val |= (v as u32) << (8 * j);
95            });
96        let pos = 0x08 + i * 4;
97        serialize_u32(&val, &mut raw[pos..(pos + 4)]);
98    });
99
100    (0..(state.vol_hwctls.len() / 2)).for_each(|i| {
101        let mut val = 0u32;
102        let idx = i * 2;
103
104        state.vol_hwctls[idx..(idx + 2)]
105            .iter()
106            .enumerate()
107            .filter(|(_, &vol_hwctl)| vol_hwctl)
108            .for_each(|(i, _)| val |= 1 << i);
109
110        state.vol_mutes[idx..(idx + 2)]
111            .iter()
112            .enumerate()
113            .filter(|(_, &vol_mute)| vol_mute)
114            .for_each(|(i, _)| val |= 1 << (i + 2));
115
116        let pos = 0x1c + i * 4;
117        serialize_u32(&val, &mut raw[pos..(pos + 4)]);
118    });
119
120    let mut val = 0u32;
121    state
122        .dim_hwctls
123        .iter()
124        .enumerate()
125        .filter(|(_, &assigned)| assigned)
126        .for_each(|(i, _)| val |= 1 << (i + 10));
127    state
128        .mute_hwctls
129        .iter()
130        .enumerate()
131        .filter(|(_, &assigned)| assigned)
132        .for_each(|(i, _)| val |= 1 << i);
133    serialize_u32(&val, &mut raw[0x30..0x34]);
134
135    serialize_i8(&state.hw_knob_value, &mut raw[0x48..0x4c]);
136
137    Ok(())
138}
139
140fn deserialize_out_group_state(state: &mut OutGroupState, raw: &[u8]) -> Result<(), String> {
141    assert!(raw.len() >= OUT_GROUP_STATE_SIZE);
142
143    deserialize_bool(&mut state.mute_enabled, &raw[0x00..0x04]);
144    deserialize_bool(&mut state.dim_enabled, &raw[0x04..0x08]);
145
146    (0..(state.vols.len() / 2)).for_each(|i| {
147        let pos = 0x08 + i * 4;
148        let mut val = 0u32;
149        deserialize_u32(&mut val, &raw[pos..(pos + 4)]);
150        state.vols[(i * 2)..(i * 2 + 2)]
151            .iter_mut()
152            .enumerate()
153            .for_each(|(j, vol)| {
154                let v = ((val >> (j * 8)) & 0xff) as i8;
155                // NOTE: inverted.
156                *vol = VOL_MAX - v;
157            });
158    });
159
160    (0..(state.vol_hwctls.len() / 2)).for_each(|i| {
161        let pos = 0x1c + i * 4;
162        let mut val = 0u32;
163        deserialize_u32(&mut val, &raw[pos..(pos + 4)]);
164        let idx = i * 2;
165
166        state.vol_hwctls[idx..(idx + 2)]
167            .iter_mut()
168            .enumerate()
169            .for_each(|(i, vol_hwctl)| *vol_hwctl = val & (1 << i) > 0);
170
171        state.vol_mutes[idx..(idx + 2)]
172            .iter_mut()
173            .enumerate()
174            .for_each(|(i, vol_mute)| {
175                *vol_mute = val & (1 << (i + 2)) > 0;
176            });
177    });
178
179    let mut val = 0u32;
180    deserialize_u32(&mut val, &raw[0x30..0x34]);
181    state
182        .dim_hwctls
183        .iter_mut()
184        .enumerate()
185        .for_each(|(i, assign)| *assign = val & (1 << (i + 10)) > 0);
186    state
187        .mute_hwctls
188        .iter_mut()
189        .enumerate()
190        .for_each(|(i, assign)| *assign = val & (1 << i) > 0);
191
192    deserialize_i8(&mut state.hw_knob_value, &raw[0x48..0x4c]);
193
194    Ok(())
195}
196
197const NOTIFY_DIM_MUTE_CHANGE: u32 = 0x00200000;
198const NOTIFY_VOL_CHANGE: u32 = 0x00400000;
199
200/// Output group operation.
201pub trait SaffireproOutGroupSpecification: SaffireproSwNoticeOperation {
202    /// Offset of output group state.
203    const OUT_GROUP_STATE_OFFSET: usize;
204
205    /// The number of outputs to be controlled.
206    const ENTRY_COUNT: usize;
207
208    /// Support hardware knob to control volume.
209    const HAS_VOL_HWCTL: bool;
210
211    /// Software notification for source of output group.
212    const SRC_NOTICE: u32;
213
214    /// Software notification for dim and mute of output group.
215    const DIM_MUTE_NOTICE: u32;
216
217    /// The minimum value of volume.
218    const VOL_MIN: i8 = VOL_MIN;
219
220    /// The maximum value of volume.
221    const VOL_MAX: i8 = VOL_MAX;
222
223    /// Instantiate structure for output group state.
224    fn create_out_group_state() -> OutGroupState {
225        OutGroupState {
226            vols: vec![0; Self::ENTRY_COUNT],
227            vol_mutes: vec![false; Self::ENTRY_COUNT],
228            vol_hwctls: vec![false; Self::ENTRY_COUNT],
229            mute_enabled: false,
230            mute_hwctls: vec![false; Self::ENTRY_COUNT],
231            dim_enabled: false,
232            dim_hwctls: vec![false; Self::ENTRY_COUNT],
233            hw_knob_value: 0,
234        }
235    }
236}
237
238impl<O: TcatExtensionOperation + SaffireproOutGroupSpecification>
239    TcatExtensionSectionParamsOperation<OutGroupState> for O
240{
241    fn cache_extension_whole_params(
242        req: &FwReq,
243        node: &FwNode,
244        sections: &ExtensionSections,
245        _: &ExtensionCaps,
246        params: &mut OutGroupState,
247        timeout_ms: u32,
248    ) -> Result<(), Error> {
249        let mut raw = vec![0u8; OUT_GROUP_STATE_SIZE];
250        Self::read_extension(
251            req,
252            node,
253            &sections.application,
254            O::OUT_GROUP_STATE_OFFSET,
255            &mut raw,
256            timeout_ms,
257        )?;
258        deserialize_out_group_state(params, &raw)
259            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
260    }
261}
262
263impl<O: TcatExtensionOperation + SaffireproOutGroupSpecification>
264    TcatExtensionSectionPartialMutableParamsOperation<OutGroupState> for O
265{
266    fn update_extension_partial_params(
267        req: &FwReq,
268        node: &FwNode,
269        sections: &ExtensionSections,
270        _: &ExtensionCaps,
271        params: &OutGroupState,
272        prev: &mut OutGroupState,
273        timeout_ms: u32,
274    ) -> Result<(), Error> {
275        let mut new = vec![0u8; OUT_GROUP_STATE_SIZE];
276        serialize_out_group_state(params, &mut new)
277            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
278
279        let mut old = vec![0u8; OUT_GROUP_STATE_SIZE];
280        serialize_out_group_state(prev, &mut old)
281            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
282
283        (0..OUT_GROUP_STATE_SIZE).step_by(4).try_for_each(|pos| {
284            if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
285                Self::write_extension(
286                    req,
287                    node,
288                    &sections.application,
289                    Self::OUT_GROUP_STATE_OFFSET + pos,
290                    &mut new[pos..(pos + 4)],
291                    timeout_ms,
292                )
293            } else {
294                Ok(())
295            }
296        })?;
297
298        if new[..0x08] != old[..0x08] {
299            Self::write_sw_notice(req, node, sections, Self::DIM_MUTE_NOTICE, timeout_ms)?;
300        }
301
302        if new[0x08..0x34] != old[0x08..0x34] {
303            Self::write_sw_notice(req, node, sections, Self::SRC_NOTICE, timeout_ms)?;
304        }
305
306        deserialize_out_group_state(prev, &new)
307            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
308    }
309}
310
311impl<O: TcatExtensionOperation + SaffireproOutGroupSpecification>
312    TcatExtensionSectionNotifiedParamsOperation<OutGroupState> for O
313{
314    fn cache_extension_notified_params(
315        req: &FwReq,
316        node: &FwNode,
317        sections: &ExtensionSections,
318        caps: &ExtensionCaps,
319        params: &mut OutGroupState,
320        msg: u32,
321        timeout_ms: u32,
322    ) -> Result<(), Error> {
323        if msg & (NOTIFY_DIM_MUTE_CHANGE | NOTIFY_VOL_CHANGE) > 0 {
324            Self::cache_extension_whole_params(req, node, sections, caps, params, timeout_ms)?;
325
326            if msg & NOTIFY_VOL_CHANGE > 0 {
327                let vol_hwctls = params.vol_hwctls.clone();
328                let hw_knob_value = params.hw_knob_value;
329                params
330                    .vols
331                    .iter_mut()
332                    .zip(vol_hwctls)
333                    .filter(|(_, vol_hwctl)| *vol_hwctl)
334                    .for_each(|(vol, _)| *vol = hw_knob_value);
335            }
336        }
337
338        Ok(())
339    }
340}
341
342/// The level of microphone input.
343#[derive(Debug, Copy, Clone, PartialEq, Eq)]
344pub enum SaffireproMicInputLevel {
345    /// Gain range: -10dB to +36 dB.
346    Line,
347    /// Gain range: +13 to +60 dB, headroom: +8dBu.
348    Instrument,
349}
350
351impl Default for SaffireproMicInputLevel {
352    fn default() -> Self {
353        Self::Line
354    }
355}
356
357/// The level of line input.
358#[derive(Debug, Copy, Clone, PartialEq, Eq)]
359pub enum SaffireproLineInputLevel {
360    /// +16 dBu.
361    Low,
362    /// -10dBV.
363    High,
364}
365
366impl Default for SaffireproLineInputLevel {
367    fn default() -> Self {
368        Self::Low
369    }
370}
371
372/// Parameters for analog inputs.
373#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
374pub struct SaffireproInputParams {
375    /// Nominal level of microphone inputs.
376    pub mic_levels: [SaffireproMicInputLevel; 2],
377    /// Nominal level of line inputs.
378    pub line_levels: [SaffireproLineInputLevel; 2],
379}
380
381const MIC_INPUT_LEVEL_INSTRUMENT_FLAG: u16 = 0x0002;
382const LINE_INPUT_LEVEL_HIGH_FLAG: u16 = 0x0001;
383
384const INPUT_PARAMS_SIZE: usize = 8;
385
386fn serialize_input_params(params: &SaffireproInputParams, raw: &mut [u8]) -> Result<(), String> {
387    assert!(raw.len() >= INPUT_PARAMS_SIZE);
388
389    let mut val = 0u32;
390    params.mic_levels.iter().enumerate().for_each(|(i, level)| {
391        if *level == SaffireproMicInputLevel::Instrument {
392            val |= (MIC_INPUT_LEVEL_INSTRUMENT_FLAG as u32) << (16 * i);
393        };
394    });
395    serialize_u32(&val, &mut raw[..4]);
396
397    let mut val = 0u32;
398    params
399        .line_levels
400        .iter()
401        .enumerate()
402        .for_each(|(i, level)| {
403            if *level == SaffireproLineInputLevel::High {
404                val |= (LINE_INPUT_LEVEL_HIGH_FLAG as u32) << (16 * i);
405            }
406        });
407    serialize_u32(&val, &mut raw[4..8]);
408
409    Ok(())
410}
411
412fn deserialize_input_params(params: &mut SaffireproInputParams, raw: &[u8]) -> Result<(), String> {
413    assert!(raw.len() >= INPUT_PARAMS_SIZE);
414
415    let mut val = 0u32;
416
417    deserialize_u32(&mut val, &raw[..4]);
418    params
419        .mic_levels
420        .iter_mut()
421        .enumerate()
422        .for_each(|(i, level)| {
423            let flag = (val >> (16 * i)) as u16;
424            *level = if flag & MIC_INPUT_LEVEL_INSTRUMENT_FLAG > 0 {
425                SaffireproMicInputLevel::Instrument
426            } else {
427                SaffireproMicInputLevel::Line
428            };
429        });
430
431    deserialize_u32(&mut val, &raw[4..8]);
432    params
433        .line_levels
434        .iter_mut()
435        .enumerate()
436        .for_each(|(i, level)| {
437            let flag = (val >> (16 * i)) as u16;
438            *level = if flag & LINE_INPUT_LEVEL_HIGH_FLAG > 0 {
439                SaffireproLineInputLevel::High
440            } else {
441                SaffireproLineInputLevel::Low
442            };
443        });
444
445    Ok(())
446}
447
448/// Input protocol specific to Pro 14 and Pro 24.
449pub trait SaffireproInputSpecification: SaffireproSwNoticeOperation {
450    const INPUT_PARAMS_OFFSET: usize;
451
452    const SW_NOTICE: u32 = 0x00000004;
453
454    const MIC_INPUT_COUNT: usize = 2;
455    const LINE_INPUT_COUNT: usize = 2;
456}
457
458impl<O: TcatExtensionOperation + SaffireproInputSpecification>
459    TcatExtensionSectionParamsOperation<SaffireproInputParams> for O
460{
461    fn cache_extension_whole_params(
462        req: &FwReq,
463        node: &FwNode,
464        sections: &ExtensionSections,
465        _: &ExtensionCaps,
466        params: &mut SaffireproInputParams,
467        timeout_ms: u32,
468    ) -> Result<(), Error> {
469        let mut raw = vec![0u8; INPUT_PARAMS_SIZE];
470        Self::read_extension(
471            req,
472            node,
473            &sections.application,
474            O::INPUT_PARAMS_OFFSET,
475            &mut raw,
476            timeout_ms,
477        )?;
478        deserialize_input_params(params, &raw)
479            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
480    }
481}
482
483impl<O: TcatExtensionOperation + SaffireproInputSpecification>
484    TcatExtensionSectionPartialMutableParamsOperation<SaffireproInputParams> for O
485{
486    fn update_extension_partial_params(
487        req: &FwReq,
488        node: &FwNode,
489        sections: &ExtensionSections,
490        _: &ExtensionCaps,
491        params: &SaffireproInputParams,
492        prev: &mut SaffireproInputParams,
493        timeout_ms: u32,
494    ) -> Result<(), Error> {
495        let mut new = vec![0u8; INPUT_PARAMS_SIZE];
496        serialize_input_params(params, &mut new)
497            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
498
499        let mut old = vec![0u8; INPUT_PARAMS_SIZE];
500        serialize_input_params(prev, &mut old)
501            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
502
503        (0..INPUT_PARAMS_SIZE).step_by(4).try_for_each(|pos| {
504            if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
505                Self::write_extension(
506                    req,
507                    node,
508                    &sections.application,
509                    Self::INPUT_PARAMS_OFFSET + pos,
510                    &mut new[pos..(pos + 4)],
511                    timeout_ms,
512                )
513            } else {
514                Ok(())
515            }
516        })?;
517
518        if new != old {
519            Self::write_sw_notice(req, node, sections, Self::SW_NOTICE, timeout_ms)?;
520        }
521
522        deserialize_input_params(prev, &new)
523            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
524    }
525}
526
527/// Type of signal for optical output interface.
528#[derive(Debug, Copy, Clone, PartialEq, Eq)]
529pub enum OpticalOutIfaceMode {
530    /// For ADAT signal.
531    Adat,
532    /// For S/PDIF signal.
533    Spdif,
534    /// For AES/EBU signal.
535    AesEbu,
536}
537
538impl Default for OpticalOutIfaceMode {
539    fn default() -> Self {
540        Self::Adat
541    }
542}
543
544impl OpticalOutIfaceMode {
545    const ADAT_VALUE: u32 = 0x00000000;
546    const SPDIF_VALUE: u32 = 0x00000001;
547    const AESEBU_VALUE: u32 = 0x00000002;
548}
549
550const OPTICAL_OUT_IFACE_MODE_MASK: u32 = 0x00000003;
551const MIC_AMP_TRANSFORMER_CH0_FLAG: u32 = 0x00000008;
552const MIC_AMP_TRANSFORMER_CH1_FLAG: u32 = 0x00000010;
553
554fn serialize_optical_out_iface_mode(
555    mode: &OpticalOutIfaceMode,
556    aesebu_is_supported: bool,
557    raw: &mut [u8],
558) -> Result<(), String> {
559    assert!(raw.len() >= 4);
560
561    let mut val = 0u32;
562    deserialize_u32(&mut val, raw);
563    val &= !OPTICAL_OUT_IFACE_MODE_MASK;
564
565    let v = match mode {
566        OpticalOutIfaceMode::Adat => OpticalOutIfaceMode::ADAT_VALUE,
567        OpticalOutIfaceMode::Spdif => OpticalOutIfaceMode::SPDIF_VALUE,
568        OpticalOutIfaceMode::AesEbu => {
569            if aesebu_is_supported {
570                OpticalOutIfaceMode::AESEBU_VALUE
571            } else {
572                Err(format!("AES/EBU is not supported but selected"))?
573            }
574        }
575    };
576    val |= v;
577
578    serialize_u32(&val, raw);
579
580    Ok(())
581}
582
583fn deserialize_optical_out_iface_mode(
584    mode: &mut OpticalOutIfaceMode,
585    aesebu_is_supported: bool,
586    raw: &[u8],
587) -> Result<(), String> {
588    assert!(raw.len() >= 4);
589
590    let mut val = 0u32;
591    deserialize_u32(&mut val, raw);
592    val &= OPTICAL_OUT_IFACE_MODE_MASK;
593
594    *mode = match val {
595        OpticalOutIfaceMode::ADAT_VALUE => OpticalOutIfaceMode::Adat,
596        OpticalOutIfaceMode::SPDIF_VALUE => OpticalOutIfaceMode::Spdif,
597        OpticalOutIfaceMode::AESEBU_VALUE => {
598            if aesebu_is_supported {
599                OpticalOutIfaceMode::AesEbu
600            } else {
601                Err(format!("AES/EBU is not supported but detected"))?
602            }
603        }
604        _ => Err(format!(
605            "Optical interface mode not found for value {}",
606            val
607        ))?,
608    };
609
610    Ok(())
611}
612
613const MIC_AMP_TRANSFORMER_FLAGS: [u32; 2] =
614    [MIC_AMP_TRANSFORMER_CH0_FLAG, MIC_AMP_TRANSFORMER_CH1_FLAG];
615
616fn serialize_mic_amp_transformers(transformers: &[bool; 2], raw: &mut [u8]) -> Result<(), String> {
617    assert!(raw.len() >= 4);
618
619    let mut val = 0u32;
620    deserialize_u32(&mut val, raw);
621    val &= !(MIC_AMP_TRANSFORMER_CH0_FLAG | MIC_AMP_TRANSFORMER_CH1_FLAG);
622
623    transformers
624        .iter()
625        .zip(MIC_AMP_TRANSFORMER_FLAGS)
626        .filter(|(&enabled, _)| enabled)
627        .for_each(|(_, flag)| val |= flag);
628
629    serialize_u32(&val, raw);
630
631    Ok(())
632}
633
634fn deserialize_mic_amp_transformers(
635    transformers: &mut [bool; 2],
636    raw: &[u8],
637) -> Result<(), String> {
638    assert!(raw.len() >= 4);
639
640    let mut val = 0u32;
641    deserialize_u32(&mut val, raw);
642    val &= MIC_AMP_TRANSFORMER_CH0_FLAG | MIC_AMP_TRANSFORMER_CH1_FLAG;
643
644    transformers
645        .iter_mut()
646        .zip(MIC_AMP_TRANSFORMER_FLAGS)
647        .for_each(|(enabled, flag)| *enabled = val & flag > 0);
648
649    Ok(())
650}
651
652/// General input/output configuration for Liquid Saffire 56 and Saffire Pro 40.
653#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
654pub struct SaffireproIoParams {
655    /// Whether to activate attenuation for analog output 0/1.
656    pub analog_out_0_1_pad: bool,
657    /// Mode of signal for optical output interface.
658    pub opt_out_iface_mode: OpticalOutIfaceMode,
659    /// Whether to enable transformer of microhphone amplifiers.
660    pub mic_amp_transformers: [bool; 2],
661}
662
663const IO_PARAMS_OFFSET: usize = 0x40;
664const IO_PARAMS_SIZE: usize = 0x20;
665
666fn serialize_io_params(
667    params: &SaffireproIoParams,
668    aesebu_is_supported: bool,
669    raw: &mut [u8],
670) -> Result<(), String> {
671    assert!(raw.len() >= IO_PARAMS_SIZE);
672
673    serialize_bool(&params.analog_out_0_1_pad, &mut raw[..4]);
674    serialize_optical_out_iface_mode(
675        &params.opt_out_iface_mode,
676        aesebu_is_supported,
677        &mut raw[0x1c..0x20],
678    )?;
679    serialize_mic_amp_transformers(&params.mic_amp_transformers, &mut raw[0x1c..0x20])?;
680
681    Ok(())
682}
683
684fn deserialize_io_params(
685    params: &mut SaffireproIoParams,
686    aesebu_is_supported: bool,
687    raw: &[u8],
688) -> Result<(), String> {
689    assert!(raw.len() >= IO_PARAMS_SIZE);
690
691    deserialize_bool(&mut params.analog_out_0_1_pad, &raw[..4]);
692    deserialize_optical_out_iface_mode(
693        &mut params.opt_out_iface_mode,
694        aesebu_is_supported,
695        &raw[0x1c..0x20],
696    )?;
697    deserialize_mic_amp_transformers(&mut params.mic_amp_transformers, &raw[0x1c..0x20])?;
698
699    Ok(())
700}
701
702/// Operation for parameters of input/output configuration.
703pub trait SaffireproIoParamsSpecification: SaffireproSwNoticeOperation {
704    /// Whether to support AES/EBU signal in optical interface.
705    const AESEBU_IS_SUPPORTED: bool;
706
707    /// Whether to support transformer function for microphone pre-amplifier.
708    const MIC_PREAMP_TRANSFORMER_IS_SUPPORTED: bool;
709}
710
711impl<O: TcatExtensionOperation + SaffireproIoParamsSpecification>
712    TcatExtensionSectionParamsOperation<SaffireproIoParams> for O
713{
714    fn cache_extension_whole_params(
715        req: &FwReq,
716        node: &FwNode,
717        sections: &ExtensionSections,
718        _: &ExtensionCaps,
719        params: &mut SaffireproIoParams,
720        timeout_ms: u32,
721    ) -> Result<(), Error> {
722        let mut raw = vec![0u8; IO_PARAMS_SIZE];
723        Self::read_extension(
724            req,
725            node,
726            &sections.application,
727            IO_PARAMS_OFFSET,
728            &mut raw,
729            timeout_ms,
730        )?;
731        deserialize_io_params(params, O::AESEBU_IS_SUPPORTED, &raw)
732            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
733    }
734}
735
736impl<O: TcatExtensionOperation + SaffireproIoParamsSpecification>
737    TcatExtensionSectionPartialMutableParamsOperation<SaffireproIoParams> for O
738{
739    fn update_extension_partial_params(
740        req: &FwReq,
741        node: &FwNode,
742        sections: &ExtensionSections,
743        _: &ExtensionCaps,
744        params: &SaffireproIoParams,
745        prev: &mut SaffireproIoParams,
746        timeout_ms: u32,
747    ) -> Result<(), Error> {
748        let mut new = vec![0u8; IO_PARAMS_SIZE];
749        serialize_io_params(params, Self::AESEBU_IS_SUPPORTED, &mut new)
750            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
751
752        let mut old = vec![0u8; IO_PARAMS_SIZE];
753        serialize_io_params(prev, Self::AESEBU_IS_SUPPORTED, &mut old)
754            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
755
756        (0..IO_PARAMS_SIZE).step_by(4).try_for_each(|pos| {
757            if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
758                Self::write_extension(
759                    req,
760                    node,
761                    &sections.application,
762                    IO_PARAMS_OFFSET + pos,
763                    &mut new[pos..(pos + 4)],
764                    timeout_ms,
765                )
766            } else {
767                Ok(())
768            }
769        })?;
770
771        if new[..0x04] != old[..0x04] {
772            Self::write_sw_notice(req, node, sections, 0x00000003, timeout_ms)?;
773        }
774
775        if new[0x1c..0x20] != old[0x1c..0x20] {
776            let mut n = 0u32;
777            deserialize_u32(&mut n, &new[0x1c..0x20]);
778            let mut o = 0u32;
779            deserialize_u32(&mut o, &old[0x1c..0x20]);
780
781            if (n ^ o) & 0x00000003 > 0 {
782                Self::write_sw_notice(req, node, sections, 0x00000004, timeout_ms)?;
783            }
784
785            if (n ^ o) & 0x00000008 > 0 {
786                Self::write_sw_notice(req, node, sections, 0x00000008, timeout_ms)?;
787            }
788
789            if (n ^ o) & 0x00000010 > 0 {
790                Self::write_sw_notice(req, node, sections, 0x00000010, timeout_ms)?;
791            }
792        }
793
794        deserialize_io_params(prev, Self::AESEBU_IS_SUPPORTED, &new)
795            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
796    }
797}
798
799#[cfg(test)]
800mod test {
801    use super::*;
802
803    #[test]
804    fn input_params_serdes() {
805        let params = SaffireproInputParams {
806            mic_levels: [
807                SaffireproMicInputLevel::Instrument,
808                SaffireproMicInputLevel::Line,
809            ],
810            line_levels: [
811                SaffireproLineInputLevel::High,
812                SaffireproLineInputLevel::Low,
813            ],
814        };
815
816        let mut raw = vec![0u8; INPUT_PARAMS_SIZE];
817        serialize_input_params(&params, &mut raw).unwrap();
818
819        let mut p = SaffireproInputParams::default();
820        deserialize_input_params(&mut p, &raw).unwrap();
821
822        assert_eq!(params, p);
823    }
824
825    #[test]
826    fn out_group_serdes() {
827        let params = OutGroupState {
828            vols: vec![0, 1, 2, 3, 4, 5],
829            vol_mutes: vec![false, true, true, false, false, true],
830            vol_hwctls: vec![true, false, false, true, true, false],
831            mute_enabled: true,
832            mute_hwctls: vec![true, true, true, false, false, false],
833            dim_enabled: true,
834            dim_hwctls: vec![false, false, false, true, true, true],
835            hw_knob_value: 33,
836        };
837
838        let mut raw = vec![0; 0x100];
839        serialize_out_group_state(&params, &mut raw).unwrap();
840
841        let mut p = OutGroupState {
842            vols: vec![Default::default(); 6],
843            vol_mutes: vec![Default::default(); 6],
844            vol_hwctls: vec![Default::default(); 6],
845            mute_enabled: true,
846            mute_hwctls: vec![Default::default(); 6],
847            dim_enabled: true,
848            dim_hwctls: vec![Default::default(); 6],
849            hw_knob_value: 33,
850        };
851        deserialize_out_group_state(&mut p, &raw).unwrap();
852
853        assert_eq!(params, p);
854    }
855
856    #[test]
857    fn io_params_serdes() {
858        let params = SaffireproIoParams {
859            analog_out_0_1_pad: true,
860            opt_out_iface_mode: OpticalOutIfaceMode::Spdif,
861            mic_amp_transformers: [true, false],
862        };
863
864        let mut raw = vec![0u8; IO_PARAMS_SIZE];
865        serialize_io_params(&params, false, &mut raw).unwrap();
866
867        let mut p = SaffireproIoParams::default();
868        deserialize_io_params(&mut p, false, &raw).unwrap();
869
870        assert_eq!(params, p);
871    }
872}