firewire_bebob_protocols/terratec/
phase88.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol implementation for Terratec Phase 88 FW.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by Terratec for Phase 88 FW.
8//!
9//! ## Diagram of internal signal flow
10//!
11//! ```text
12//!                        analog-input-1/2 ------+------------> stream-output-1/2
13//!                        analog-input-3/4 ------|------------> stream-output-3/4
14//!                        analog-input-5/6 ------|-|-+--------> stream-output-5/6
15//! line-input-7/8 ---or-> analog-input-7/8 ------|-|-|-+------> stream-output-7/8
16//! mic-input-7/8  ---+                           | | | |
17//!                        digital-input-1/2 -----|-|-|-|-+----> stream-output-9/10
18//!                                               | | | | |
19//!                                               v v v v v
20//!                                             ++=========++
21//!                                             ||         ||
22//!                        stream-source-1/2 -> || 12 x  2 ||
23//!                        (one source only)    ||  mixer  ||
24//!                            ^ ^ ^ ^ ^        ||         ||
25//!                            | | | | |        ++---------++
26//!                            | | | | |              v
27//!                            | | | | |       mixer-output-1/2
28//!                            | | | | |              v
29//!                            | | | | |     (one destination only)
30//!                            | | | | |          | | | | |
31//! stream-input-1/2 ----------+-|-|-|-|---------or-|-|-|-|----> analog-output-1/2
32//! stream-input-3/4 ------------+-|-|-|-----------or-|-|-|----> analog-output-3/4
33//! stream-input-5/6 --------------+-|-|-------------or-|-|----> analog-output-5/6
34//! stream-input-7/8 ----------------+-|---------------or-|----> analog-output-7/8
35//! stream-input-9/10 -----------------+-----------------or----> digital-output-1/2
36//! ```
37
38use super::*;
39
40/// The protocol implementation for media and sampling clock.
41#[derive(Default, Debug)]
42pub struct Phase88ClkProtocol;
43
44impl MediaClockFrequencyOperation for Phase88ClkProtocol {
45    const FREQ_LIST: &'static [u32] = &[32000, 44100, 48000, 88200, 96000];
46}
47
48const CLK_SRC_EXT_FB_ID: u8 = 0x08;
49const CLK_SRC_EXT_WORD_FB_ID: u8 = 0x09;
50
51impl SamplingClockSourceOperation for Phase88ClkProtocol {
52    const DST: SignalAddr = SignalAddr::Subunit(SignalSubunitAddr {
53        subunit: MUSIC_SUBUNIT_0,
54        plug_id: 0x03,
55    });
56
57    const SRC_LIST: &'static [SignalAddr] = &[
58        // Internal.
59        SignalAddr::Subunit(SignalSubunitAddr {
60            subunit: MUSIC_SUBUNIT_0,
61            plug_id: 0x05,
62        }),
63        // S/PDIF input in optical interface.
64        SignalAddr::Unit(SignalUnitAddr::Ext(0x04)),
65        // Word clock input in BNC interface.
66        SignalAddr::Unit(SignalUnitAddr::Ext(0x07)),
67    ];
68
69    fn cache_src(
70        avc: &BebobAvc,
71        params: &mut SamplingClockParameters,
72        timeout_ms: u32,
73    ) -> Result<(), Error> {
74        let mut op = AudioSelector::new(CLK_SRC_EXT_FB_ID, CtlAttr::Current, 0xff);
75        avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
76        params.src_idx = if op.input_plug_id == 0x00 {
77            // Internal.
78            0
79        } else {
80            let mut op = AudioSelector::new(CLK_SRC_EXT_WORD_FB_ID, CtlAttr::Current, 0xff);
81            avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
82            if op.input_plug_id == 0x00 {
83                // S/PDIF.
84                1
85            } else {
86                // Word clock.
87                2
88            }
89        };
90        Ok(())
91    }
92
93    fn update_src(
94        avc: &BebobAvc,
95        params: &SamplingClockParameters,
96        old: &mut SamplingClockParameters,
97        timeout_ms: u32,
98    ) -> Result<(), Error> {
99        let (is_ext, ext_is_word) = match params.src_idx {
100            0 => (0u8, 0u8),
101            1 => (0u8, 1u8),
102            2 => (1u8, 1u8),
103            _ => {
104                let msg = format!("Invalid index of source of clock: {}", params.src_idx);
105                Err(Error::new(FileError::Inval, &msg))?
106            }
107        };
108
109        let mut op = AudioSelector::new(CLK_SRC_EXT_WORD_FB_ID, CtlAttr::Current, ext_is_word);
110        avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
111
112        let mut op = AudioSelector::new(CLK_SRC_EXT_FB_ID, CtlAttr::Current, is_ext);
113        avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
114
115        *old = *params;
116
117        Ok(())
118    }
119}
120
121/// The protocol implementation of physical input.
122#[derive(Default, Debug)]
123pub struct Phase88PhysInputProtocol;
124
125impl AvcSelectorOperation for Phase88PhysInputProtocol {
126    //  NOTE: source of analog-input-7/8
127    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x0a];
128    const INPUT_PLUG_ID_LIST: &'static [u8] = &[
129        0x00, // line
130        0x01, // mic
131    ];
132}
133
134/// The protocol implementation of source of physical input to mixer.
135#[derive(Default, Debug)]
136pub struct Phase88MixerPhysSourceProtocol;
137
138impl AvcAudioFeatureSpecification for Phase88MixerPhysSourceProtocol {
139    const ENTRIES: &'static [(u8, AudioCh)] = &[
140        (0x02, AudioCh::Each(0)), // analog-input-1
141        (0x02, AudioCh::Each(1)), // analog-input-2
142        (0x03, AudioCh::Each(0)), // analog-input-3
143        (0x03, AudioCh::Each(1)), // analog-input-4
144        (0x04, AudioCh::Each(0)), // analog-input-5
145        (0x04, AudioCh::Each(1)), // analog-input-6
146        (0x05, AudioCh::Each(0)), // analog-input-7
147        (0x05, AudioCh::Each(1)), // analog-input-8
148        (0x06, AudioCh::Each(0)), // digital-input-1
149        (0x06, AudioCh::Each(1)), // digital-input-2
150    ];
151}
152
153impl AvcLevelOperation for Phase88MixerPhysSourceProtocol {}
154
155impl AvcMuteOperation for Phase88MixerPhysSourceProtocol {}
156
157/// The protocol implementation of source of stream input to mixer.
158#[derive(Default, Debug)]
159pub struct Phase88MixerStreamSourceProtocol;
160
161impl AvcAudioFeatureSpecification for Phase88MixerStreamSourceProtocol {
162    const ENTRIES: &'static [(u8, AudioCh)] = &[
163        (0x07, AudioCh::Each(0)), // stream-source-1
164        (0x07, AudioCh::Each(1)), // stream-source-2
165    ];
166}
167
168impl AvcLevelOperation for Phase88MixerStreamSourceProtocol {}
169
170impl AvcMuteOperation for Phase88MixerStreamSourceProtocol {}
171
172impl AvcSelectorOperation for Phase88MixerStreamSourceProtocol {
173    const FUNC_BLOCK_ID_LIST: &'static [u8] = &[0x07];
174    const INPUT_PLUG_ID_LIST: &'static [u8] = &[
175        0x01, // stream-input-1/2
176        0x02, // stream-input-3/4
177        0x03, // stream-input-5/6
178        0x04, // stream-input-7/8
179        0x00, // stream-input-9/10
180    ];
181}
182
183/// The protocol implementation of mixer output.
184#[derive(Default, Debug)]
185pub struct Phase88MixerOutputProtocol;
186
187impl AvcAudioFeatureSpecification for Phase88MixerOutputProtocol {
188    const ENTRIES: &'static [(u8, AudioCh)] = &[(0x00, AudioCh::Each(0)), (0x01, AudioCh::Each(1))];
189}
190
191impl AvcLevelOperation for Phase88MixerOutputProtocol {}
192
193impl AvcMuteOperation for Phase88MixerOutputProtocol {}
194
195const MIXER_OUT_SELECTOR_FB_ID_LIST: [u8; 1] = [0x06];
196
197const MIXER_OUT_SELECTOR_ID_LIST: [u8; 6] = [
198    0x01, // analog-output-1/2
199    0x02, // analog-output-3/4
200    0x03, // analog-output-5/6
201    0x04, // analog-output-7/8
202    0x00, // digital-output-1/2
203    0x05, // no destination
204];
205
206const PHYS_OUTPUT_SELECTOR_FB_ID_LIST: [u8; 5] = [
207    0x01, // analog-output-1/2
208    0x02, // analog-output-3/4
209    0x03, // analog-output-5/6
210    0x04, // analog-output-7/8
211    0x05, // digital-output-1/2
212];
213
214// NOTE: "mixer-output-1/2", "stream-input" corresponds to the channel
215#[allow(dead_code)]
216const PHYS_OUTPUT_SELECTOR_ID_LIST: [u8; 2] = [0x00, 0x01];
217
218// NOTE: destination of mixer output.
219impl AvcSelectorOperation for Phase88MixerOutputProtocol {
220    const FUNC_BLOCK_ID_LIST: &'static [u8] = &MIXER_OUT_SELECTOR_FB_ID_LIST;
221    const INPUT_PLUG_ID_LIST: &'static [u8] = &MIXER_OUT_SELECTOR_ID_LIST;
222
223    fn cache_selectors(
224        avc: &BebobAvc,
225        params: &mut AvcSelectorParameters,
226        timeout_ms: u32,
227    ) -> Result<(), Error> {
228        assert_eq!(params.selectors.len(), 1);
229
230        let func_block_id = MIXER_OUT_SELECTOR_FB_ID_LIST[0];
231
232        let mut op = AudioSelector::new(func_block_id, CtlAttr::Current, 0xff);
233        avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
234
235        MIXER_OUT_SELECTOR_ID_LIST
236            .iter()
237            .position(|&input_plug_id| input_plug_id == op.input_plug_id)
238            .ok_or_else(|| {
239                let msg = format!(
240                    "Unexpected value for index of input plug number: {}",
241                    op.input_plug_id
242                );
243                Error::new(FileError::Io, &msg)
244            })
245            .map(|pos| params.selectors[0] = pos)
246    }
247
248    fn update_selectors(
249        avc: &BebobAvc,
250        params: &AvcSelectorParameters,
251        old: &mut AvcSelectorParameters,
252        timeout_ms: u32,
253    ) -> Result<(), Error> {
254        let selector = params.selectors[0];
255
256        if selector != old.selectors[0] {
257            let func_block_id = MIXER_OUT_SELECTOR_FB_ID_LIST[0];
258            let input_plug_id = MIXER_OUT_SELECTOR_ID_LIST
259                .iter()
260                .nth(selector)
261                .ok_or_else(|| {
262                    let msg = format!(
263                        "Invalid argument for index of input plug number: {}",
264                        selector
265                    );
266                    Error::new(FileError::Inval, &msg)
267                })
268                .copied()?;
269
270            let mut op = AudioSelector::new(func_block_id, CtlAttr::Current, input_plug_id);
271            avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
272
273            PHYS_OUTPUT_SELECTOR_FB_ID_LIST
274                .iter()
275                .enumerate()
276                .try_for_each(|(i, &func_block_id)| {
277                    let plug_id_idx = if input_plug_id == 0x05 {
278                        // The mixer-output-1/2 is not available. Set corresponding stream-input to
279                        // physical outputs as source.
280                        0
281                    } else {
282                        if i == 0 {
283                            // The mixer-output-1/2 is configured for source of the physical output.
284                            0
285                        } else {
286                            // The stream-input-1/2 is configured for source of the physical output.
287                            1
288                        }
289                    };
290
291                    let val = PHYS_OUTPUT_SELECTOR_ID_LIST[plug_id_idx];
292                    let mut op = AudioSelector::new(func_block_id, CtlAttr::Current, val);
293                    avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
294                })?;
295
296            old.selectors[0] = selector;
297        }
298
299        Ok(())
300    }
301}