firewire_dice_protocols/
maudio.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2020 Takashi Sakamoto
3
4//! Hardware specification and application protocol specific to M-Audio ProFire series.
5//!
6//! The modules includes structure, enumeration, and trait and its implementation for hardware
7//! specification and application protocol specific to M-Audio ProFire series.
8//!
9//! ## Diagram of internal signal flow for Profire 2626
10//!
11//! ```text
12//!
13//! XLR input 1 -------+-------+
14//! Phone input 1 -----+       |
15//!                            |
16//! XLR input 2 -------+-------+
17//! Phone input 2 -----+       |
18//!                            +----------------> analog-input-1/2
19//! XLR input 3/4 ------------------------------> analog-input-3/4
20//! XLR input 5/6 ------------------------------> analog-input-5/6
21//! XLR input 7/8 ------------------------------> analog-input-7/8
22//! Coaxial input ------------------------------> spdif-input-1/2
23//! Optical input A ----------------------------> adat-input-1..8
24//! Optical input B -----------or---------------> adat-input-8..16
25//!                             +---------------> spdif-input-3/4
26//!
27//!                          ++=============++
28//! analog-input-1/2 ------> || 70 x 68     || --> analog-output-1/2
29//! analog-input-3/4 ------> || router      || --> analog-output-3/4
30//! analog-input-5/6 ------> || up to       || --> analog-output-5/6
31//! analog-input-7/8 ------> || 128 entries || --> analog-output-7/8
32//!                          ||             ||
33//! spdif-input-1/2 -------> ||             || --> spdif-output-1/2
34//! spdif-input-3/4 -------> ||             || --> spdif-output-3/4
35//!                          ||             ||
36//! adat-input-1/2 --------> ||             || --> adat-output-1/2
37//! adat-input-3/4 --------> ||             || --> adat-output-3/4
38//! adat-input-5/6 --------> ||             || --> adat-output-5/6
39//! adat-input-7/8 --------> ||             || --> adat-output-7/8
40//!                          ||             ||
41//! adat-input-9/10 -------> ||             || --> adat-output-9/10
42//! adat-input-11/12 ------> ||             || --> adat-output-11/12
43//! adat-input-13/14 ------> ||             || --> adat-output-13/14
44//! adat-input-15/16 ------> ||             || --> adat-output-15/16
45//!                          ||             ||
46//! stream-input-A-1/2 ----> ||             || --> stream-output-A-1/2
47//! stream-input-A-3/4 ----> ||             || --> stream-output-A-3/4
48//! stream-input-A-5/6 ----> ||             || --> stream-output-A-5/6
49//! stream-input-A-7/8 ----> ||             || --> stream-output-A-7/8
50//! stream-input-A-9/10 ---> ||             || --> stream-output-A-9/10
51//!                          ||             ||
52//! stream-input-B-1/2 ----> ||             || --> stream-output-B-1/2
53//! stream-input-B-3/4 ----> ||             || --> stream-output-B-3/4
54//! stream-input-B-5/6 ----> ||             || --> stream-output-B-5/6
55//! stream-input-B-7/8 ----> ||             || --> stream-output-B-7/8
56//! stream-input-B-9/10 ---> ||             || --> stream-output-B-9/10
57//! stream-input-B-11/12 --> ||             || --> stream-output-B-11/12
58//! stream-input-B-13/14 --> ||             || --> stream-output-B-13/14
59//! stream-input-B-15/16 --> ||             || --> stream-output-B-15/16
60//!                          ||             ||
61//! mixer-output-1/2 ------> ||             || --> mixer-input-1/2
62//! mixer-output-3/4 ------> ||             || --> mixer-input-3/4
63//! mixer-output-5/6 ------> ||             || --> mixer-input-5/6
64//! mixer-output-7/8 ------> ||             || --> mixer-input-7/8
65//! mixer-output-9/10 -----> ||             || --> mixer-input-9/10
66//! mixer-output-11/12 ----> ||             || --> mixer-input-11/12
67//! mixer-output-13/14 ----> ||             || --> mixer-input-13/14
68//! mixer-output-15/16 ----> ||             || --> mixer-input-15/16
69//!                          ||             || --> mixer-input-17/18
70//!                          ++=============++
71//!
72//!                          ++============++
73//! mixer-input-1/2 ----->   ||            || --> mixer-output-1/2
74//! mixer-input-3/4 ----->   ||            || --> mixer-output-3/4
75//! mixer-input-5/6 ----->   ||            || --> mixer-output-5/6
76//! mixer-input-7/8 ----->   ||  18 x 16   || --> mixer-output-7/8
77//! mixer-input-9/10 ---->   ||            || --> mixer-output-9/10
78//! mixer-input-11/11 --->   ||   mixer    || --> mixer-output-11/12
79//! mixer-input-13/14 --->   ||            || --> mixer-output-13/14
80//! mixer-input-15/16 --->   ||            || --> mixer-output-15/16
81//! mixer-input-17/18 --->   ||            ||
82//!                          ++============++
83//!
84//! analog-output-1/2 ------------+-------------> Phone output 1/2
85//!                               +-------------> Headphone output 1/2
86//! analog-output-3/4 ------------+-------------> Phone output 1/2
87//!                               +-------------> Headphone output 3/4
88//! analog-output-5/6 --------------------------> Phone output 1/2
89//! analog-output-7/8 --------------------------> Phone output 1/2
90//!
91//! spdif-output-1/2 ---------------------------> Coaxial output
92//!
93//! adat-output-1..8 ---------------------------> Optical A output
94//!
95//! adat-output-9..16 ------------or------------> Optical B output
96//! spdif-output-3/4 -------------+
97//!
98//! ```
99//!
100//! ## Diagram of internal signal flow for Profire 610
101//!
102//! At 176.4/192.0 kHz, both stream-input-A and stream-input-B are available for
103//! analog-output-1..6 and spdif-output-1/2 per each.
104//!
105//! ```text
106//!
107//! XLR input 1/2 ------------------------------> analog-input-1/2
108//! Phone input 3/4 ----------------------------> analog-input-3/4
109//! coaxial input 1/2 --------------------------> spdif-input-1/2
110//!
111//!                          ++=============++
112//! analog-input-1/2 ------> || 32 x 30     || --> analog-output-1/2
113//! analog-input-3/4 ------> || router      || --> analog-output-3/4
114//!                          || up to       ||
115//! spdif-input-1/2 -------> || 128 entries || --> spdif-output-1/2
116//!                          ||             ||
117//! stream-input-1/2 ------> ||             || --> stream-output-1/2
118//! stream-input-3/4 ------> ||             || --> stream-output-3/4
119//! stream-input-5/6 ------> ||             || --> stream-output-5/6
120//! stream-input-7/8 ------> ||             ||
121//! stream-input-9/10 -----> ||             ||
122//!                          ||             ||
123//! mixer-output-1/2 ------> ||             || --> mixer-input-1/2
124//! mixer-output-3/4 ------> ||             || --> mixer-input-3/4
125//! mixer-output-5/6 ------> ||             || --> mixer-input-5/6
126//! mixer-output-7/8 ------> ||             || --> mixer-input-7/8
127//! mixer-output-9/10 -----> ||             || --> mixer-input-9/10
128//! mixer-output-11/12 ----> ||             || --> mixer-input-11/12
129//! mixer-output-13/14 ----> ||             || --> mixer-input-13/14
130//! mixer-output-15/16 ----> ||             || --> mixer-input-15/16
131//!                          ||             || --> mixer-input-17/18
132//!                          ++=============++
133//!
134//!                          ++============++
135//! mixer-input-1/2 ----->   ||            || --> mixer-output-1/2
136//! mixer-input-3/4 ----->   ||            || --> mixer-output-3/4
137//! mixer-input-5/6 ----->   ||            || --> mixer-output-5/6
138//! mixer-input-7/8 ----->   ||  18 x 16   || --> mixer-output-7/8
139//! mixer-input-9/10 ---->   ||            || --> mixer-output-9/10
140//! mixer-input-11/11 --->   ||   mixer    || --> mixer-output-11/12
141//! mixer-input-13/14 --->   ||            || --> mixer-output-13/14
142//! mixer-input-15/16 --->   ||            || --> mixer-output-15/16
143//! mixer-input-17/18 --->   ||            ||
144//!                          ++============++
145//!
146//! analog-output-1/2 ------------+-------------> Phone output 1/2
147//!                               +-------------> Headphone output 1/2
148//! analog-output-3/4 ------------+-------------> Phone output 3/4
149//!                               +-------------> Headphone output 1/2
150//!
151//! spdif-output-1/2 ---------------------------> Coaxial output
152//!
153
154use super::{
155    tcat::{extension::*, global_section::*, tcd22xx_spec::*, *},
156    *,
157};
158
159/// Protocol implementation specific to ProFire 2626.
160#[derive(Default, Debug)]
161pub struct Pfire2626Protocol;
162
163impl TcatOperation for Pfire2626Protocol {}
164
165impl TcatGlobalSectionSpecification for Pfire2626Protocol {
166    // NOTE: ClockSource::Tdif is used for second optical interface as 'ADAT_AUX'.
167    const AVAILABLE_CLOCK_SOURCE_OVERRIDE: Option<&'static [ClockSource]> = Some(&[
168        ClockSource::Aes1,
169        ClockSource::Aes4,
170        ClockSource::Adat,
171        ClockSource::Tdif,
172        ClockSource::WordClock,
173        ClockSource::Internal,
174    ]);
175}
176
177impl TcatExtensionOperation for Pfire2626Protocol {}
178
179impl PfireSpecificSpecification for Pfire2626Protocol {
180    const HAS_OPT_IFACE_B: bool = true;
181    const SUPPORT_STANDALONE_CONVERTER: bool = true;
182}
183
184impl Tcd22xxSpecification for Pfire2626Protocol {
185    const INPUTS: &'static [Input] = &[
186        Input {
187            id: SrcBlkId::Ins1,
188            offset: 0,
189            count: 8,
190            label: None,
191        },
192        Input {
193            id: SrcBlkId::Aes,
194            offset: 0,
195            count: 2,
196            label: None,
197        },
198        Input {
199            id: SrcBlkId::Adat,
200            offset: 0,
201            count: 8,
202            label: None,
203        },
204        // NOTE: share the same optical interface.
205        Input {
206            id: SrcBlkId::Adat,
207            offset: 8,
208            count: 8,
209            label: None,
210        },
211        Input {
212            id: SrcBlkId::Aes,
213            offset: 6,
214            count: 2,
215            label: None,
216        },
217    ];
218    const OUTPUTS: &'static [Output] = &[
219        Output {
220            id: DstBlkId::Ins1,
221            offset: 0,
222            count: 8,
223            label: None,
224        },
225        Output {
226            id: DstBlkId::Aes,
227            offset: 0,
228            count: 2,
229            label: None,
230        },
231        Output {
232            id: DstBlkId::Adat,
233            offset: 0,
234            count: 8,
235            label: None,
236        },
237        // NOTE: share the same optical interface.
238        Output {
239            id: DstBlkId::Adat,
240            offset: 8,
241            count: 8,
242            label: None,
243        },
244        Output {
245            id: DstBlkId::Aes,
246            offset: 6,
247            count: 2,
248            label: None,
249        },
250    ];
251    const FIXED: &'static [SrcBlk] = &[
252        SrcBlk {
253            id: SrcBlkId::Ins1,
254            ch: 0,
255        },
256        SrcBlk {
257            id: SrcBlkId::Ins1,
258            ch: 1,
259        },
260        SrcBlk {
261            id: SrcBlkId::Ins1,
262            ch: 2,
263        },
264        SrcBlk {
265            id: SrcBlkId::Ins1,
266            ch: 3,
267        },
268        SrcBlk {
269            id: SrcBlkId::Ins1,
270            ch: 4,
271        },
272        SrcBlk {
273            id: SrcBlkId::Ins1,
274            ch: 5,
275        },
276        SrcBlk {
277            id: SrcBlkId::Ins1,
278            ch: 6,
279        },
280        SrcBlk {
281            id: SrcBlkId::Ins1,
282            ch: 7,
283        },
284    ];
285}
286
287/// Protocol implementation specific to ProFire 610.
288#[derive(Default, Debug)]
289pub struct Pfire610Protocol;
290
291impl TcatOperation for Pfire610Protocol {}
292
293impl TcatGlobalSectionSpecification for Pfire610Protocol {
294    const AVAILABLE_CLOCK_SOURCE_OVERRIDE: Option<&'static [ClockSource]> =
295        Some(&[ClockSource::Aes1, ClockSource::Internal]);
296}
297
298impl TcatExtensionOperation for Pfire610Protocol {}
299
300impl PfireSpecificSpecification for Pfire610Protocol {
301    const HAS_OPT_IFACE_B: bool = false;
302    const SUPPORT_STANDALONE_CONVERTER: bool = false;
303}
304
305// NOTE: the second rx stream is firstly available at higher sampling rate.
306impl Tcd22xxSpecification for Pfire610Protocol {
307    const INPUTS: &'static [Input] = &[
308        Input {
309            id: SrcBlkId::Ins0,
310            offset: 0,
311            count: 4,
312            label: None,
313        },
314        Input {
315            id: SrcBlkId::Aes,
316            offset: 0,
317            count: 2,
318            label: None,
319        },
320    ];
321    const OUTPUTS: &'static [Output] = &[
322        Output {
323            id: DstBlkId::Ins0,
324            offset: 0,
325            count: 8,
326            label: None,
327        },
328        Output {
329            id: DstBlkId::Aes,
330            offset: 0,
331            count: 2,
332            label: None,
333        },
334    ];
335    const FIXED: &'static [SrcBlk] = &[
336        SrcBlk {
337            id: SrcBlkId::Ins0,
338            ch: 0,
339        },
340        SrcBlk {
341            id: SrcBlkId::Ins0,
342            ch: 1,
343        },
344    ];
345}
346
347/// Mode of optical interface.
348#[derive(Debug, Copy, Clone, PartialEq, Eq)]
349pub enum OptIfaceMode {
350    /// For S/PDIF signal.
351    Spdif,
352    /// For ADAT signal.
353    Adat,
354}
355
356impl Default for OptIfaceMode {
357    fn default() -> Self {
358        Self::Spdif
359    }
360}
361
362/// Mode of standalone converter.
363#[derive(Debug, Copy, Clone, PartialEq, Eq)]
364pub enum StandaloneConverterMode {
365    /// For A/D and D/A conversion.
366    AdDa,
367    /// For A/D conversion only.
368    AdOnly,
369}
370
371impl Default for StandaloneConverterMode {
372    fn default() -> Self {
373        Self::AdDa
374    }
375}
376
377/// Mode of standalone converter.
378#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
379pub struct PfireSpecificParams {
380    /// Whether volumes of 4 analog output pairs have assignment to hardware knob.
381    pub knob_assigns: [bool; 4],
382    /// Mode of optical interface B.
383    pub opt_iface_b_mode: OptIfaceMode,
384    /// Mode of converter at standalone.
385    pub standalone_mode: StandaloneConverterMode,
386}
387
388// const KNOB_ASSIGN_OFFSET: usize = 0x00;
389// const STANDALONE_MODE_OFFSET: usize = 0x04;
390
391const KNOB_ASSIGN_MASK: u32 = 0x0f;
392const OPT_IFACE_B_IS_SPDIF_FLAG: u32 = 0x10;
393const STANDALONE_CONVERTER_IS_AD_ONLY_FLAG: u32 = 0x02;
394
395const MIN_SIZE: usize = 8;
396
397fn serialize(params: &PfireSpecificParams, raw: &mut [u8]) -> Result<(), String> {
398    assert!(raw.len() >= MIN_SIZE);
399
400    let mut val = 0u32;
401    params
402        .knob_assigns
403        .iter()
404        .enumerate()
405        .filter(|(_, &v)| v)
406        .for_each(|(i, _)| val |= 1 << i);
407
408    if params.opt_iface_b_mode == OptIfaceMode::Spdif {
409        val |= OPT_IFACE_B_IS_SPDIF_FLAG;
410    }
411    serialize_u32(&val, &mut raw[..4]);
412
413    let mut val = 0u32;
414    if params.standalone_mode == StandaloneConverterMode::AdOnly {
415        val |= STANDALONE_CONVERTER_IS_AD_ONLY_FLAG;
416    }
417    serialize_u32(&val, &mut raw[4..8]);
418
419    Ok(())
420}
421
422fn deserialize(params: &mut PfireSpecificParams, raw: &[u8]) -> Result<(), String> {
423    assert!(raw.len() >= MIN_SIZE);
424
425    let mut val = 0u32;
426    deserialize_u32(&mut val, &raw[..4]);
427    params
428        .knob_assigns
429        .iter_mut()
430        .enumerate()
431        .for_each(|(i, v)| *v = (val & KNOB_ASSIGN_MASK) & (1 << i) > 0);
432
433    params.opt_iface_b_mode = if val & OPT_IFACE_B_IS_SPDIF_FLAG > 0 {
434        OptIfaceMode::Spdif
435    } else {
436        OptIfaceMode::Adat
437    };
438
439    deserialize_u32(&mut val, &raw[4..8]);
440    params.standalone_mode = if val & STANDALONE_CONVERTER_IS_AD_ONLY_FLAG > 0 {
441        StandaloneConverterMode::AdOnly
442    } else {
443        StandaloneConverterMode::AdDa
444    };
445
446    Ok(())
447}
448
449/// Protocol implementation specific to ProFire series.
450pub trait PfireSpecificSpecification {
451    const HAS_OPT_IFACE_B: bool;
452    const SUPPORT_STANDALONE_CONVERTER: bool;
453
454    const KNOB_COUNT: usize = 4;
455}
456
457impl<O: TcatExtensionOperation + PfireSpecificSpecification>
458    TcatExtensionSectionParamsOperation<PfireSpecificParams> for O
459{
460    fn cache_extension_whole_params(
461        req: &FwReq,
462        node: &FwNode,
463        sections: &ExtensionSections,
464        _: &ExtensionCaps,
465        params: &mut PfireSpecificParams,
466        timeout_ms: u32,
467    ) -> Result<(), Error> {
468        let mut raw = vec![0u8; MIN_SIZE];
469        Self::read_extension(req, node, &sections.application, 0, &mut raw, timeout_ms)?;
470        deserialize(params, &raw).map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
471    }
472}
473
474impl<O: TcatExtensionOperation + PfireSpecificSpecification>
475    TcatExtensionSectionPartialMutableParamsOperation<PfireSpecificParams> for O
476{
477    fn update_extension_partial_params(
478        req: &FwReq,
479        node: &FwNode,
480        sections: &ExtensionSections,
481        _: &ExtensionCaps,
482        params: &PfireSpecificParams,
483        prev: &mut PfireSpecificParams,
484        timeout_ms: u32,
485    ) -> Result<(), Error> {
486        let mut new = vec![0u8; MIN_SIZE];
487        serialize(params, &mut new)
488            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
489
490        let mut old = vec![0u8; MIN_SIZE];
491        serialize(prev, &mut old)
492            .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
493
494        (0..MIN_SIZE).step_by(4).try_for_each(|pos| {
495            if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
496                Self::write_extension(
497                    req,
498                    node,
499                    &sections.application,
500                    pos,
501                    &mut new[pos..(pos + 4)],
502                    timeout_ms,
503                )
504            } else {
505                Ok(())
506            }
507        })?;
508
509        deserialize(prev, &new).map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
510    }
511}
512
513#[cfg(test)]
514mod test {
515    use super::*;
516
517    #[test]
518    fn pfire_specific_params_serdes() {
519        let params = PfireSpecificParams {
520            knob_assigns: [false, true, true, false],
521            opt_iface_b_mode: OptIfaceMode::Spdif,
522            standalone_mode: StandaloneConverterMode::AdDa,
523        };
524
525        let mut raw = [0; MIN_SIZE];
526        serialize(&params, &mut raw).unwrap();
527
528        let mut p = PfireSpecificParams::default();
529        deserialize(&mut p, &raw).unwrap();
530
531        assert_eq!(params, p);
532    }
533}