firewire_bebob_protocols/maudio/
normal.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol implementation for M-Audio FireWire series.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by M-Audio normal FireWire series.
8//!
9//! DM1000 is used for M-Audio FireWire 410. DM1000E is used for M-Audio FireWire Audiophile,
10//! and Solo.
11//!
12//! ## Diagram of internal signal flow for FireWire 410
13//!
14//! ```text
15//! analog-input-1/2 ---+----------------------+--------------------------> stream-output-1/2
16//! digital-input-1/2 --|-+--------------------|-+------------------------> stream-output-3/4
17//!                     | |                    | |
18//!                     | |                    v v
19//!                     | |                ++=======++
20//!  stream-input-1/2 --|-|-+------------->||       ||
21//!  stream-input-3/4 --|-|-|-+----------->|| 14x 2 ||
22//!  stream-input-5/6 --|-|-|-|-+--------->||  aux  || ---> aux-output-1/2
23//!  stream-input-7/8 --|-|-|-|-|-+------->|| mixer ||        | | | | | |
24//!  stream-input-9/10 -|-|-|-|-|-|-+----->||       ||        | | | | | |
25//!                     | | | | | | |      ++=======++        +-|-|-|-|-|-> analog-output-1/2
26//!                     | | | | | | |                         | | | | | |
27//!                     | | | | | | |      ++=======++        | +-|-|-|-|-> analog-output-3/4
28//!                     | | +-|-|-|-|----> ||       ||        | | | | | |
29//!                     | | | +-|-|-|----> || 10x 2 ||        | | +-|-|-|-> analog-output-5/6
30//!                     | | | | +-|-|----> ||  hp   ||        | | | | | |
31//!                     | | | | | +-|----> || mixer ||        | | | +-|-|-> analog-output-7/8
32//!                     | | | | | | +----> ||       ||        | | | | | |
33//!                     | | | | | | |      ++=======++        | | | | +-|-> digital-output-1/2
34//!                     | | | | | | |           v             | | | | | |
35//!                     | | | | | | |    hp-mixer-output-1/2 -|-|-|-|-|-+-> headphone-1/2
36//!                     v v v v v v v                         | | | | |
37//!                   ++=============++                       | | | | |
38//!                   ||             || -- mixer-output-1/2 --+ | | | |
39//!                   ||    14x10    || -- mixer-output-3/4 ----+ | | |
40//!                   ||             || -- mixer-output-5/6 ------+ | |
41//!                   ||    mixer    || -- mixer-output-7/8 --------+ |
42//!                   ||             || -- mixer-output-9/10 ---------+
43//!                   ++=============++
44//! ```
45//!
46//! ## Diagram of internal signal flow for FireWire Audiophile
47//!
48//! ```text
49//! analog-input-1/2 ---+----------------------+----------------------> stream-output-1/2
50//! digital-input-1/2 --|-+--------------------|-+--------------------> stream-output-3/4
51//!                     | |                    | |
52//!                     | |                    v v
53//!                     | |                ++=======++
54//!  stream-input-1/2 --|-|-+------------->|| 10x2  ||
55//!  stream-input-3/4 --|-|-|-+----------->||  aux  || --> aux-output-1/2
56//!  stream-input-5/6 --|-|-|-|-+--------->|| mixer ||        | | | |
57//!                     | | | | |          ++=======++        +-|-|-|-> analog-output-1/2
58//!                     | | | | |                             | | | |
59//!                     | | | | |                             | +-|-|-> analog-output-3/4
60//!                     | | | | |                             | | | |
61//!                     | | | | |                             | | +-|-> analog-output-5/6
62//!                     | | | | |                             | | | |
63//!                     | | | | |                             | | | +-> headphone-1/2
64//!                     v v v v v                             | | |     (one source only)
65//!                   ++=============++                       | | |       ^   ^   ^
66//!                   ||    10x6     || -- mixer-output-1/2 --+-|-|-------+   |   |
67//!                   ||    mixer    || -- mixer-output-3/4 ----+-|-----------+   |
68//!                   ||             || -- mixer-output-5/6 ------+---------------+
69//!                   ++=============++
70//! ```
71//!
72//! ## Diagram of internal signal flow for FireWire Solo
73//!
74//! ```text
75//! analog-input-1/2 --------+------------------------------> stream-output-1/2
76//! digital-input-1/2 -------|-+----------------------------> stream-output-3/4
77//!                          | |
78//!                          v v
79//!                      ++=======++
80//!  stream-input-1/2 -->||  8x4  || --> mixer-output-1/2 --> analog-output-1/2
81//!  stream-input-3/4 -->|| mixer || --> mixer-output-3/4 --> digital-output-1/2
82//!                      ++=======++
83//! ```
84//!
85//! ## Diagram of internal signal flow for Ozonic
86//!
87//! ```text
88//! analog-input-1/2 --------+------------------------------> stream-output-1/2
89//! analog-input-3/4 --------|-+----------------------------> stream-output-3/4
90//!                          | |
91//!                          v v
92//!                      ++=======++
93//!  stream-input-1/2 -->||  8x4  || --> mixer-output-1/2 --> analog-output-1/2
94//!  stream-input-3/4 -->|| mixer || --> mixer-output-3/4 --> analog-output-3/4
95//!                      ++=======++
96//! ```
97//!
98//! The protocol implementation for M-Audio FireWire 410 was written with firmware version below:
99//!
100//! ```sh
101//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
102//! protocol:
103//!   version: 1
104//! bootloader:
105//!   timestamp: 2003-04-04T01:46:25+0000
106//!   version: 0.0.0
107//! hardware:
108//!   GUID: 0x000af510000d6c01
109//!   model ID: 0x000002
110//!   revision: 0.0.1
111//! software:
112//!   timestamp: 2007-05-04T10:26:56+0000
113//!   ID: 0x00010046
114//!   revision: 0.255.65535
115//! image:
116//!   base address: 0x20080000
117//!   maximum size: 0x180000
118//! ```
119//!
120//! The protocol implementation for M-Audio FireWire Audiophile was written with firmware version
121//! below:
122//!
123//! ```sh
124//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
125//! protocol:
126//!   version: 1
127//! bootloader:
128//!   timestamp: 2003-10-06T10:00:41+0000
129//!   version: 0.0.0
130//! hardware:
131//!   GUID: 0x002b7e2e000d6c03
132//!   model ID: 0x00000d
133//!   revision: 0.0.1
134//! software:
135//!   timestamp: 2007-05-04T10:22:12+0000
136//!   ID: 0x00010060
137//!   revision: 0.255.65535
138//! image:
139//!   base address: 0x20080000
140//!   maximum size: 0x180000
141//! ```
142//!
143//! The protocol implementation for M-Audio FireWire Solo was written with firmware version below:
144//!
145//! ```sh
146//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
147//! protocol:
148//!   version: 1
149//! bootloader:
150//!   timestamp: 2004-09-15T01:22:54+0000
151//!   version: 0.0.0
152//! hardware:
153//!   GUID: 0x00c256a4000d6c0b
154//!   model ID: 0x000090
155//!   revision: 0.0.0
156//! software:
157//!   timestamp: 2007-08-08T01:56:28+0000
158//!   ID: 0x00010062
159//!   revision: 0.255.65535
160//! image:
161//!   base address: 0x20080000
162//!   maximum size: 0x180000
163//! ```
164//!
165//! The protocol implementation for M-Audio Ozonic was written with firmware version below:
166//!
167//! ```sh
168//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
169//! protocol:
170//!   version: 1
171//! bootloader:
172//!   timestamp: 2004-09-15T05:56:44+0000
173//!   version: 0.0.0
174//! hardware:
175//!   GUID: 0x0078c1d7000d6c0a
176//!   model ID: 0x000001
177//!   revision: 0.0.1
178//! software:
179//!   timestamp: 2005-09-05T09:10:22+0000
180//!   ID: 0x0000000a
181//!   revision: 0.0.20
182//! image:
183//!   base address: 0x20080000
184//!   maximum size: 0x180000
185//! ```
186
187use super::*;
188
189/// The protocol implementation for media and sampling clock of FireWire 410.
190#[derive(Default, Debug)]
191pub struct Fw410ClkProtocol;
192
193impl MediaClockFrequencyOperation for Fw410ClkProtocol {
194    const FREQ_LIST: &'static [u32] = &[44100, 48000, 88200, 96000, 176400, 192000];
195}
196
197impl SamplingClockSourceOperation for Fw410ClkProtocol {
198    const DST: SignalAddr = SignalAddr::Subunit(SignalSubunitAddr {
199        subunit: MUSIC_SUBUNIT_0,
200        plug_id: 0x01,
201    });
202
203    const SRC_LIST: &'static [SignalAddr] = &[
204        // Internal
205        SignalAddr::Subunit(SignalSubunitAddr {
206            subunit: MUSIC_SUBUNIT_0,
207            plug_id: 0x01,
208        }),
209        // S/PDIF
210        SignalAddr::Unit(SignalUnitAddr::Ext(0x02)),
211    ];
212}
213
214/// The protocol implementation for meter in FireWire 410.
215#[derive(Default, Debug)]
216pub struct Fw410MeterProtocol;
217
218impl MaudioNormalMeterProtocol for Fw410MeterProtocol {
219    const PHYS_INPUT_COUNT: usize = 4;
220    const STREAM_INPUT_COUNT: usize = 0;
221    const PHYS_OUTPUT_COUNT: usize = 10;
222    const ROTARY_COUNT: usize = 1;
223    const HAS_SWITCH: bool = false;
224    const HAS_SYNC_STATUS: bool = true;
225}
226
227/// The protocol implementation for physical input of FireWire 410.
228#[derive(Default, Debug)]
229pub struct Fw410PhysInputProtocol;
230
231impl AvcAudioFeatureSpecification for Fw410PhysInputProtocol {
232    const ENTRIES: &'static [(u8, AudioCh)] = &[
233        (0x03, AudioCh::Each(0)), // analog-input-1
234        (0x03, AudioCh::Each(1)), // analog-input-2
235        (0x04, AudioCh::Each(0)), // digital-input-1
236        (0x04, AudioCh::Each(1)), // digital-input-2
237    ];
238}
239
240impl AvcLevelOperation for Fw410PhysInputProtocol {}
241
242impl AvcLrBalanceOperation for Fw410PhysInputProtocol {}
243
244/// The protocol implementation for physical output of FireWire 410.
245#[derive(Default, Debug)]
246pub struct Fw410PhysOutputProtocol;
247
248impl AvcAudioFeatureSpecification for Fw410PhysOutputProtocol {
249    const ENTRIES: &'static [(u8, AudioCh)] = &[
250        (0x0a, AudioCh::Each(0)), // analog-output-1
251        (0x0a, AudioCh::Each(1)), // analog-output-2
252        (0x0b, AudioCh::Each(0)), // analog-output-3
253        (0x0b, AudioCh::Each(1)), // analog-output-4
254        (0x0c, AudioCh::Each(0)), // analog-output-5
255        (0x0c, AudioCh::Each(1)), // analog-output-6
256        (0x0d, AudioCh::Each(0)), // analog-output-7
257        (0x0d, AudioCh::Each(1)), // analog-output-8
258        (0x0e, AudioCh::Each(0)), // digital-output-1
259        (0x0e, AudioCh::Each(1)), // digital-output-2
260    ];
261}
262
263impl AvcLevelOperation for Fw410PhysOutputProtocol {}
264
265impl AvcSelectorOperation for Fw410PhysOutputProtocol {
266    // NOTE: "analog-output-1/2", "analog-output-3/4", "analog-output-5/6", "analog-output-7/8",
267    //       "analog-output-9/10"
268    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x02, 0x03, 0x04, 0x05, 0x06];
269    // NOTE: "mixer-output", "aux-output-1/2"
270    const INPUT_PLUG_ID_LIST: &'static [u8] = &[0x00, 0x01];
271}
272
273/// The protocol implementation for source of aux mixer in FireWire 410.
274#[derive(Default, Debug)]
275pub struct Fw410AuxSourceProtocol;
276
277impl AvcAudioFeatureSpecification for Fw410AuxSourceProtocol {
278    const ENTRIES: &'static [(u8, AudioCh)] = &[
279        (0x07, AudioCh::Each(0)), // analog-input-1
280        (0x07, AudioCh::Each(1)), // analog-input-2
281        (0x08, AudioCh::Each(0)), // digital-input-1
282        (0x08, AudioCh::Each(1)), // digital-input-2
283        (0x06, AudioCh::Each(0)), // stream-input-1
284        (0x06, AudioCh::Each(1)), // stream-input-2
285        (0x05, AudioCh::Each(0)), // stream-input-3
286        (0x05, AudioCh::Each(1)), // stream-input-4
287        (0x05, AudioCh::Each(2)), // stream-input-5
288        (0x05, AudioCh::Each(3)), // stream-input-6
289        (0x05, AudioCh::Each(4)), // stream-input-7
290        (0x05, AudioCh::Each(5)), // stream-input-8
291        (0x05, AudioCh::Each(6)), // stream-input-9
292        (0x05, AudioCh::Each(7)), // stream-input-10
293    ];
294}
295
296impl AvcLevelOperation for Fw410AuxSourceProtocol {}
297
298/// The protocol implementation for output of aux mixer in FireWire 410.
299#[derive(Default, Debug)]
300pub struct Fw410AuxOutputProtocol;
301
302impl AvcAudioFeatureSpecification for Fw410AuxOutputProtocol {
303    const ENTRIES: &'static [(u8, AudioCh)] = &[
304        (0x09, AudioCh::Each(0)), // aux-output-1
305        (0x09, AudioCh::Each(1)), // aux-output-2
306    ];
307}
308
309impl AvcLevelOperation for Fw410AuxOutputProtocol {}
310
311/// The protocol implementation for output of headphone in FireWire 410.
312#[derive(Default, Debug)]
313pub struct Fw410HeadphoneProtocol;
314
315impl AvcAudioFeatureSpecification for Fw410HeadphoneProtocol {
316    const ENTRIES: &'static [(u8, AudioCh)] = &[
317        (0x0f, AudioCh::Each(0)), // headphone-1
318        (0x0f, AudioCh::Each(1)), // headphone-2
319    ];
320}
321
322impl AvcLevelOperation for Fw410HeadphoneProtocol {}
323
324impl AvcSelectorOperation for Fw410HeadphoneProtocol {
325    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x07];
326    // NOTE: "mixer", "aux-1/2".
327    const INPUT_PLUG_ID_LIST: &'static [u8] = &[0x00, 0x01];
328}
329
330/// The protocol implementation for source of S/PDIF output in FireWire 410.
331#[derive(Default, Debug)]
332pub struct Fw410SpdifOutputProtocol;
333
334impl AvcSelectorOperation for Fw410SpdifOutputProtocol {
335    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x01];
336    // NOTE: "Coaxial", "Optical".
337    const INPUT_PLUG_ID_LIST: &'static [u8] = &[0x00, 0x01];
338}
339
340/// The protocol implementation for mixer in FireWire 410.
341#[derive(Default, Debug)]
342pub struct Fw410MixerProtocol;
343
344impl MaudioNormalMixerOperation for Fw410MixerProtocol {
345    const DST_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
346        (0x01, AudioCh::Each(0)), // mixer-1/2
347        (0x01, AudioCh::Each(2)), // mixer-3/4
348        (0x01, AudioCh::Each(4)), // mixer-5/6
349        (0x01, AudioCh::Each(6)), // mixer-7/8
350        (0x01, AudioCh::Each(8)), // mixer-1/2
351    ];
352    const SRC_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
353        (0x02, AudioCh::Each(0)), // analog-input-1/2
354        (0x03, AudioCh::Each(0)), // digital-input-1/2
355        (0x01, AudioCh::Each(0)), // stream-input-1/2
356        (0x00, AudioCh::Each(0)), // stream-input-3/4
357        (0x00, AudioCh::Each(2)), // stream-input-5/6
358        (0x00, AudioCh::Each(4)), // stream-input-7/8
359        (0x00, AudioCh::Each(6)), // stream-input-9/10
360    ];
361}
362
363impl MaudioNormalMixerOperation for Fw410HeadphoneProtocol {
364    const DST_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[(0x07, AudioCh::Each(0))];
365    const SRC_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
366        (0x00, AudioCh::Each(0)), // mixer-output-1/2
367        (0x00, AudioCh::Each(2)), // mixer-output-3/4
368        (0x00, AudioCh::Each(4)), // mixer-output-5/6
369        (0x00, AudioCh::Each(6)), // mixer-output-7/8
370        (0x00, AudioCh::Each(8)), // mixer-output-9/10
371    ];
372}
373
374/// The protocol implementation for media and sampling clock of FireWire Solo.
375#[derive(Default, Debug)]
376pub struct SoloClkProtocol;
377
378impl MediaClockFrequencyOperation for SoloClkProtocol {
379    const FREQ_LIST: &'static [u32] = &[44100, 48000, 88200, 96000];
380}
381
382impl SamplingClockSourceOperation for SoloClkProtocol {
383    const DST: SignalAddr = SignalAddr::Subunit(SignalSubunitAddr {
384        subunit: MUSIC_SUBUNIT_0,
385        plug_id: 0x01,
386    });
387
388    const SRC_LIST: &'static [SignalAddr] = &[
389        // Internal
390        SignalAddr::Subunit(SignalSubunitAddr {
391            subunit: MUSIC_SUBUNIT_0,
392            plug_id: 0x01,
393        }),
394        // S/PDIF
395        SignalAddr::Unit(SignalUnitAddr::Ext(0x01)),
396    ];
397}
398
399/// The protocol implementation for meter in FireWire Solo.
400#[derive(Default, Debug)]
401pub struct SoloMeterProtocol;
402
403impl MaudioNormalMeterProtocol for SoloMeterProtocol {
404    const PHYS_INPUT_COUNT: usize = 4;
405    const STREAM_INPUT_COUNT: usize = 4;
406    const PHYS_OUTPUT_COUNT: usize = 4;
407    const ROTARY_COUNT: usize = 0;
408    const HAS_SWITCH: bool = false;
409    const HAS_SYNC_STATUS: bool = true;
410}
411
412/// The protocol implementation for physical input of FireWire Solo.
413#[derive(Default, Debug)]
414pub struct SoloPhysInputProtocol;
415
416impl AvcAudioFeatureSpecification for SoloPhysInputProtocol {
417    const ENTRIES: &'static [(u8, AudioCh)] = &[
418        (0x03, AudioCh::Each(0)), // analog-input-1
419        (0x03, AudioCh::Each(1)), // analog-input-2
420        (0x04, AudioCh::Each(0)), // digital-input-1
421        (0x04, AudioCh::Each(1)), // digital-input-2
422    ];
423}
424
425impl AvcLevelOperation for SoloPhysInputProtocol {}
426
427impl AvcLrBalanceOperation for SoloPhysInputProtocol {}
428
429/// The protocol implementation for stream input of FireWire Solo.
430#[derive(Default, Debug)]
431pub struct SoloStreamInputProtocol;
432
433impl AvcAudioFeatureSpecification for SoloStreamInputProtocol {
434    const ENTRIES: &'static [(u8, AudioCh)] = &[
435        (0x01, AudioCh::Each(0)), // stream-input-1
436        (0x01, AudioCh::Each(1)), // stream-input-2
437        (0x02, AudioCh::Each(0)), // stream-input-3
438        (0x02, AudioCh::Each(1)), // stream-input-4
439    ];
440}
441
442impl AvcLevelOperation for SoloStreamInputProtocol {}
443
444// NOTE: outputs are not configurable, connected to hardware dial directly.
445
446/// The protocol implementation for source of S/PDIF output in FireWire Solo.
447#[derive(Default, Debug)]
448pub struct SoloSpdifOutputProtocol;
449
450impl AvcSelectorOperation for SoloSpdifOutputProtocol {
451    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x01];
452    // NOTE: "stream-3/4", "mixer-3/4".
453    const INPUT_PLUG_ID_LIST: &'static [u8] = &[0x00, 0x01];
454}
455
456/// The protocol implementation for mixer in FireWire Solo.
457#[derive(Default, Debug)]
458pub struct SoloMixerProtocol;
459
460impl MaudioNormalMixerOperation for SoloMixerProtocol {
461    const DST_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
462        // mixer-1/2 directly connected to analog-output-1/2 and headphone-1/2
463        (0x01, AudioCh::Each(0)),
464        (0x01, AudioCh::Each(2)), // mixer-3/4 directly connected to digital-output-1/2
465    ];
466    const SRC_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
467        (0x00, AudioCh::Each(0)), // analog-input-1/2
468        (0x01, AudioCh::Each(0)), // digital-input-1/2
469        (0x02, AudioCh::Each(0)), // stream-input-1/2
470        (0x03, AudioCh::Each(0)), // stream-input-3/4
471    ];
472}
473
474/// The protocol implementation for media and sampling clock of FireWire Audiophile.
475#[derive(Default, Debug)]
476pub struct AudiophileClkProtocol;
477
478impl MediaClockFrequencyOperation for AudiophileClkProtocol {
479    const FREQ_LIST: &'static [u32] = &[44100, 48000, 88200, 96000];
480}
481
482impl SamplingClockSourceOperation for AudiophileClkProtocol {
483    const DST: SignalAddr = SignalAddr::Subunit(SignalSubunitAddr {
484        subunit: MUSIC_SUBUNIT_0,
485        plug_id: 0x01,
486    });
487
488    const SRC_LIST: &'static [SignalAddr] = &[
489        // Internal
490        SignalAddr::Subunit(SignalSubunitAddr {
491            subunit: MUSIC_SUBUNIT_0,
492            plug_id: 0x01,
493        }),
494        // S/PDIF
495        SignalAddr::Unit(SignalUnitAddr::Ext(0x02)),
496    ];
497}
498
499/// The protocol implementation for meter in FireWire Audiophile.
500#[derive(Default, Debug)]
501pub struct AudiophileMeterProtocol;
502
503impl MaudioNormalMeterProtocol for AudiophileMeterProtocol {
504    const PHYS_INPUT_COUNT: usize = 4;
505    const STREAM_INPUT_COUNT: usize = 0;
506    const PHYS_OUTPUT_COUNT: usize = 6;
507    const ROTARY_COUNT: usize = 2;
508    const HAS_SWITCH: bool = true;
509    const HAS_SYNC_STATUS: bool = true;
510}
511
512/// The protocol implementation for physical input in FireWire Audiophile.
513#[derive(Default, Debug)]
514pub struct AudiophilePhysInputProtocol;
515
516impl AvcAudioFeatureSpecification for AudiophilePhysInputProtocol {
517    const ENTRIES: &'static [(u8, AudioCh)] = &[
518        (0x04, AudioCh::Each(0)), // analog-input-1
519        (0x04, AudioCh::Each(1)), // analog-input-2
520        (0x05, AudioCh::Each(0)), // digital-input-1
521        (0x05, AudioCh::Each(1)), // digital-input-2
522    ];
523}
524
525impl AvcLevelOperation for AudiophilePhysInputProtocol {}
526
527impl AvcLrBalanceOperation for AudiophilePhysInputProtocol {}
528
529/// The protocol implementation for physical output in FireWire Audiophile.
530#[derive(Default, Debug)]
531pub struct AudiophilePhysOutputProtocol;
532
533impl AvcAudioFeatureSpecification for AudiophilePhysOutputProtocol {
534    const ENTRIES: &'static [(u8, AudioCh)] = &[
535        (0x0c, AudioCh::Each(0)), // analog-output-1
536        (0x0c, AudioCh::Each(1)), // analog-output-2
537        (0x0d, AudioCh::Each(0)), // analog-output-3
538        (0x0d, AudioCh::Each(1)), // analog-output-4
539        (0x0e, AudioCh::Each(0)), // digital-output-1
540        (0x0e, AudioCh::Each(1)), // digital-output-2
541    ];
542}
543
544impl AvcLevelOperation for AudiophilePhysOutputProtocol {}
545
546impl AvcSelectorOperation for AudiophilePhysOutputProtocol {
547    // NOTE: "analog-output-1/2", "analog-output-3/4", "analog-output-5/6"
548    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x01, 0x02, 0x03];
549    // NOTE: "mixer-output", "aux-output-1/2"
550    const INPUT_PLUG_ID_LIST: &'static [u8] = &[0x00, 0x01];
551}
552
553/// The protocol implementation for source of aux mixer in FireWire Audiophile.
554#[derive(Default, Debug)]
555pub struct AudiophileAuxSourceProtocol;
556
557impl AvcAudioFeatureSpecification for AudiophileAuxSourceProtocol {
558    const ENTRIES: &'static [(u8, AudioCh)] = &[
559        (0x09, AudioCh::Each(0)), // analog-input-1
560        (0x09, AudioCh::Each(1)), // analog-input-2
561        (0x0a, AudioCh::Each(0)), // digital-input-1
562        (0x0a, AudioCh::Each(1)), // digital-input-2
563        (0x06, AudioCh::Each(0)), // stream-input-1
564        (0x06, AudioCh::Each(1)), // stream-input-2
565        (0x07, AudioCh::Each(0)), // stream-input-3
566        (0x07, AudioCh::Each(1)), // stream-input-4
567        (0x08, AudioCh::Each(0)), // stream-input-5
568        (0x08, AudioCh::Each(1)), // stream-input-6
569    ];
570}
571
572impl AvcLevelOperation for AudiophileAuxSourceProtocol {}
573
574/// The protocol implementation for output of aux mixer in FireWire Audiophile.
575#[derive(Default, Debug)]
576pub struct AudiophileAuxOutputProtocol;
577
578impl AvcAudioFeatureSpecification for AudiophileAuxOutputProtocol {
579    const ENTRIES: &'static [(u8, AudioCh)] = &[
580        (0x0b, AudioCh::Each(0)), // aux-output-1
581        (0x0b, AudioCh::Each(1)), // aux-output-2
582    ];
583}
584
585impl AvcLevelOperation for AudiophileAuxOutputProtocol {}
586
587/// The protocol implementation for output of headphone in FireWire Audiophile.
588#[derive(Default, Debug)]
589pub struct AudiophileHeadphoneProtocol;
590
591impl AvcAudioFeatureSpecification for AudiophileHeadphoneProtocol {
592    const ENTRIES: &'static [(u8, AudioCh)] = &[
593        (0x0f, AudioCh::Each(0)), // headphone-1
594        (0x0f, AudioCh::Each(1)), // headphone-2
595    ];
596}
597
598impl AvcLevelOperation for AudiophileHeadphoneProtocol {}
599
600impl AvcSelectorOperation for AudiophileHeadphoneProtocol {
601    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x04];
602    // NOTE: "mixer-1/2", "mixer-3/4", "mixer-5/6", "aux-1/2".
603    const INPUT_PLUG_ID_LIST: &'static [u8] = &[0x00, 0x01, 0x02, 0x03];
604}
605
606/// The protocol implementation for mixer in FireWire Solo.
607#[derive(Default, Debug)]
608pub struct AudiophileMixerProtocol;
609
610impl MaudioNormalMixerOperation for AudiophileMixerProtocol {
611    const DST_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
612        (0x01, AudioCh::Each(0)), // mixer-1/2
613        (0x02, AudioCh::Each(0)), // mixer-3/4
614        (0x03, AudioCh::Each(0)), // mixer-5/6
615    ];
616    const SRC_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
617        (0x03, AudioCh::Each(0)), // analog-input-1/2
618        (0x04, AudioCh::Each(0)), // digital-input-1/2
619        (0x00, AudioCh::Each(0)), // stream-input-1/2
620        (0x01, AudioCh::Each(0)), // stream-input-3/4
621        (0x02, AudioCh::Each(0)), // stream-input-5/6
622    ];
623}
624
625/// The protocol implementation for media and sampling clock of Ozonic.
626#[derive(Default, Debug)]
627pub struct OzonicClkProtocol;
628
629impl MediaClockFrequencyOperation for OzonicClkProtocol {
630    const FREQ_LIST: &'static [u32] = &[44100, 48000, 88200, 96000];
631}
632
633impl SamplingClockSourceOperation for OzonicClkProtocol {
634    const DST: SignalAddr = SignalAddr::Subunit(SignalSubunitAddr {
635        subunit: MUSIC_SUBUNIT_0,
636        plug_id: 0x05,
637    });
638
639    const SRC_LIST: &'static [SignalAddr] = &[
640        // Internal
641        SignalAddr::Subunit(SignalSubunitAddr {
642            subunit: MUSIC_SUBUNIT_0,
643            plug_id: 0x05,
644        }),
645    ];
646}
647
648/// The protocol implementation for meter in Ozonic.
649#[derive(Default, Debug)]
650pub struct OzonicMeterProtocol;
651
652impl MaudioNormalMeterProtocol for OzonicMeterProtocol {
653    const PHYS_INPUT_COUNT: usize = 4;
654    const STREAM_INPUT_COUNT: usize = 4;
655    const PHYS_OUTPUT_COUNT: usize = 4;
656    const ROTARY_COUNT: usize = 0;
657    const HAS_SWITCH: bool = false;
658    const HAS_SYNC_STATUS: bool = false;
659}
660
661/// The protocol implementation for physical input of FireWire Audiophile.
662#[derive(Default, Debug)]
663pub struct OzonicPhysInputProtocol;
664
665impl AvcAudioFeatureSpecification for OzonicPhysInputProtocol {
666    const ENTRIES: &'static [(u8, AudioCh)] = &[
667        (0x03, AudioCh::Each(0)), // analog-input-1
668        (0x03, AudioCh::Each(1)), // analog-input-2
669        (0x04, AudioCh::Each(0)), // analog-input-3
670        (0x04, AudioCh::Each(1)), // analog-input-4
671    ];
672}
673
674impl AvcLevelOperation for OzonicPhysInputProtocol {}
675
676impl AvcLrBalanceOperation for OzonicPhysInputProtocol {}
677
678/// The protocol implementation for stream input of FireWire Audiophile.
679#[derive(Default, Debug)]
680pub struct OzonicStreamInputProtocol;
681
682impl AvcAudioFeatureSpecification for OzonicStreamInputProtocol {
683    const ENTRIES: &'static [(u8, AudioCh)] = &[
684        (0x01, AudioCh::Each(0)), // stream-input-1
685        (0x01, AudioCh::Each(1)), // stream-input-2
686        (0x02, AudioCh::Each(0)), // stream-input-3
687        (0x02, AudioCh::Each(1)), // stream-input-4
688    ];
689}
690
691impl AvcLevelOperation for OzonicStreamInputProtocol {}
692
693// NOTE: outputs are not configurable, connected to hardware dial directly.
694
695/// The state of switch with LED specific to FireWire Audiophile.
696#[derive(Debug, Clone, Copy, Eq, PartialEq)]
697pub enum AudiophileSwitchState {
698    Off,
699    A,
700    B,
701}
702
703impl Default for AudiophileSwitchState {
704    fn default() -> Self {
705        Self::Off
706    }
707}
708
709impl AudiophileSwitchState {
710    const VALUE_OFF: u8 = 0x00;
711    const VALUE_A: u8 = 0x01;
712    const VALUE_B: u8 = 0x02;
713
714    fn to_val(&self) -> u8 {
715        match self {
716            Self::Off => Self::VALUE_OFF,
717            Self::A => Self::VALUE_A,
718            Self::B => Self::VALUE_B,
719        }
720    }
721
722    #[allow(dead_code)]
723    fn from_val(val: u8) -> Self {
724        match val {
725            Self::VALUE_A => Self::A,
726            Self::VALUE_B => Self::B,
727            _ => Self::Off,
728        }
729    }
730}
731
732/// The structure to express AV/C vendor-dependent command for LED switch specific to FireWire
733/// Audiophile.
734pub struct AudiophileLedSwitch {
735    state: AudiophileSwitchState,
736    op: VendorDependent,
737}
738
739impl AudiophileLedSwitch {
740    pub fn new(switch_state: AudiophileSwitchState) -> Self {
741        let mut instance = Self::default();
742        instance.state = switch_state;
743        instance
744    }
745}
746
747impl Default for AudiophileLedSwitch {
748    fn default() -> Self {
749        Self {
750            state: Default::default(),
751            op: VendorDependent {
752                company_id: MAUDIO_OUI,
753                data: vec![0x02, 0x00, 0x01, 0xff, 0xff, 0xff],
754            },
755        }
756    }
757}
758
759impl AvcOp for AudiophileLedSwitch {
760    const OPCODE: u8 = VendorDependent::OPCODE;
761}
762
763impl AvcControl for AudiophileLedSwitch {
764    fn build_operands(&mut self, addr: &AvcAddr) -> Result<Vec<u8>, AvcCmdBuildError> {
765        self.op.data[3] = self.state.to_val();
766        AvcControl::build_operands(&mut self.op, addr)
767    }
768
769    fn parse_operands(&mut self, addr: &AvcAddr, operands: &[u8]) -> Result<(), AvcRespParseError> {
770        AvcControl::parse_operands(&mut self.op, addr, operands)
771    }
772}
773
774/// The structure to express metering information. The `Default` trait should be implemented to
775/// call `MaudioNormalMeterProtocol::create_meter()`.
776#[derive(Debug, Clone, PartialEq, Eq)]
777pub struct MaudioNormalMeter {
778    pub phys_inputs: Vec<i32>,
779    pub stream_inputs: Option<Vec<i32>>,
780    pub phys_outputs: Vec<i32>,
781    pub headphone: Option<[i32; 2]>,
782    pub aux_output: Option<[i32; 2]>,
783    pub rotaries: Option<[i32; 2]>,
784    pub switch: Option<AudiophileSwitchState>,
785    pub sync_status: Option<bool>,
786
787    cache: Vec<u8>,
788}
789
790/// The trait for meter protocol specific to M-Audio FireWire series.
791pub trait MaudioNormalMeterProtocol {
792    const PHYS_INPUT_COUNT: usize;
793    const STREAM_INPUT_COUNT: usize;
794    const PHYS_OUTPUT_COUNT: usize;
795    const ROTARY_COUNT: usize;
796    const HAS_SWITCH: bool;
797    const HAS_SYNC_STATUS: bool;
798
799    const LEVEL_MIN: i32 = 0;
800    const LEVEL_MAX: i32 = i32::MAX;
801    const LEVEL_STEP: i32 = 0x100;
802
803    const ROTARY_MIN: i32 = 0;
804    const ROTARY_MAX: i32 = i16::MAX as i32;
805    const ROTARY_STEP: i32 = 0x200;
806
807    fn create_meter() -> MaudioNormalMeter {
808        let mut meter = MaudioNormalMeter {
809            phys_inputs: vec![0; Self::PHYS_INPUT_COUNT],
810            stream_inputs: Default::default(),
811            phys_outputs: vec![0; Self::PHYS_OUTPUT_COUNT],
812            headphone: Default::default(),
813            aux_output: Default::default(),
814            rotaries: Default::default(),
815            switch: Default::default(),
816            sync_status: Default::default(),
817            cache: vec![0; Self::calculate_meter_frame_size()],
818        };
819
820        if Self::STREAM_INPUT_COUNT > 0 {
821            meter.stream_inputs = Some(vec![0; Self::STREAM_INPUT_COUNT]);
822        } else {
823            meter.headphone = Some(Default::default());
824            meter.aux_output = Some(Default::default());
825        }
826
827        if Self::ROTARY_COUNT > 0 {
828            meter.rotaries = Some(Default::default());
829        }
830
831        if Self::HAS_SWITCH {
832            meter.switch = Some(Default::default());
833        }
834
835        if Self::HAS_SYNC_STATUS {
836            meter.sync_status = Some(Default::default());
837        }
838
839        meter
840    }
841
842    fn calculate_meter_frame_size() -> usize {
843        let mut size = Self::PHYS_INPUT_COUNT + Self::PHYS_OUTPUT_COUNT;
844
845        if Self::STREAM_INPUT_COUNT > 0 {
846            size += Self::STREAM_INPUT_COUNT;
847        } else {
848            // Plus headphone-1 and -2, aux-1 and -2.
849            size += 4;
850        }
851
852        if Self::ROTARY_COUNT > 0 || Self::HAS_SWITCH || Self::HAS_SYNC_STATUS {
853            size += 1;
854        }
855
856        size * 4
857    }
858
859    fn read_meter(
860        req: &FwReq,
861        node: &FwNode,
862        meter: &mut MaudioNormalMeter,
863        timeout_ms: u32,
864    ) -> Result<(), Error> {
865        let frame = &mut meter.cache;
866
867        // For rotaries, switch, and sync_status if available.
868        let mut bitmap = [0; 4];
869        let pos = frame.len() - 4;
870        bitmap.copy_from_slice(&frame[pos..]);
871
872        req.transaction_sync(
873            node,
874            FwTcode::ReadBlockRequest,
875            DM_APPL_METER_OFFSET,
876            frame.len(),
877            frame,
878            timeout_ms,
879        )?;
880
881        let mut quadlet = [0; 4];
882
883        meter.phys_inputs.iter_mut().enumerate().for_each(|(i, m)| {
884            let pos = i * 4;
885            quadlet.copy_from_slice(&frame[pos..(pos + 4)]);
886            *m = i32::from_be_bytes(quadlet);
887        });
888
889        if let Some(stream_inputs) = &mut meter.stream_inputs {
890            stream_inputs.iter_mut().enumerate().for_each(|(i, m)| {
891                let pos = (Self::PHYS_INPUT_COUNT + i) * 4;
892                quadlet.copy_from_slice(&frame[pos..(pos + 4)]);
893                *m = i32::from_be_bytes(quadlet);
894            });
895        }
896
897        meter
898            .phys_outputs
899            .iter_mut()
900            .enumerate()
901            .for_each(|(i, m)| {
902                let pos = (Self::PHYS_INPUT_COUNT + Self::STREAM_INPUT_COUNT + i) * 4;
903                quadlet.copy_from_slice(&frame[pos..(pos + 4)]);
904                *m = i32::from_be_bytes(quadlet);
905            });
906
907        if let Some(headphone) = &mut meter.headphone {
908            headphone.iter_mut().enumerate().for_each(|(i, m)| {
909                let pos = (Self::PHYS_INPUT_COUNT + Self::PHYS_OUTPUT_COUNT + i) * 4;
910                quadlet.copy_from_slice(&frame[pos..(pos + 4)]);
911                *m = i32::from_be_bytes(quadlet);
912            });
913        }
914
915        if let Some(aux_output) = &mut meter.aux_output {
916            aux_output.iter_mut().enumerate().for_each(|(i, m)| {
917                let pos = (Self::PHYS_INPUT_COUNT + Self::PHYS_OUTPUT_COUNT + 2 + i) * 4;
918                quadlet.copy_from_slice(&frame[pos..(pos + 4)]);
919                *m = i32::from_be_bytes(quadlet);
920            });
921        }
922
923        if let Some(rotaries) = &mut meter.rotaries {
924            rotaries.iter_mut().enumerate().for_each(|(i, r)| {
925                let shift = i * 4;
926                if (bitmap[1] ^ frame[frame.len() - 3]) & (0x0f << shift) > 0 {
927                    let flag = (frame[frame.len() - 3] >> shift) & 0x0f;
928                    if flag == 0x01 {
929                        if *r <= Self::ROTARY_MAX - Self::ROTARY_STEP {
930                            *r += Self::ROTARY_STEP;
931                        }
932                    } else if flag == 0x02 {
933                        if *r >= Self::ROTARY_MIN + Self::ROTARY_STEP {
934                            *r -= Self::ROTARY_STEP;
935                        }
936                    }
937                }
938            });
939        }
940
941        if let Some(switch) = &mut meter.switch {
942            if bitmap[0] ^ frame[frame.len() - 4] & 0xf0 > 0 {
943                if bitmap[0] & 0xf0 > 0 {
944                    *switch = match *switch {
945                        AudiophileSwitchState::Off => AudiophileSwitchState::A,
946                        AudiophileSwitchState::A => AudiophileSwitchState::B,
947                        AudiophileSwitchState::B => AudiophileSwitchState::Off,
948                    };
949                }
950            }
951        }
952
953        if let Some(sync_status) = &mut meter.sync_status {
954            *sync_status = frame[frame.len() - 1] > 0;
955        }
956
957        Ok(())
958    }
959}
960
961/// The protocol implementation for mixer in FireWire Solo.
962#[derive(Default, Debug)]
963pub struct OzonicMixerProtocol;
964
965impl MaudioNormalMixerOperation for OzonicMixerProtocol {
966    const DST_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
967        (0x01, AudioCh::Each(0)), // mixer-1/2 directly connected to analog-output-1/2
968        (0x02, AudioCh::Each(0)), // mixer-3/4 directly connected to analog-output-3/4
969    ];
970    const SRC_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)] = &[
971        (0x02, AudioCh::Each(0)), // analog-input-1/2
972        (0x03, AudioCh::Each(0)), // analog-input-3/4
973        (0x00, AudioCh::Each(0)), // stream-input-1/2
974        (0x01, AudioCh::Each(0)), // stream-input-3/4
975    ];
976}
977
978/// The parameter of mixer. The `Default` trait should be implemented to call
979/// `MaudioNormalMixerOperation::create_mixer_parameters()`.
980#[derive(Debug, Clone, PartialEq, Eq)]
981pub struct MaudioNormalMixerParameters(pub Vec<Vec<bool>>);
982
983/// The trait for mixer operation.
984pub trait MaudioNormalMixerOperation {
985    const DST_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)];
986    const SRC_FUNC_BLOCK_ID_LIST: &'static [(u8, AudioCh)];
987
988    const SRC_OFF: i16 = 0x8000u16 as i16;
989    const SRC_ON: i16 = 0;
990
991    fn create_mixer_parameters() -> MaudioNormalMixerParameters {
992        MaudioNormalMixerParameters(vec![
993            vec![
994                Default::default();
995                Self::SRC_FUNC_BLOCK_ID_LIST.len()
996            ];
997            Self::DST_FUNC_BLOCK_ID_LIST.len()
998        ])
999    }
1000
1001    /// The ASIC get heavy load from AV/C status request for mixer parameters, thus this method
1002    /// often brings timeout.
1003    fn cache(
1004        avc: &BebobAvc,
1005        params: &mut MaudioNormalMixerParameters,
1006        timeout_ms: u32,
1007    ) -> Result<(), Error> {
1008        assert_eq!(params.0.len(), Self::DST_FUNC_BLOCK_ID_LIST.len());
1009        assert_eq!(params.0[0].len(), Self::SRC_FUNC_BLOCK_ID_LIST.len());
1010
1011        params
1012            .0
1013            .iter_mut()
1014            .zip(Self::DST_FUNC_BLOCK_ID_LIST)
1015            .try_for_each(|(srcs, &(dst_func_block_id, dst_audio_ch))| {
1016                srcs.iter_mut()
1017                    .zip(Self::SRC_FUNC_BLOCK_ID_LIST)
1018                    .try_for_each(|(src, &(src_func_block_id, src_audio_ch))| {
1019                        let mut op = AudioProcessing::new(
1020                            dst_func_block_id,
1021                            CtlAttr::Current,
1022                            src_func_block_id,
1023                            src_audio_ch,
1024                            dst_audio_ch,
1025                            ProcessingCtl::Mixer(vec![-1]),
1026                        );
1027                        avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
1028                            .map(|_| {
1029                                if let ProcessingCtl::Mixer(data) = op.ctl {
1030                                    *src = data[0] == Self::SRC_ON
1031                                }
1032                            })
1033                    })
1034            })
1035    }
1036
1037    fn update(
1038        avc: &BebobAvc,
1039        params: &MaudioNormalMixerParameters,
1040        old: &mut MaudioNormalMixerParameters,
1041        timeout_ms: u32,
1042    ) -> Result<(), Error> {
1043        old.0
1044            .iter_mut()
1045            .zip(params.0.iter())
1046            .zip(Self::DST_FUNC_BLOCK_ID_LIST)
1047            .try_for_each(
1048                |((old_srcs, new_srcs), &(dst_func_block_id, dst_audio_ch))| {
1049                    old_srcs
1050                        .iter_mut()
1051                        .zip(new_srcs.iter())
1052                        .zip(Self::SRC_FUNC_BLOCK_ID_LIST)
1053                        .filter(|((o, n), _)| !n.eq(o))
1054                        .try_for_each(|((old, &new), &(src_func_block_id, src_audio_ch))| {
1055                            let val = if new { Self::SRC_ON } else { Self::SRC_OFF };
1056                            let mut op = AudioProcessing::new(
1057                                dst_func_block_id,
1058                                CtlAttr::Current,
1059                                src_func_block_id,
1060                                src_audio_ch,
1061                                dst_audio_ch,
1062                                ProcessingCtl::Mixer(vec![val]),
1063                            );
1064                            avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
1065                                .map(|_| *old = new)
1066                        })
1067                },
1068            )
1069    }
1070}