firewire_motu_protocols/
version_2.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol used in version 2 devices of MOTU FireWire series.
5//!
6//! The modules includes structure, enumeration, and trait and its implementation for protocol
7//! used in version 2 devices of Mark of the Unicorn FireWire series.
8
9use super::{register_dsp::*, *};
10
11/// Signal source of sampling clock.
12#[derive(Debug, Copy, Clone, PartialEq, Eq)]
13pub enum V2ClkSrc {
14    /// Internal.
15    Internal,
16    /// S/PDIF on coaxial interface.
17    SpdifCoax,
18    /// Word clock on BNC interface.
19    WordClk,
20    /// Any signal on optical interface.
21    SignalOpt,
22    /// ADAT on optical interface.
23    AdatOpt,
24    /// ADAT on D-Sub interface.
25    AdatDsub,
26    /// AES/EBU on XLR interface.
27    AesebuXlr,
28}
29
30impl Default for V2ClkSrc {
31    fn default() -> Self {
32        Self::Internal
33    }
34}
35
36/// The parameters of media and sampling clocks.
37#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
38pub struct Version2ClockParameters {
39    /// The rate of media clock.
40    pub rate: ClkRate,
41    /// The source of sampling clock.
42    pub source: V2ClkSrc,
43}
44
45const CLK_RATE_LABEL: &str = "clock-rate-v2";
46const CLK_RATE_MASK: u32 = 0x00000038;
47const CLK_RATE_SHIFT: usize = 3;
48
49const CLK_SRC_LABEL: &str = "clock-source-v2";
50const CLK_SRC_MASK: u32 = 0x00000007;
51const CLK_SRC_SHIFT: usize = 0;
52
53/// The trait for specification of sampling and media clocks.
54pub trait MotuVersion2ClockSpecification {
55    const CLK_RATES: &'static [ClkRate];
56    const CLK_RATE_VALS: &'static [u8];
57
58    const CLK_SRCS: &'static [V2ClkSrc];
59    const CLK_SRC_VALS: &'static [u8];
60}
61
62impl<O> MotuWhollyCacheableParamsOperation<Version2ClockParameters> for O
63where
64    O: MotuVersion2ClockSpecification,
65{
66    fn cache_wholly(
67        req: &mut FwReq,
68        node: &mut FwNode,
69        params: &mut Version2ClockParameters,
70        timeout_ms: u32,
71    ) -> Result<(), Error> {
72        let quad = read_quad(req, node, OFFSET_CLK, timeout_ms)?;
73
74        deserialize_flag(
75            &mut params.rate,
76            &quad,
77            CLK_RATE_MASK,
78            CLK_RATE_SHIFT,
79            Self::CLK_RATES,
80            Self::CLK_RATE_VALS,
81            CLK_RATE_LABEL,
82        )?;
83
84        deserialize_flag(
85            &mut params.source,
86            &quad,
87            CLK_SRC_MASK,
88            CLK_SRC_SHIFT,
89            Self::CLK_SRCS,
90            Self::CLK_SRC_VALS,
91            CLK_SRC_LABEL,
92        )
93    }
94}
95
96impl<O> MotuWhollyUpdatableParamsOperation<Version2ClockParameters> for O
97where
98    O: MotuVersion2ClockSpecification,
99{
100    fn update_wholly(
101        req: &mut FwReq,
102        node: &mut FwNode,
103        params: &Version2ClockParameters,
104        timeout_ms: u32,
105    ) -> Result<(), Error> {
106        let mut quad = read_quad(req, node, OFFSET_CLK, timeout_ms)?;
107
108        serialize_flag(
109            &params.rate,
110            &mut quad,
111            CLK_RATE_MASK,
112            CLK_RATE_SHIFT,
113            Self::CLK_RATES,
114            Self::CLK_RATE_VALS,
115            CLK_RATE_LABEL,
116        )?;
117
118        serialize_flag(
119            &params.source,
120            &mut quad,
121            CLK_SRC_MASK,
122            CLK_SRC_SHIFT,
123            Self::CLK_SRCS,
124            Self::CLK_SRC_VALS,
125            CLK_SRC_LABEL,
126        )?;
127
128        write_quad(req, node, OFFSET_CLK, quad, timeout_ms)
129    }
130}
131
132/// Mode of optical interface.
133#[derive(Debug, Copy, Clone, PartialEq, Eq)]
134pub enum V2OptIfaceMode {
135    None,
136    Adat,
137    Spdif,
138}
139
140impl Default for V2OptIfaceMode {
141    fn default() -> Self {
142        Self::None
143    }
144}
145
146/// The parameters of optical interfaces.
147#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
148pub struct Version2OpticalIfaceParameters {
149    /// The mode of signal in optical input interface.
150    pub input_mode: V2OptIfaceMode,
151    /// The mode of signal in optical output interface.
152    pub output_mode: V2OptIfaceMode,
153}
154
155const OPT_IN_IFACE_LABEL: &str = "optical-input-iface-v2";
156const OPT_IN_IFACE_MASK: u32 = 0x00000300;
157const OPT_IN_IFACE_SHIFT: usize = 8;
158
159const OPT_OUT_IFACE_LABEL: &str = "optical-output-iface-v2";
160const OPT_OUT_IFACE_MASK: u32 = 0x00000c00;
161const OPT_OUT_IFACE_SHIFT: usize = 10;
162
163const OPT_IFACE_MODE_VALS: &[u8] = &[0x00, 0x01, 0x02];
164
165/// The trait for specificification of mode of optical input and output interfaces.
166pub trait MotuVersion2OpticalIfaceSpecification {
167    const OPT_IFACE_MODES: &'static [V2OptIfaceMode];
168}
169
170impl<O> MotuWhollyCacheableParamsOperation<Version2OpticalIfaceParameters> for O
171where
172    O: MotuVersion2OpticalIfaceSpecification,
173{
174    fn cache_wholly(
175        req: &mut FwReq,
176        node: &mut FwNode,
177        params: &mut Version2OpticalIfaceParameters,
178        timeout_ms: u32,
179    ) -> Result<(), Error> {
180        let quad = read_quad(req, node, OFFSET_PORT, timeout_ms)?;
181
182        deserialize_flag(
183            &mut params.input_mode,
184            &quad,
185            OPT_IN_IFACE_MASK,
186            OPT_IN_IFACE_SHIFT,
187            Self::OPT_IFACE_MODES,
188            OPT_IFACE_MODE_VALS,
189            OPT_IN_IFACE_LABEL,
190        )?;
191
192        deserialize_flag(
193            &mut params.output_mode,
194            &quad,
195            OPT_OUT_IFACE_MASK,
196            OPT_OUT_IFACE_SHIFT,
197            Self::OPT_IFACE_MODES,
198            OPT_IFACE_MODE_VALS,
199            OPT_OUT_IFACE_LABEL,
200        )
201    }
202}
203
204impl<O> MotuWhollyUpdatableParamsOperation<Version2OpticalIfaceParameters> for O
205where
206    O: MotuVersion2OpticalIfaceSpecification,
207{
208    fn update_wholly(
209        req: &mut FwReq,
210        node: &mut FwNode,
211        params: &Version2OpticalIfaceParameters,
212        timeout_ms: u32,
213    ) -> Result<(), Error> {
214        let mut quad = read_quad(req, node, OFFSET_PORT, timeout_ms)?;
215
216        serialize_flag(
217            &params.input_mode,
218            &mut quad,
219            OPT_IN_IFACE_MASK,
220            OPT_IN_IFACE_SHIFT,
221            Self::OPT_IFACE_MODES,
222            OPT_IFACE_MODE_VALS,
223            OPT_IN_IFACE_LABEL,
224        )?;
225
226        serialize_flag(
227            &params.output_mode,
228            &mut quad,
229            OPT_OUT_IFACE_MASK,
230            OPT_OUT_IFACE_SHIFT,
231            Self::OPT_IFACE_MODES,
232            OPT_IFACE_MODE_VALS,
233            OPT_OUT_IFACE_LABEL,
234        )?;
235
236        write_quad(req, node, OFFSET_PORT, quad, timeout_ms)
237    }
238}
239
240/// The protocol implementation for 828mkII.
241#[derive(Default)]
242pub struct F828mk2Protocol;
243
244impl MotuPortAssignSpecification for F828mk2Protocol {
245    const ASSIGN_PORT_TARGETS: &'static [TargetPort] = &[
246        TargetPort::PhonePair,     // = Stream-0/1
247        TargetPort::AnalogPair(0), // = Stream-2/3
248        TargetPort::AnalogPair(1), // = Stream-4/5
249        TargetPort::AnalogPair(2), // = Stream-6/7
250        TargetPort::AnalogPair(3), // = Stream-8/9
251        TargetPort::MainPair,      // = Stream-10/11
252        TargetPort::SpdifPair,     // = Stream-12/13
253        TargetPort::AdatPair(0),   // = Stream-14/15
254        TargetPort::AdatPair(1),   // = Stream-16/17
255        TargetPort::AdatPair(2),   // = Stream-18/19
256        TargetPort::AdatPair(3),   // = Stream-20/21
257    ];
258
259    const ASSIGN_PORT_VALS: &'static [u8] = &[
260        0x01, // = Stream-0/1
261        0x02, // = Stream-2/3
262        0x03, // = Stream-4/5
263        0x04, // = Stream-6/7
264        0x05, // = Stream-8/9
265        0x06, // = Stream-10/11
266        0x07, // = Stream-12/13
267        0x08, // = Stream-14/15
268        0x09, // = Stream-16/17
269        0x0a, // = Stream-18/19
270        0x0b, // = Stream-20/21
271    ];
272}
273
274impl MotuWordClockOutputSpecification for F828mk2Protocol {}
275
276impl MotuClockNameDisplaySpecification for F828mk2Protocol {}
277
278impl MotuVersion2ClockSpecification for F828mk2Protocol {
279    const CLK_RATES: &'static [ClkRate] = &[
280        ClkRate::R44100,
281        ClkRate::R48000,
282        ClkRate::R88200,
283        ClkRate::R96000,
284    ];
285    const CLK_RATE_VALS: &'static [u8] = &[0x00, 0x01, 0x02, 0x03];
286
287    const CLK_SRCS: &'static [V2ClkSrc] = &[
288        V2ClkSrc::Internal,
289        V2ClkSrc::SignalOpt,
290        V2ClkSrc::SpdifCoax,
291        V2ClkSrc::WordClk,
292        V2ClkSrc::AdatDsub,
293    ];
294    const CLK_SRC_VALS: &'static [u8] = &[0x00, 0x01, 0x02, 0x04, 0x05];
295}
296
297impl MotuVersion2OpticalIfaceSpecification for F828mk2Protocol {
298    const OPT_IFACE_MODES: &'static [V2OptIfaceMode] = &[
299        V2OptIfaceMode::None,
300        V2OptIfaceMode::Adat,
301        V2OptIfaceMode::Spdif,
302    ];
303}
304
305impl MotuRegisterDspSpecification for F828mk2Protocol {
306    const MIXER_OUTPUT_DESTINATIONS: &'static [TargetPort] = &[
307        TargetPort::Disabled,
308        TargetPort::PhonePair,
309        TargetPort::AnalogPair(0),
310        TargetPort::AnalogPair(1),
311        TargetPort::AnalogPair(2),
312        TargetPort::AnalogPair(3),
313        TargetPort::MainPair,
314        TargetPort::SpdifPair,
315        TargetPort::AdatPair(0),
316        TargetPort::AdatPair(1),
317        TargetPort::AdatPair(2),
318        TargetPort::AdatPair(3),
319    ];
320}
321
322impl MotuRegisterDspMixerMonauralSourceSpecification for F828mk2Protocol {
323    const MIXER_SOURCES: &'static [TargetPort] = &[
324        TargetPort::Analog(0),
325        TargetPort::Analog(1),
326        TargetPort::Analog(2),
327        TargetPort::Analog(3),
328        TargetPort::Analog(4),
329        TargetPort::Analog(5),
330        TargetPort::Analog(6),
331        TargetPort::Analog(7),
332        TargetPort::Analog(8), // Mic-0
333        TargetPort::Analog(9), // Mic-1
334        TargetPort::Spdif(0),
335        TargetPort::Spdif(1),
336        TargetPort::Adat(0),
337        TargetPort::Adat(1),
338        TargetPort::Adat(2),
339        TargetPort::Adat(3),
340        TargetPort::Adat(4),
341        TargetPort::Adat(5),
342        TargetPort::Adat(6),
343        TargetPort::Adat(7),
344    ];
345}
346
347impl MotuRegisterDspLineInputSpecification for F828mk2Protocol {
348    const LINE_INPUT_COUNT: usize = 8;
349    const CH_OFFSET: usize = 0;
350}
351
352impl F828mk2Protocol {
353    /// Notification mask for speed of word clock output, and phone assignment. The change of phone
354    /// assignment is also notified in message delivered by the sequence of isochronous packets.
355    pub const NOTIFY_PORT_CHANGE: u32 = 0x40000000;
356
357    /// Notification mask for footswitch.
358    pub const NOTIFY_FOOTSWITCH_MASK: u32 = 0x01000000;
359}
360
361impl MotuRegisterDspMeterSpecification for F828mk2Protocol {
362    const INPUT_PORTS: &'static [TargetPort] = &[
363        TargetPort::Analog(0),
364        TargetPort::Analog(1),
365        TargetPort::Analog(2),
366        TargetPort::Analog(3),
367        TargetPort::Analog(4),
368        TargetPort::Analog(5),
369        TargetPort::Analog(6),
370        TargetPort::Analog(7),
371        TargetPort::Analog(8),
372        TargetPort::Analog(9),
373        TargetPort::Spdif(0),
374        TargetPort::Spdif(1),
375        TargetPort::Adat(0),
376        TargetPort::Adat(1),
377        TargetPort::Adat(2),
378        TargetPort::Adat(3),
379        TargetPort::Adat(4),
380        TargetPort::Adat(5),
381        TargetPort::Adat(6),
382        TargetPort::Adat(7),
383    ];
384    const OUTPUT_PORT_PAIRS: &'static [TargetPort] = &[
385        TargetPort::PhonePair,
386        TargetPort::AnalogPair(0),
387        TargetPort::AnalogPair(1),
388        TargetPort::AnalogPair(2),
389        TargetPort::AnalogPair(3),
390        TargetPort::MainPair,
391        TargetPort::SpdifPair,
392        TargetPort::AdatPair(0),
393        TargetPort::AdatPair(1),
394        TargetPort::AdatPair(2),
395        TargetPort::AdatPair(3),
396    ];
397    const OUTPUT_PORT_PAIR_POS: &'static [[usize; 2]] = &[
398        [2, 3],
399        [4, 5],
400        [6, 7],
401        [8, 9],
402        [10, 11],
403        [12, 13],
404        [14, 15],
405        [16, 17],
406        [18, 19],
407        [20, 21],
408        [22, 23],
409    ];
410}
411
412impl MotuRegisterDspMeterOutputTargetSpecification for F828mk2Protocol {}
413
414/// The protocol implementation for 8pre.
415#[derive(Default)]
416pub struct F8preProtocol;
417
418impl MotuPortAssignSpecification for F8preProtocol {
419    const ASSIGN_PORT_TARGETS: &'static [TargetPort] =
420        &[TargetPort::PhonePair, TargetPort::MainPair];
421    const ASSIGN_PORT_VALS: &'static [u8] = &[0x01, 0x02];
422}
423
424impl MotuVersion2ClockSpecification for F8preProtocol {
425    const CLK_RATES: &'static [ClkRate] = &[
426        ClkRate::R44100,
427        ClkRate::R48000,
428        ClkRate::R88200,
429        ClkRate::R96000,
430    ];
431    const CLK_RATE_VALS: &'static [u8] = &[0x00, 0x01, 0x02, 0x03];
432
433    const CLK_SRCS: &'static [V2ClkSrc] = &[V2ClkSrc::Internal, V2ClkSrc::AdatOpt];
434    const CLK_SRC_VALS: &'static [u8] = &[0x00, 0x01];
435}
436
437impl MotuVersion2OpticalIfaceSpecification for F8preProtocol {
438    const OPT_IFACE_MODES: &'static [V2OptIfaceMode] =
439        &[V2OptIfaceMode::None, V2OptIfaceMode::Adat];
440}
441
442impl MotuRegisterDspSpecification for F8preProtocol {
443    const MIXER_OUTPUT_DESTINATIONS: &'static [TargetPort] = &[
444        TargetPort::Disabled,
445        TargetPort::PhonePair,
446        TargetPort::MainPair,
447        TargetPort::AdatPair(0),
448        TargetPort::AdatPair(1),
449        TargetPort::AdatPair(2),
450        TargetPort::AdatPair(3),
451    ];
452}
453
454impl MotuRegisterDspMixerMonauralSourceSpecification for F8preProtocol {
455    const MIXER_SOURCES: &'static [TargetPort] = &[
456        TargetPort::Analog(0),
457        TargetPort::Analog(1),
458        TargetPort::Analog(2),
459        TargetPort::Analog(3),
460        TargetPort::Analog(4),
461        TargetPort::Analog(5),
462        TargetPort::Analog(6),
463        TargetPort::Analog(7),
464        TargetPort::Adat(0),
465        TargetPort::Adat(1),
466        TargetPort::Adat(2),
467        TargetPort::Adat(3),
468        TargetPort::Adat(4),
469        TargetPort::Adat(5),
470        TargetPort::Adat(6),
471        TargetPort::Adat(7),
472    ];
473}
474
475impl MotuRegisterDspMeterSpecification for F8preProtocol {
476    const INPUT_PORTS: &'static [TargetPort] = &[
477        TargetPort::Analog(0),
478        TargetPort::Analog(1),
479        TargetPort::Analog(2),
480        TargetPort::Analog(3),
481        TargetPort::Analog(4),
482        TargetPort::Analog(5),
483        TargetPort::Analog(6),
484        TargetPort::Analog(7),
485        TargetPort::Adat(0),
486        TargetPort::Adat(1),
487        TargetPort::Adat(2),
488        TargetPort::Adat(3),
489        TargetPort::Adat(4),
490        TargetPort::Adat(5),
491        TargetPort::Adat(6),
492        TargetPort::Adat(7),
493    ];
494    const OUTPUT_PORT_PAIRS: &'static [TargetPort] = &[
495        TargetPort::PhonePair,
496        TargetPort::AnalogPair(0),
497        TargetPort::AdatPair(0),
498        TargetPort::AdatPair(1),
499        TargetPort::AdatPair(2),
500        TargetPort::AdatPair(3),
501    ];
502    const OUTPUT_PORT_PAIR_POS: &'static [[usize; 2]] =
503        &[[2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]];
504}
505
506/// The protocol implementation for Traveler.
507#[derive(Default)]
508pub struct TravelerProtocol;
509
510impl MotuPortAssignSpecification for TravelerProtocol {
511    const ASSIGN_PORT_TARGETS: &'static [TargetPort] = &[
512        TargetPort::PhonePair,     // = Stream-0/1
513        TargetPort::AnalogPair(0), // = Stream-2/3
514        TargetPort::AnalogPair(1), // = Stream-4/5
515        TargetPort::AnalogPair(2), // = Stream-6/7
516        TargetPort::AnalogPair(3), // = Stream-8/9
517        TargetPort::AesEbuPair,    // = Stream-10/11
518        TargetPort::SpdifPair,     // = Stream-12/13
519        TargetPort::AdatPair(0),   // = Stream-14/15
520        TargetPort::AdatPair(1),   // = Stream-16/17
521        TargetPort::AdatPair(2),   // = Stream-18/19
522        TargetPort::AdatPair(3),   // = Stream-20/21
523    ];
524
525    const ASSIGN_PORT_VALS: &'static [u8] = &[
526        0x01, // = Stream-0/1
527        0x02, // = Stream-2/3
528        0x03, // = Stream-4/5
529        0x04, // = Stream-6/7
530        0x05, // = Stream-8/9
531        0x06, // = Stream-10/11
532        0x07, // = Stream-12/13
533        0x08, // = Stream-14/15
534        0x09, // = Stream-16/17
535        0x0a, // = Stream-18/19
536        0x0b, // = Stream-20/21
537    ];
538}
539
540impl MotuWordClockOutputSpecification for TravelerProtocol {}
541
542impl MotuClockNameDisplaySpecification for TravelerProtocol {}
543
544impl MotuVersion2ClockSpecification for TravelerProtocol {
545    const CLK_RATES: &'static [ClkRate] = &[
546        ClkRate::R44100,
547        ClkRate::R48000,
548        ClkRate::R88200,
549        ClkRate::R96000,
550        ClkRate::R176400,
551        ClkRate::R192000,
552    ];
553    const CLK_RATE_VALS: &'static [u8] = &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
554
555    const CLK_SRCS: &'static [V2ClkSrc] = &[
556        V2ClkSrc::Internal,
557        V2ClkSrc::SignalOpt,
558        V2ClkSrc::SpdifCoax,
559        V2ClkSrc::WordClk,
560        V2ClkSrc::AdatDsub,
561        V2ClkSrc::AesebuXlr,
562    ];
563    const CLK_SRC_VALS: &'static [u8] = &[0x00, 0x01, 0x02, 0x04, 0x05, 0x07];
564}
565
566impl MotuVersion2OpticalIfaceSpecification for TravelerProtocol {
567    const OPT_IFACE_MODES: &'static [V2OptIfaceMode] = &[
568        V2OptIfaceMode::None,
569        V2OptIfaceMode::Adat,
570        V2OptIfaceMode::Spdif,
571    ];
572}
573
574impl MotuRegisterDspSpecification for TravelerProtocol {
575    const MIXER_OUTPUT_DESTINATIONS: &'static [TargetPort] = &[
576        TargetPort::Disabled,
577        TargetPort::PhonePair,
578        TargetPort::AnalogPair(0),
579        TargetPort::AnalogPair(1),
580        TargetPort::AnalogPair(2),
581        TargetPort::AnalogPair(3),
582        TargetPort::AesEbuPair,
583        TargetPort::SpdifPair,
584        TargetPort::AdatPair(0),
585        TargetPort::AdatPair(1),
586        TargetPort::AdatPair(2),
587        TargetPort::AdatPair(3),
588    ];
589}
590
591impl MotuRegisterDspMixerMonauralSourceSpecification for TravelerProtocol {
592    const MIXER_SOURCES: &'static [TargetPort] = &[
593        TargetPort::Analog(0),
594        TargetPort::Analog(1),
595        TargetPort::Analog(2),
596        TargetPort::Analog(3),
597        TargetPort::Analog(4),
598        TargetPort::Analog(5),
599        TargetPort::Analog(6),
600        TargetPort::Analog(7),
601        TargetPort::AesEbu(0),
602        TargetPort::AesEbu(1),
603        TargetPort::Spdif(0),
604        TargetPort::Spdif(1),
605        TargetPort::Adat(0),
606        TargetPort::Adat(1),
607        TargetPort::Adat(2),
608        TargetPort::Adat(3),
609        TargetPort::Adat(4),
610        TargetPort::Adat(5),
611        TargetPort::Adat(6),
612        TargetPort::Adat(7),
613    ];
614}
615
616impl MotuRegisterDspLineInputSpecification for TravelerProtocol {
617    const LINE_INPUT_COUNT: usize = 4;
618    const CH_OFFSET: usize = 4;
619}
620
621impl MotuRegisterDspMeterSpecification for TravelerProtocol {
622    const INPUT_PORTS: &'static [TargetPort] = &[
623        TargetPort::Analog(0),
624        TargetPort::Analog(1),
625        TargetPort::Analog(2),
626        TargetPort::Analog(3),
627        TargetPort::Analog(4),
628        TargetPort::Analog(5),
629        TargetPort::Analog(6),
630        TargetPort::Analog(7),
631        TargetPort::AesEbu(8),
632        TargetPort::AesEbu(9),
633        TargetPort::Spdif(0),
634        TargetPort::Spdif(1),
635        TargetPort::Adat(0),
636        TargetPort::Adat(1),
637        TargetPort::Adat(2),
638        TargetPort::Adat(3),
639        TargetPort::Adat(4),
640        TargetPort::Adat(5),
641        TargetPort::Adat(6),
642        TargetPort::Adat(7),
643    ];
644    const OUTPUT_PORT_PAIRS: &'static [TargetPort] = &[
645        TargetPort::PhonePair,
646        TargetPort::AnalogPair(0),
647        TargetPort::AnalogPair(1),
648        TargetPort::AnalogPair(2),
649        TargetPort::AnalogPair(3),
650        TargetPort::AesEbuPair,
651        TargetPort::SpdifPair,
652        TargetPort::AdatPair(0),
653        TargetPort::AdatPair(1),
654        TargetPort::AdatPair(2),
655        TargetPort::AdatPair(3),
656    ];
657    const OUTPUT_PORT_PAIR_POS: &'static [[usize; 2]] = &[
658        [2, 3],
659        [4, 5],
660        [6, 7],
661        [8, 9],
662        [10, 11],
663        [12, 13],
664        [14, 15],
665        [16, 17],
666        [18, 19],
667        [20, 21],
668        [22, 23],
669    ];
670}
671
672impl MotuRegisterDspMeterOutputTargetSpecification for TravelerProtocol {}
673
674/// State of inputs in Traveler.
675#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
676pub struct TravelerMicInputState {
677    /// The gain of microphone input. The value is between 0x00 and 0x35.
678    pub gain: [u8; TravelerProtocol::MIC_INPUT_COUNT],
679    /// Whether to pad microphone input.
680    pub pad: [bool; TravelerProtocol::MIC_INPUT_COUNT],
681}
682
683const TRAVELER_MIC_PARAM_OFFSET: usize = 0x0c1c;
684const TRAVELER_MIC_GAIN_MASK: u8 = 0x3f;
685const TRAVELER_MIC_PAD_FLAG: u8 = 0x40;
686const TRAVELER_MIC_CHANGE_FLAG: u8 = 0x80;
687
688impl TravelerProtocol {
689    /// Notification mask for mic gain, and pad.
690    pub const NOTIFY_MIC_PARAM_MASK: u32 = 0x20000000;
691
692    /// Notification mask for speed of word clock output, phone assignment.
693    pub const NOTIFY_PORT_CHANGE: u32 = 0x40000000;
694
695    /// Notification mask for signal format of optical input/output interfaces.
696    pub const NOTIFY_FORMAT_CHANGE: u32 = 0x08000000;
697
698    /// The number of microphone inputs.
699    pub const MIC_INPUT_COUNT: usize = 4;
700
701    /// The minimum value of microphone input.
702    pub const MIC_GAIN_MIN: u8 = 0x00;
703    /// The maximum value of microphone input.
704    pub const MIC_GAIN_MAX: u8 = 0x35;
705    /// The step value of microphone input.
706    pub const MIC_GAIN_STEP: u8 = 0x01;
707}
708
709impl MotuWhollyCacheableParamsOperation<TravelerMicInputState> for TravelerProtocol {
710    fn cache_wholly(
711        req: &mut FwReq,
712        node: &mut FwNode,
713        params: &mut TravelerMicInputState,
714        timeout_ms: u32,
715    ) -> Result<(), Error> {
716        read_quad(req, node, TRAVELER_MIC_PARAM_OFFSET as u32, timeout_ms).map(|val| {
717            (0..Self::MIC_INPUT_COUNT).for_each(|i| {
718                let v = ((val >> (i * 8)) & 0xff) as u8;
719                params.gain[i] = v & TRAVELER_MIC_GAIN_MASK;
720                params.pad[i] = v & TRAVELER_MIC_PAD_FLAG > 0;
721            });
722        })
723    }
724}
725
726impl MotuWhollyUpdatableParamsOperation<TravelerMicInputState> for TravelerProtocol {
727    fn update_wholly(
728        req: &mut FwReq,
729        node: &mut FwNode,
730        params: &TravelerMicInputState,
731        timeout_ms: u32,
732    ) -> Result<(), Error> {
733        let val = (0..Self::MIC_INPUT_COUNT).fold(0u32, |val, i| {
734            let mut v = TRAVELER_MIC_CHANGE_FLAG;
735            if params.pad[i] {
736                v |= TRAVELER_MIC_PAD_FLAG;
737            }
738            v |= params.gain[i] & TRAVELER_MIC_GAIN_MASK;
739            val | ((v as u32) << (i * 8))
740        });
741        write_quad(req, node, TRAVELER_MIC_PARAM_OFFSET as u32, val, timeout_ms)
742    }
743}
744
745/// The protocol implementation for Ultralite.
746#[derive(Default)]
747pub struct UltraliteProtocol;
748
749impl MotuPortAssignSpecification for UltraliteProtocol {
750    const ASSIGN_PORT_TARGETS: &'static [TargetPort] = &[
751        TargetPort::PhonePair,     // Stream-0/1
752        TargetPort::AnalogPair(0), // Stream-2/3
753        TargetPort::AnalogPair(1), // Stream-4/5
754        TargetPort::AnalogPair(2), // Stream-6/7
755        TargetPort::AnalogPair(3), // Stream-8/9
756        TargetPort::MainPair,      // Stream-10/11
757        TargetPort::SpdifPair,     // Stream-12/13
758    ];
759
760    const ASSIGN_PORT_VALS: &'static [u8] = &[
761        0x01, // Stream-0/1
762        0x02, // Stream-2/3
763        0x03, // Stream-4/5
764        0x04, // Stream-6/7
765        0x05, // Stream-8/9
766        0x06, // Stream-10/11
767        0x07, // Stream-12/13
768    ];
769}
770
771impl MotuClockNameDisplaySpecification for UltraliteProtocol {}
772
773impl MotuVersion2ClockSpecification for UltraliteProtocol {
774    const CLK_RATES: &'static [ClkRate] = &[
775        ClkRate::R44100,
776        ClkRate::R48000,
777        ClkRate::R88200,
778        ClkRate::R96000,
779    ];
780    const CLK_RATE_VALS: &'static [u8] = &[0x00, 0x01, 0x02, 0x03];
781
782    const CLK_SRCS: &'static [V2ClkSrc] = &[V2ClkSrc::Internal, V2ClkSrc::SpdifCoax];
783    const CLK_SRC_VALS: &'static [u8] = &[0x00, 0x02];
784}
785
786impl MotuRegisterDspSpecification for UltraliteProtocol {
787    const MIXER_OUTPUT_DESTINATIONS: &'static [TargetPort] = &[
788        TargetPort::Disabled,
789        TargetPort::PhonePair,
790        TargetPort::AnalogPair(0),
791        TargetPort::AnalogPair(1),
792        TargetPort::AnalogPair(2),
793        TargetPort::AnalogPair(3),
794        TargetPort::MainPair,
795        TargetPort::SpdifPair,
796    ];
797}
798
799impl MotuRegisterDspMixerMonauralSourceSpecification for UltraliteProtocol {
800    const MIXER_SOURCES: &'static [TargetPort] = &[
801        TargetPort::Analog(0),
802        TargetPort::Analog(1),
803        TargetPort::Analog(2),
804        TargetPort::Analog(3),
805        TargetPort::Analog(4),
806        TargetPort::Analog(5),
807        TargetPort::Analog(6),
808        TargetPort::Analog(7),
809        TargetPort::Spdif(0),
810        TargetPort::Spdif(1),
811    ];
812}
813
814impl MotuRegisterDspMonauralInputSpecification for UltraliteProtocol {}
815
816impl MotuRegisterDspMeterSpecification for UltraliteProtocol {
817    const INPUT_PORTS: &'static [TargetPort] = &[
818        TargetPort::Analog(0),
819        TargetPort::Analog(1),
820        TargetPort::Analog(2),
821        TargetPort::Analog(3),
822        TargetPort::Analog(4),
823        TargetPort::Analog(5),
824        TargetPort::Analog(6),
825        TargetPort::Analog(7),
826        TargetPort::Spdif(0),
827        TargetPort::Spdif(1),
828    ];
829    const OUTPUT_PORT_PAIRS: &'static [TargetPort] = &[
830        TargetPort::PhonePair,
831        TargetPort::AnalogPair(0),
832        TargetPort::AnalogPair(1),
833        TargetPort::AnalogPair(2),
834        TargetPort::AnalogPair(3),
835        TargetPort::MainPair,
836        TargetPort::SpdifPair,
837    ];
838    const OUTPUT_PORT_PAIR_POS: &'static [[usize; 2]] =
839        &[[2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13], [14, 15]];
840}
841
842/// The parameter of assignment to main output pair in Ultralite.
843#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
844pub struct UltraliteMainAssign(pub TargetPort);
845
846const ULTRALITE_MAIN_ASSIGN_MASK: u32 = 0x000f0000;
847const ULTRALITE_MAIN_ASSIGN_SHIFT: usize = 16;
848const ULTRALITE_MAIN_ASSIGN_LABEL: &str = "ultralite-main-assign";
849
850impl UltraliteProtocol {
851    /// Notification mask for main assignment, and phone assignment. The change of phone assignment
852    /// is also notified in message delivered by the sequence of isochronous packets.
853    pub const NOTIFY_PORT_CHANGE: u32 = 0x40000000;
854
855    /// The target of knob control.
856    pub const KNOB_TARGETS: &'static [TargetPort] = &[
857        TargetPort::MainPair,
858        TargetPort::Analog6Pairs,
859        TargetPort::Analog8Pairs,
860        TargetPort::SpdifPair,
861    ];
862
863    /// The number of inputs.
864    pub const INPUT_COUNT: usize = 10;
865
866    /// The minimum value of input.
867    pub const INPUT_GAIN_MIN: u8 = 0x00;
868    /// The maximum value of input.
869    pub const INPUT_GAIN_MAX: u8 = 0x18;
870    /// The step value of input.
871    pub const INPUT_GAIN_STEP: u8 = 0x01;
872}
873
874const KNOB_TARGET_VALS: &[u8] = &[0x00, 0x01, 0x02, 0x03];
875
876impl MotuWhollyCacheableParamsOperation<UltraliteMainAssign> for UltraliteProtocol {
877    fn cache_wholly(
878        req: &mut FwReq,
879        node: &mut FwNode,
880        params: &mut UltraliteMainAssign,
881        timeout_ms: u32,
882    ) -> Result<(), Error> {
883        let quad = read_quad(req, node, OFFSET_PORT, timeout_ms)?;
884        deserialize_flag(
885            &mut params.0,
886            &quad,
887            ULTRALITE_MAIN_ASSIGN_MASK,
888            ULTRALITE_MAIN_ASSIGN_SHIFT,
889            Self::KNOB_TARGETS,
890            KNOB_TARGET_VALS,
891            ULTRALITE_MAIN_ASSIGN_LABEL,
892        )
893    }
894}
895
896impl MotuWhollyUpdatableParamsOperation<UltraliteMainAssign> for UltraliteProtocol {
897    /// Update whole parameters.
898    fn update_wholly(
899        req: &mut FwReq,
900        node: &mut FwNode,
901        params: &UltraliteMainAssign,
902        timeout_ms: u32,
903    ) -> Result<(), Error> {
904        let mut quad = read_quad(req, node, OFFSET_PORT, timeout_ms)?;
905        serialize_flag(
906            &params.0,
907            &mut quad,
908            ULTRALITE_MAIN_ASSIGN_MASK,
909            ULTRALITE_MAIN_ASSIGN_SHIFT,
910            Self::KNOB_TARGETS,
911            KNOB_TARGET_VALS,
912            ULTRALITE_MAIN_ASSIGN_LABEL,
913        )?;
914        write_quad(req, node, OFFSET_PORT, quad, timeout_ms)
915    }
916}
917
918/// The protocol implementation for 896HD.
919#[derive(Default)]
920pub struct F896hdProtocol;
921
922impl F896hdProtocol {
923    /// Notification mask for programmable meter.
924    pub const NOTIFY_PROGRAMMABLE_METER_MASK: u32 = 0x40000000;
925
926    /// Notification mask for footswitch.
927    pub const NOTIFY_FOOTSWITCH_MASK: u32 = 0x01000000;
928}
929
930impl MotuPortAssignSpecification for F896hdProtocol {
931    const ASSIGN_PORT_TARGETS: &'static [TargetPort] = &[
932        TargetPort::PhonePair,     // Stream-0/1
933        TargetPort::AnalogPair(0), // Stream-2/3
934        TargetPort::AnalogPair(1), // Stream-4/5
935        TargetPort::AnalogPair(2), // Stream-6/7
936        TargetPort::AnalogPair(3), // Stream-8/9
937        TargetPort::MainPair,      // Stream-10/11
938        TargetPort::AesEbuPair,    // Stream-12/13
939        TargetPort::AdatPair(0),   // Stream-14/15
940        TargetPort::AdatPair(1),   // Stream-16/17
941        TargetPort::AdatPair(2),   // Stream-18/19
942        TargetPort::AdatPair(3),   // Stream-20/21
943    ];
944
945    const ASSIGN_PORT_VALS: &'static [u8] = &[
946        0x01, // Stream-0/1
947        0x02, // Stream-2/3
948        0x03, // Stream-4/5
949        0x04, // Stream-6/7
950        0x05, // Stream-8/9
951        0x06, // Stream-10/11
952        0x07, // Stream-12/13
953        0x08, // Stream-14/15
954        0x09, // Stream-16/17
955        0x0a, // Stream-18/19
956        0x0b, // Stream-20/21
957    ];
958}
959
960impl MotuWordClockOutputSpecification for F896hdProtocol {}
961
962impl MotuAesebuRateConvertSpecification for F896hdProtocol {
963    const AESEBU_RATE_CONVERT_MASK: u32 = 0x00000300;
964    const AESEBU_RATE_CONVERT_SHIFT: usize = 8;
965}
966
967impl MotuLevelMetersSpecification for F896hdProtocol {
968    const LEVEL_METERS_PROGRAMMABLE_MODES: &'static [LevelMetersProgrammableMode] = &[
969        LevelMetersProgrammableMode::AnalogOutput,
970        LevelMetersProgrammableMode::AdatAInput,
971        LevelMetersProgrammableMode::AdatAOutput,
972    ];
973}
974
975impl MotuVersion2ClockSpecification for F896hdProtocol {
976    const CLK_RATES: &'static [ClkRate] = &[
977        ClkRate::R44100,
978        ClkRate::R48000,
979        ClkRate::R88200,
980        ClkRate::R96000,
981        ClkRate::R176400,
982        ClkRate::R192000,
983    ];
984    const CLK_RATE_VALS: &'static [u8] = &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
985
986    const CLK_SRCS: &'static [V2ClkSrc] = &[
987        V2ClkSrc::Internal,
988        V2ClkSrc::AdatOpt,
989        V2ClkSrc::AesebuXlr,
990        V2ClkSrc::WordClk,
991        V2ClkSrc::AdatDsub,
992    ];
993    const CLK_SRC_VALS: &'static [u8] = &[0x00, 0x01, 0x02, 0x04, 0x05];
994}
995
996impl MotuVersion2OpticalIfaceSpecification for F896hdProtocol {
997    const OPT_IFACE_MODES: &'static [V2OptIfaceMode] =
998        &[V2OptIfaceMode::None, V2OptIfaceMode::Adat];
999}
1000
1001impl MotuRegisterDspSpecification for F896hdProtocol {
1002    const MIXER_OUTPUT_DESTINATIONS: &'static [TargetPort] = &[
1003        TargetPort::Disabled,
1004        TargetPort::PhonePair,
1005        TargetPort::AnalogPair(0),
1006        TargetPort::AnalogPair(1),
1007        TargetPort::AnalogPair(2),
1008        TargetPort::AnalogPair(3),
1009        TargetPort::MainPair,
1010        TargetPort::AesEbuPair,
1011        TargetPort::AdatPair(0),
1012        TargetPort::AdatPair(1),
1013        TargetPort::AdatPair(2),
1014        TargetPort::AdatPair(3),
1015    ];
1016}
1017
1018impl MotuRegisterDspMixerMonauralSourceSpecification for F896hdProtocol {
1019    const MIXER_SOURCES: &'static [TargetPort] = &[
1020        TargetPort::Analog(0),
1021        TargetPort::Analog(1),
1022        TargetPort::Analog(2),
1023        TargetPort::Analog(3),
1024        TargetPort::Analog(4),
1025        TargetPort::Analog(5),
1026        TargetPort::Analog(6),
1027        TargetPort::Analog(7),
1028        TargetPort::AesEbu(0),
1029        TargetPort::AesEbu(1),
1030        TargetPort::Adat(0),
1031        TargetPort::Adat(1),
1032        TargetPort::Adat(2),
1033        TargetPort::Adat(3),
1034        TargetPort::Adat(4),
1035        TargetPort::Adat(5),
1036        TargetPort::Adat(6),
1037        TargetPort::Adat(7),
1038    ];
1039}
1040
1041impl MotuRegisterDspMeterSpecification for F896hdProtocol {
1042    const INPUT_PORTS: &'static [TargetPort] = &[
1043        TargetPort::Analog(0),
1044        TargetPort::Analog(1),
1045        TargetPort::Analog(2),
1046        TargetPort::Analog(3),
1047        TargetPort::Analog(4),
1048        TargetPort::Analog(5),
1049        TargetPort::Analog(6),
1050        TargetPort::Analog(7),
1051        TargetPort::AesEbu(0),
1052        TargetPort::AesEbu(1),
1053        TargetPort::Adat(0),
1054        TargetPort::Adat(1),
1055        TargetPort::Adat(2),
1056        TargetPort::Adat(3),
1057        TargetPort::Adat(4),
1058        TargetPort::Adat(5),
1059        TargetPort::Adat(6),
1060        TargetPort::Adat(7),
1061    ];
1062    const OUTPUT_PORT_PAIRS: &'static [TargetPort] = &[
1063        TargetPort::PhonePair,
1064        TargetPort::AnalogPair(0),
1065        TargetPort::AnalogPair(1),
1066        TargetPort::AnalogPair(2),
1067        TargetPort::AnalogPair(3),
1068        TargetPort::MainPair,
1069        TargetPort::AesEbuPair,
1070        TargetPort::AdatPair(0),
1071        TargetPort::AdatPair(1),
1072        TargetPort::AdatPair(2),
1073        TargetPort::AdatPair(3),
1074    ];
1075    const OUTPUT_PORT_PAIR_POS: &'static [[usize; 2]] = &[
1076        [2, 3],
1077        [4, 5],
1078        [6, 7],
1079        [8, 9],
1080        [10, 11],
1081        [12, 13],
1082        [14, 15],
1083        [16, 17],
1084        [18, 19],
1085        [20, 21],
1086        [22, 23],
1087    ];
1088}
1089
1090impl MotuRegisterDspMeterOutputTargetSpecification for F896hdProtocol {}
1091
1092#[cfg(test)]
1093mod test {
1094    use super::*;
1095
1096    #[test]
1097    fn common_assign_port_specification() {
1098        assert_eq!(
1099            F828mk2Protocol::ASSIGN_PORT_TARGETS.len(),
1100            F828mk2Protocol::ASSIGN_PORT_VALS.len()
1101        );
1102        assert_eq!(
1103            F8preProtocol::ASSIGN_PORT_TARGETS.len(),
1104            F8preProtocol::ASSIGN_PORT_VALS.len()
1105        );
1106        assert_eq!(
1107            TravelerProtocol::ASSIGN_PORT_TARGETS.len(),
1108            TravelerProtocol::ASSIGN_PORT_VALS.len()
1109        );
1110        assert_eq!(
1111            UltraliteProtocol::ASSIGN_PORT_TARGETS.len(),
1112            UltraliteProtocol::ASSIGN_PORT_VALS.len()
1113        );
1114        assert_eq!(
1115            F896hdProtocol::ASSIGN_PORT_TARGETS.len(),
1116            F896hdProtocol::ASSIGN_PORT_VALS.len()
1117        );
1118    }
1119
1120    #[test]
1121    fn v2_clock_specification() {
1122        assert_eq!(
1123            F828mk2Protocol::CLK_RATES.len(),
1124            F828mk2Protocol::CLK_RATE_VALS.len()
1125        );
1126        assert_eq!(
1127            F828mk2Protocol::CLK_SRCS.len(),
1128            F828mk2Protocol::CLK_SRC_VALS.len()
1129        );
1130
1131        assert_eq!(
1132            F8preProtocol::CLK_RATES.len(),
1133            F8preProtocol::CLK_RATE_VALS.len()
1134        );
1135        assert_eq!(
1136            F8preProtocol::CLK_SRCS.len(),
1137            F8preProtocol::CLK_SRC_VALS.len()
1138        );
1139
1140        assert_eq!(
1141            TravelerProtocol::CLK_RATES.len(),
1142            TravelerProtocol::CLK_RATE_VALS.len()
1143        );
1144        assert_eq!(
1145            TravelerProtocol::CLK_SRCS.len(),
1146            TravelerProtocol::CLK_SRC_VALS.len()
1147        );
1148
1149        assert_eq!(
1150            UltraliteProtocol::CLK_RATES.len(),
1151            UltraliteProtocol::CLK_RATE_VALS.len()
1152        );
1153        assert_eq!(
1154            UltraliteProtocol::CLK_SRCS.len(),
1155            UltraliteProtocol::CLK_SRC_VALS.len()
1156        );
1157
1158        assert_eq!(
1159            F896hdProtocol::CLK_RATES.len(),
1160            F896hdProtocol::CLK_RATE_VALS.len()
1161        );
1162        assert_eq!(
1163            F896hdProtocol::CLK_SRCS.len(),
1164            F896hdProtocol::CLK_SRC_VALS.len()
1165        );
1166    }
1167
1168    #[test]
1169    fn register_dsp_meter_specification() {
1170        assert_eq!(
1171            F828mk2Protocol::OUTPUT_PORT_PAIRS.len(),
1172            F828mk2Protocol::OUTPUT_PORT_PAIR_POS.len(),
1173        );
1174
1175        assert_eq!(
1176            F8preProtocol::OUTPUT_PORT_PAIRS.len(),
1177            F8preProtocol::OUTPUT_PORT_PAIR_POS.len(),
1178        );
1179
1180        assert_eq!(
1181            TravelerProtocol::OUTPUT_PORT_PAIRS.len(),
1182            TravelerProtocol::OUTPUT_PORT_PAIR_POS.len(),
1183        );
1184
1185        assert_eq!(
1186            UltraliteProtocol::OUTPUT_PORT_PAIRS.len(),
1187            UltraliteProtocol::OUTPUT_PORT_PAIR_POS.len(),
1188        );
1189
1190        assert_eq!(
1191            F896hdProtocol::OUTPUT_PORT_PAIRS.len(),
1192            F896hdProtocol::OUTPUT_PORT_PAIR_POS.len(),
1193        );
1194    }
1195
1196    #[test]
1197    fn ultralite_specification() {
1198        assert_eq!(
1199            UltraliteProtocol::KNOB_TARGETS.len(),
1200            KNOB_TARGET_VALS.len()
1201        );
1202    }
1203}