firewire_fireworks_protocols/
port_conf.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol about port configuration.
5//!
6//! The module includes protocol about port configuration defined by Echo Audio Digital Corporation
7//! for Fireworks board module.
8
9use super::*;
10
11const CATEGORY_PORT_CONF: u32 = 9;
12
13const CMD_SET_MIRROR: u32 = 0;
14const CMD_GET_MIRROR: u32 = 1;
15const CMD_SET_DIG_MODE: u32 = 2;
16const CMD_GET_DIG_MODE: u32 = 3;
17const CMD_SET_PHANTOM: u32 = 4;
18const CMD_GET_PHANTOM: u32 = 5;
19const CMD_SET_STREAM_MAP: u32 = 6;
20const CMD_GET_STREAM_MAP: u32 = 7;
21
22/// The parameters for source of control room.
23#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
24pub struct EfwControlRoomSource(pub usize);
25
26const CONTROL_ROOM_SOURCES: &[PhysGroupType] = &[
27    PhysGroupType::Analog,
28    PhysGroupType::Headphones,
29    PhysGroupType::Spdif,
30];
31
32/// The specification of control room operation.
33pub trait EfwControlRoomSpecification: EfwHardwareSpecification {
34    fn control_room_source_pairs() -> Vec<(PhysGroupType, usize)> {
35        Self::PHYS_OUTPUT_GROUPS
36            .iter()
37            .filter(|(group_type, _)| {
38                CONTROL_ROOM_SOURCES
39                    .iter()
40                    .find(|t| group_type.eq(t))
41                    .is_some()
42            })
43            .flat_map(|&(group_type, count)| {
44                let entries: Vec<(PhysGroupType, usize)> =
45                    (0..count).step_by(2).map(|i| (group_type, i)).collect();
46                entries
47            })
48            .collect()
49    }
50}
51
52fn phys_group_pairs(groups: &[(PhysGroupType, usize)]) -> Vec<(PhysGroupType, usize)> {
53    groups
54        .iter()
55        .flat_map(|&(group_type, count)| {
56            let entries: Vec<(PhysGroupType, usize)> =
57                (0..count).step_by(2).map(|pos| (group_type, pos)).collect();
58            entries
59        })
60        .collect()
61}
62
63impl<O, P> EfwWhollyCachableParamsOperation<P, EfwControlRoomSource> for O
64where
65    O: EfwControlRoomSpecification,
66    P: EfwProtocolExtManual,
67{
68    fn cache_wholly(
69        proto: &mut P,
70        states: &mut EfwControlRoomSource,
71        timeout_ms: u32,
72    ) -> Result<(), Error> {
73        let args = Vec::new();
74        let mut params = vec![0];
75        proto.transaction(
76            CATEGORY_PORT_CONF,
77            CMD_GET_MIRROR,
78            &args,
79            &mut params,
80            timeout_ms,
81        )?;
82        let pos = (params[0] / 2) as usize;
83        let entries = phys_group_pairs(Self::PHYS_OUTPUT_GROUPS);
84        let entry = entries.iter().nth(pos).ok_or_else(|| {
85            let msg = format!("Unexpected value {} for source of control room", pos);
86            Error::new(FileError::Nxio, &msg)
87        })?;
88
89        states.0 = Self::control_room_source_pairs()
90            .iter()
91            .position(|e| entry.eq(e))
92            .ok_or_else(|| {
93                let msg = format!(
94                    "Unexpected entry for source of control room: {:?},{}",
95                    entry.0, entry.1
96                );
97                Error::new(FileError::Nxio, &msg)
98            })?;
99
100        Ok(())
101    }
102}
103
104impl<O, P> EfwWhollyUpdatableParamsOperation<P, EfwControlRoomSource> for O
105where
106    O: EfwControlRoomSpecification,
107    P: EfwProtocolExtManual,
108{
109    fn update_wholly(
110        proto: &mut P,
111        states: &EfwControlRoomSource,
112        timeout_ms: u32,
113    ) -> Result<(), Error> {
114        let pairs = Self::control_room_source_pairs();
115        let entry = pairs.iter().nth(states.0).ok_or_else(|| {
116            let msg = format!("Invalid value for source of control room: {}", states.0);
117            Error::new(FileError::Inval, &msg)
118        })?;
119
120        let pos = phys_group_pairs(Self::PHYS_OUTPUT_GROUPS)
121            .iter()
122            .position(|e| entry.eq(&e))
123            .map(|pos| pos * 2)
124            .ok_or_else(|| {
125                let msg = format!(
126                    "Invalid entry for source of control room: {:?},{}",
127                    entry.0, entry.1
128                );
129                Error::new(FileError::Inval, &msg)
130            })?;
131
132        let args = [pos as u32];
133        let mut params = Vec::new();
134        proto.transaction(
135            CATEGORY_PORT_CONF,
136            CMD_SET_MIRROR,
137            &args,
138            &mut params,
139            timeout_ms,
140        )
141    }
142}
143
144/// Type of audio signal for dignal input and output.
145#[derive(Debug, Copy, Clone, PartialEq, Eq)]
146pub enum EfwDigitalMode {
147    /// Coaxial interface for S/PDIF signal.
148    SpdifCoax,
149    /// XLR interface for AES/EBU signal.
150    AesebuXlr,
151    /// Optical interface for S/PDIF signal.
152    SpdifOpt,
153    /// Optical interface for ADAT signal.
154    AdatOpt,
155    Unknown(u32),
156}
157
158impl Default for EfwDigitalMode {
159    fn default() -> Self {
160        Self::Unknown(u32::MAX)
161    }
162}
163
164fn serialize_digital_mode(mode: &EfwDigitalMode, val: &mut u32) {
165    *val = match *mode {
166        EfwDigitalMode::SpdifCoax => 0,
167        EfwDigitalMode::AesebuXlr => 1,
168        EfwDigitalMode::SpdifOpt => 2,
169        EfwDigitalMode::AdatOpt => 3,
170        EfwDigitalMode::Unknown(val) => val,
171    };
172}
173
174fn deserialize_digital_mode(val: u32) -> EfwDigitalMode {
175    match val {
176        0 => EfwDigitalMode::SpdifCoax,
177        1 => EfwDigitalMode::AesebuXlr,
178        2 => EfwDigitalMode::SpdifOpt,
179        3 => EfwDigitalMode::AdatOpt,
180        _ => EfwDigitalMode::Unknown(val),
181    }
182}
183
184/// The specification for mode of digital input and output.
185pub trait EfwDigitalModeSpecification: EfwHardwareSpecification {
186    const DIG_MODES: &'static [(HwCap, EfwDigitalMode)] = &[
187        (HwCap::OptionalSpdifCoax, EfwDigitalMode::SpdifCoax),
188        (HwCap::OptionalAesebuXlr, EfwDigitalMode::AesebuXlr),
189        (HwCap::OptionalSpdifOpt, EfwDigitalMode::SpdifOpt),
190        (HwCap::OptionalAdatOpt, EfwDigitalMode::AdatOpt),
191    ];
192
193    fn create_digital_mode() -> EfwDigitalMode {
194        Self::DIG_MODES
195            .iter()
196            .find(|(cap, _)| Self::CAPABILITIES.iter().find(|c| cap.eq(c)).is_some())
197            .map(|(_, mode)| *mode)
198            .unwrap()
199    }
200
201    fn create_digital_modes() -> Vec<EfwDigitalMode> {
202        Self::DIG_MODES
203            .iter()
204            .filter(|(cap, _)| Self::CAPABILITIES.iter().find(|c| cap.eq(c)).is_some())
205            .map(|(_, mode)| *mode)
206            .collect()
207    }
208}
209
210impl<O, P> EfwWhollyCachableParamsOperation<P, EfwDigitalMode> for O
211where
212    O: EfwDigitalModeSpecification,
213    P: EfwProtocolExtManual,
214{
215    fn cache_wholly(
216        proto: &mut P,
217        states: &mut EfwDigitalMode,
218        timeout_ms: u32,
219    ) -> Result<(), Error> {
220        assert!(Self::CAPABILITIES
221            .iter()
222            .find(|cap| Self::DIG_MODES.iter().find(|(c, _)| c.eq(cap)).is_some())
223            .is_some());
224
225        let args = Vec::new();
226        let mut params = vec![0];
227        proto
228            .transaction(
229                CATEGORY_PORT_CONF,
230                CMD_GET_DIG_MODE,
231                &args,
232                &mut params,
233                timeout_ms,
234            )
235            .map(|_| *states = deserialize_digital_mode(params[0]))
236    }
237}
238
239impl<O, P> EfwWhollyUpdatableParamsOperation<P, EfwDigitalMode> for O
240where
241    O: EfwDigitalModeSpecification,
242    P: EfwProtocolExtManual,
243{
244    fn update_wholly(proto: &mut P, states: &EfwDigitalMode, timeout_ms: u32) -> Result<(), Error> {
245        assert!(Self::CAPABILITIES
246            .iter()
247            .find(|cap| Self::DIG_MODES.iter().find(|(c, _)| c.eq(cap)).is_some())
248            .is_some());
249
250        let mut args = [0];
251        let mut params = Vec::new();
252        serialize_digital_mode(&states, &mut args[0]);
253        proto.transaction(
254            CATEGORY_PORT_CONF,
255            CMD_SET_DIG_MODE,
256            &args,
257            &mut params,
258            timeout_ms,
259        )
260    }
261}
262
263/// The parameters for phantom powering.
264#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
265pub struct EfwPhantomPowering(pub bool);
266
267/// The specification for mode of digital input and output.
268pub trait EfwPhantomPoweringSpecification: EfwHardwareSpecification {}
269
270impl<O, P> EfwWhollyCachableParamsOperation<P, EfwPhantomPowering> for O
271where
272    O: EfwPhantomPoweringSpecification,
273    P: EfwProtocolExtManual,
274{
275    fn cache_wholly(
276        proto: &mut P,
277        states: &mut EfwPhantomPowering,
278        timeout_ms: u32,
279    ) -> Result<(), Error> {
280        let args = Vec::new();
281        let mut params = vec![0];
282        proto
283            .transaction(
284                CATEGORY_PORT_CONF,
285                CMD_GET_PHANTOM,
286                &args,
287                &mut params,
288                timeout_ms,
289            )
290            .map(|_| states.0 = params[0] > 0)
291    }
292}
293
294impl<O, P> EfwWhollyUpdatableParamsOperation<P, EfwPhantomPowering> for O
295where
296    O: EfwPhantomPoweringSpecification,
297    P: EfwProtocolExtManual,
298{
299    fn update_wholly(
300        proto: &mut P,
301        states: &EfwPhantomPowering,
302        timeout_ms: u32,
303    ) -> Result<(), Error> {
304        let args = [states.0 as u32];
305        let mut params = Vec::new();
306        proto.transaction(
307            CATEGORY_PORT_CONF,
308            CMD_SET_PHANTOM,
309            &args,
310            &mut params,
311            timeout_ms,
312        )
313    }
314}
315
316/// Mapping between rx stream channel pairs and physical output channel pairs per mode of sampling
317/// transfer frequency.
318#[derive(Debug, Clone, PartialEq, Eq)]
319pub struct EfwRxStreamMaps(pub Vec<Vec<usize>>);
320
321/// The specification of rx stream mapping.
322pub trait EfwRxStreamMapsSpecification: EfwHardwareSpecification {
323    const STREAM_MAPPING_RATE_TABLE: [&'static [u32]; 3] =
324        [&[44100, 48000, 32000], &[88200, 96000], &[176400, 192000]];
325
326    fn create_rx_stream_maps() -> EfwRxStreamMaps {
327        let maps = Self::RX_CHANNEL_COUNTS
328            .iter()
329            .map(|&count| vec![Default::default(); count])
330            .collect();
331        EfwRxStreamMaps(maps)
332    }
333}
334
335const MAP_SIZE: usize = 70;
336const MAP_ENTRY_UNABAILABLE: u32 = 0xffffffff;
337
338impl<O, P> EfwWhollyCachableParamsOperation<P, EfwRxStreamMaps> for O
339where
340    O: EfwRxStreamMapsSpecification,
341    P: EfwProtocolExtManual,
342{
343    fn cache_wholly(
344        proto: &mut P,
345        states: &mut EfwRxStreamMaps,
346        timeout_ms: u32,
347    ) -> Result<(), Error> {
348        states
349            .0
350            .iter_mut()
351            .zip(Self::STREAM_MAPPING_RATE_TABLE)
352            .try_for_each(|(state, rates)| {
353                let args = [rates[0] as u32];
354                let mut params = vec![0; MAP_SIZE];
355
356                proto
357                    .transaction(
358                        CATEGORY_PORT_CONF,
359                        CMD_GET_STREAM_MAP,
360                        &args,
361                        &mut params,
362                        timeout_ms,
363                    )
364                    .map(|_| {
365                        params[4..]
366                            .iter()
367                            .zip(state.iter_mut())
368                            .for_each(|(&quad, src)| *src = (quad / 2) as usize);
369                    })
370            })
371    }
372}
373
374impl<O, P> EfwPartiallyUpdatableParamsOperation<P, EfwRxStreamMaps> for O
375where
376    O: EfwRxStreamMapsSpecification,
377    P: EfwProtocolExtManual,
378{
379    fn update_partially(
380        proto: &mut P,
381        states: &mut EfwRxStreamMaps,
382        updates: EfwRxStreamMaps,
383        timeout_ms: u32,
384    ) -> Result<(), Error> {
385        states
386            .0
387            .iter_mut()
388            .zip(&updates.0)
389            .zip(Self::STREAM_MAPPING_RATE_TABLE)
390            .zip(Self::RX_CHANNEL_COUNTS)
391            .zip(Self::TX_CHANNEL_COUNTS)
392            .filter(|((((o, n), _), _), _)| !n.eq(o))
393            .try_for_each(
394                |((((curr, update), rates), rx_channel_count), tx_channel_count)| {
395                    let mut args = [0; MAP_SIZE];
396
397                    args[0] = rates[0];
398                    // NOTE: This field is filled with clock source bits, however it's not used actually.
399                    args[1] = 0;
400                    args[2] = (rx_channel_count / 2) as u32;
401                    args[3] = (Self::phys_output_count() / 2) as u32;
402                    args[4..36].fill(MAP_ENTRY_UNABAILABLE);
403                    args[36] = (tx_channel_count / 2) as u32;
404                    args[37] = (Self::phys_input_count() / 2) as u32;
405                    args[38..70].fill(MAP_ENTRY_UNABAILABLE);
406
407                    args[4..]
408                        .iter_mut()
409                        .zip(update.iter())
410                        .for_each(|(quad, &src)| *quad = (src * 2) as u32);
411
412                    // MEMO: No hardware supports tx stream mapping.
413
414                    proto
415                        .transaction(
416                            CATEGORY_PORT_CONF,
417                            CMD_SET_STREAM_MAP,
418                            &args,
419                            &mut Vec::new(),
420                            timeout_ms,
421                        )
422                        .map(|_| curr.copy_from_slice(&update))
423                },
424            )
425    }
426}