firewire_bebob_protocols/
yamaha_terratec.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol implementation for Yamaha Go and Terratec Phase 24 FW series.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by Yamaha and Terratec for Go and 24 FW series.
8//!
9//! DM1000E is used for Yamaha GO 44 and GO 46.
10//!
11//! ## Diagram of internal signal flow
12//!
13//! ```text
14//! analog-input-1/2  --+----------------------------------------+-----> stream-output-1/2
15//! digital-input-1/2 --|-+--------------------------------------|-+---> stream-output-3/4
16//!                     | |                                      | |
17//!                     | |      ++=======++                     | |
18//!                     +-|----> ||       ||                     | |
19//!                       +----> ||       ||                     | |
20//!                              || 6 x 2 ||                     | |
21//! stream-input-1/2 ---+------> || mixer ||--> mixer-output-1/2 | |
22//! stream-input-3/4 ---|-+----> ||       ||           |         | |
23//! stream-input-5/6 ---|-|-+--> ||       ||           |         | |
24//!                     | | |    ++=======++           |         | |
25//!                     +-|-|--------------------------|---------|-|--->
26//!                     | +-|--------------------------|---------|-|--->
27//!                     | | +--------------------------|---------|-|---> analog-output-1/2
28//!                     | | |                          +---------|-|---> (one source only)
29//!                     | | |                          |         +-|--->
30//!                     | | |                          |         | +--->
31//!                     | | |                          |         | |
32//!                     +-|-|--------------------------|---------|-|--->
33//!                     | +-|--------------------------|---------|-|--->
34//!                     | | +--------------------------|---------|-|---> analog-output-3/4
35//!                     | | |                          +---------|-|---> (one source only)
36//!                     | | |                          |         +-|--->
37//!                     | | |                          |         | +--->
38//!                     | | |                          |         | |
39//!                     +-|-|--------------------------|---------|-|--->
40//!                       +-|--------------------------|---------|-|--->
41//!                         +--------------------------|---------|-|---> digital-output-1/2
42//!                                                    +---------|-|---> (one source only)
43//!                                                              +-|--->
44//!                                                                +--->
45//! ```
46//!
47//! The protocol implementation for Yamaha GO 44 was written with firmware version below:
48//!
49//! ```sh
50//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
51//! protocol:
52//!   version: 1
53//! bootloader:
54//!   timestamp: 2005-12-20T10:10:04+0000
55//!   version: 0.0.0
56//! hardware:
57//!   GUID: 0x0002e24700a0de00
58//!   model ID: 0x00000b
59//!   revision: 0.0.1
60//! software:
61//!   timestamp: 2006-04-20T10:57:53+0000
62//!   ID: 0x0010000b
63//!   revision: 1.29.3359
64//! image:
65//!   base address: 0x20080000
66//!   maximum size: 0x180000
67//! ```
68//!
69//! The protocol implementation for Yamaha GO 46 was written with firmware version below:
70//!
71//! ```sh
72//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
73//! protocol:
74//!   version: 1
75//! bootloader:
76//!   timestamp: 2005-12-20T10:10:14+0000
77//!   version: 0.0.0
78//! hardware:
79//!   GUID: 0x000283e700a0de00
80//!   model ID: 0x00000c
81//!   revision: 0.0.1
82//! software:
83//!   timestamp: 2006-01-26T02:31:32+0000
84//!   ID: 0x0010000c
85//!   revision: 1.34.3359
86//! image:
87//!   base address: 0x20080000
88//!   maximum size: 0x180000
89//! ```
90//!
91//! The protocol implementation for Terratec Phase X24 was written with firmware version below:
92//!
93//! ```sh
94//! $ cargo run --bin bco-bootloader-info -- /dev/fw1
95//! protocol:
96//!   version: 1
97//! bootloader:
98//!   timestamp: 2005-07-29T02:05:14+0000
99//!   version: 0.0.0
100//! hardware:
101//!   GUID: 0x0062c9c7000aac07
102//!   model ID: 0x000007
103//!   revision: 0.0.1
104//! software:
105//!   timestamp: 2005-07-25T01:56:53+0000
106//!   ID: 0x00000007
107//!   revision: 1.32.3359
108//! image:
109//!   base address: 0x20080000
110//!   maximum size: 0x180000
111//! ```
112
113use super::*;
114
115/// The protocol implementation of media and sampling clock for Yamaha Go 44/46 and PHASE 24/X24 FW;
116#[derive(Default, Debug)]
117pub struct GoPhase24ClkProtocol;
118
119impl MediaClockFrequencyOperation for GoPhase24ClkProtocol {
120    const FREQ_LIST: &'static [u32] = &[44100, 48000, 88200, 96000, 192000];
121}
122
123const CLK_SRC_FB_ID: u8 = 0x04;
124
125impl SamplingClockSourceOperation for GoPhase24ClkProtocol {
126    // NOTE: these destination and source can not be connected actually.
127    const DST: SignalAddr = SignalAddr::Subunit(SignalSubunitAddr {
128        subunit: MUSIC_SUBUNIT_0,
129        plug_id: 0x04,
130    });
131
132    const SRC_LIST: &'static [SignalAddr] = &[
133        // Internal.
134        SignalAddr::Subunit(SignalSubunitAddr {
135            subunit: MUSIC_SUBUNIT_0,
136            plug_id: 0x03,
137        }),
138        // S/PDIF input.
139        SignalAddr::Unit(SignalUnitAddr::Ext(0x01)),
140    ];
141
142    fn cache_src(
143        avc: &BebobAvc,
144        params: &mut SamplingClockParameters,
145        timeout_ms: u32,
146    ) -> Result<(), Error> {
147        let mut op = AudioSelector::new(CLK_SRC_FB_ID, CtlAttr::Current, 0xff);
148        avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
149            .map(|_| params.src_idx = op.input_plug_id as usize)
150    }
151
152    fn update_src(
153        avc: &BebobAvc,
154        params: &SamplingClockParameters,
155        old: &mut SamplingClockParameters,
156        timeout_ms: u32,
157    ) -> Result<(), Error> {
158        let mut op = AudioSelector::new(CLK_SRC_FB_ID, CtlAttr::Current, params.src_idx as u8);
159        avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
160            .map(|_| *old = *params)
161    }
162}
163
164/// The protocol implementation of physical input for coaxial models.
165pub struct GoPhase24CoaxPhysInputProtocol;
166
167const INPUT_NOMINAL_LEVEL_FB_ID: u8 = 0x02;
168
169const INPUT_NOMINAL_LEVELS: [i16; 3] = [0xf400u16 as i16, 0xfd00u16 as i16, 0x0000u16 as i16];
170
171impl AvcSelectorOperation for GoPhase24CoaxPhysInputProtocol {
172    // Unused.
173    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x00];
174    const INPUT_PLUG_ID_LIST: &'static [u8] = &[0x00, 0x01, 0x02];
175
176    fn cache_selectors(
177        avc: &BebobAvc,
178        params: &mut AvcSelectorParameters,
179        timeout_ms: u32,
180    ) -> Result<(), Error> {
181        assert_eq!(params.selectors.len(), 1);
182
183        let mut op = AudioFeature::new(
184            INPUT_NOMINAL_LEVEL_FB_ID,
185            CtlAttr::Current,
186            AudioCh::Master,
187            FeatureCtl::Volume(VolumeData::new(1)),
188        );
189        avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
190        if let FeatureCtl::Volume(data) = op.ctl {
191            INPUT_NOMINAL_LEVELS
192                .iter()
193                .position(|l| *l == data.0[0])
194                .ok_or_else(|| {
195                    let msg = format!(
196                        "Unexpected value for value of nominal level: 0x{:04x}",
197                        data.0[0]
198                    );
199                    Error::new(FileError::Io, &msg)
200                })
201                .map(|pos| params.selectors[0] = pos)
202        } else {
203            Ok(())
204        }
205    }
206
207    fn update_selectors(
208        avc: &BebobAvc,
209        params: &AvcSelectorParameters,
210        old: &mut AvcSelectorParameters,
211        timeout_ms: u32,
212    ) -> Result<(), Error> {
213        assert_eq!(params.selectors.len(), 1);
214        assert_eq!(old.selectors.len(), 1);
215
216        if old.selectors[0] != params.selectors[0] {
217            let val = INPUT_NOMINAL_LEVELS
218                .iter()
219                .nth(old.selectors[0])
220                .ok_or_else(|| {
221                    let msg = format!(
222                        "Invalid argument for index of nominal level: {}",
223                        old.selectors[0]
224                    );
225                    Error::new(FileError::Inval, &msg)
226                })
227                .copied()?;
228            let mut op = AudioFeature::new(
229                INPUT_NOMINAL_LEVEL_FB_ID,
230                CtlAttr::Current,
231                AudioCh::Master,
232                FeatureCtl::Volume(VolumeData(vec![val])),
233            );
234            avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
235                .map(|_| {
236                    old.selectors[0] = params.selectors[0];
237                })
238        } else {
239            Ok(())
240        }
241    }
242}
243
244/// The protocol implementation of physical output for coaxial models.
245pub struct GoPhase24CoaxPhysOutputProtocol;
246
247impl AvcSelectorOperation for GoPhase24CoaxPhysOutputProtocol {
248    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[
249        0x01, // analog-output-1/2
250        0x03, // digital-output-1/2
251    ];
252    const INPUT_PLUG_ID_LIST: &'static [u8] = &[
253        0x00, // stream-input-1/2
254        0x01, // stream-input-3/4
255        0x02, // analog-input-1/2
256        0x03, // digital-input-1/2
257        0x04, // mixer-output-1/2
258        0x05, // stream-input-5/6
259    ];
260}
261/// The protocol implementation of physical output for optical models.
262#[derive(Default, Debug)]
263pub struct GoPhase24OptPhysOutputProtocol;
264
265impl AvcAudioFeatureSpecification for GoPhase24OptPhysOutputProtocol {
266    const ENTRIES: &'static [(u8, AudioCh)] = &[
267        (0x01, AudioCh::Each(0)), // analog-output-1
268        (0x01, AudioCh::Each(1)), // analog-output-2
269        (0x01, AudioCh::Each(2)), // analog-output-3
270        (0x01, AudioCh::Each(3)), // analog-output-4
271    ];
272}
273
274impl AvcLevelOperation for GoPhase24OptPhysOutputProtocol {}
275
276impl AvcMuteOperation for GoPhase24OptPhysOutputProtocol {}
277
278impl AvcSelectorOperation for GoPhase24OptPhysOutputProtocol {
279    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[
280        0x01, // analog-output-1/2
281        0x02, // analog-output-3/4
282        0x03, // digital-output-1/2
283    ];
284    const INPUT_PLUG_ID_LIST: &'static [u8] = &[
285        0x00, // stream-input-1/2
286        0x01, // stream-input-3/4
287        0x02, // analog-input-1/2
288        0x03, // digital-input-1/2
289        0x04, // mixer-output-1/2
290        0x05, // stream-input-5/6
291    ];
292}
293
294/// The protocol implementation of mixer source gain for coaxial model.
295pub struct GoPhase24CoaxHeadphoneProtocol;
296
297impl AvcSelectorOperation for GoPhase24CoaxHeadphoneProtocol {
298    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x02];
299    const INPUT_PLUG_ID_LIST: &'static [u8] = &[
300        0x00, // stream-input-1/2
301        0x01, // stream-input-3/4
302        0x02, // analog-input-1/2
303        0x03, // digital-input-1/2
304        0x04, // mixer-output-1/2
305        0x05, // stream-input-5/6
306    ];
307}
308
309/// The protocol implementation of mixer source gain.
310#[derive(Default, Debug)]
311pub struct GoPhase24MixerSourceProtocol;
312
313impl AvcAudioFeatureSpecification for GoPhase24MixerSourceProtocol {
314    const ENTRIES: &'static [(u8, AudioCh)] = &[
315        (0x06, AudioCh::Each(0)), // analog-input-1/2
316        (0x06, AudioCh::Each(1)), // analog-input-1/2
317        (0x07, AudioCh::Each(0)), // digital-input-1/2
318        (0x07, AudioCh::Each(1)), // digital-input-1/2
319        (0x03, AudioCh::Each(0)), // stream-input-1/2
320        (0x03, AudioCh::Each(1)), // stream-input-1/2
321        (0x04, AudioCh::Each(0)), // stream-input-3/4
322        (0x04, AudioCh::Each(1)), // stream-input-3/4
323        (0x05, AudioCh::Each(0)), // stream-input-5/6
324        (0x05, AudioCh::Each(1)), // stream-input-5/6
325    ];
326}
327
328impl AvcLevelOperation for GoPhase24MixerSourceProtocol {}
329
330impl AvcMuteOperation for GoPhase24MixerSourceProtocol {}
331
332/// The protocol implementation of mixer output volume for coaxial models.
333#[derive(Default, Debug)]
334pub struct GoPhase24CoaxMixerOutputProtocol;
335
336impl AvcAudioFeatureSpecification for GoPhase24CoaxMixerOutputProtocol {
337    const ENTRIES: &'static [(u8, AudioCh)] = &[(0x01, AudioCh::Each(0)), (0x01, AudioCh::Each(1))];
338}
339
340impl AvcLevelOperation for GoPhase24CoaxMixerOutputProtocol {}
341
342impl AvcMuteOperation for GoPhase24CoaxMixerOutputProtocol {}
343
344/// The protocol implementation of mixer output volume for optical models.
345#[derive(Default, Debug)]
346pub struct GoPhase24OptMixerOutputProtocol;
347
348impl AvcAudioFeatureSpecification for GoPhase24OptMixerOutputProtocol {
349    const ENTRIES: &'static [(u8, AudioCh)] = &[(0x02, AudioCh::Each(0)), (0x02, AudioCh::Each(1))];
350}
351
352impl AvcLevelOperation for GoPhase24OptMixerOutputProtocol {}
353
354impl AvcMuteOperation for GoPhase24OptMixerOutputProtocol {}