firewire_fireworks_protocols/
hw_info.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4//! Protocol about hardware information.
5//!
6//! The module includes protocol about hardware information defined by Echo Audio Digital Corporation
7//! for Fireworks board module.
8//!
9//! ## Table for capabilities retrieved from each Fireworks model
10//!
11//! capabilities       | 1200f | 400f | af12(old) | af12(new) | af8(old) | af9 | af4 | af2 | rip |
12//! ------------------ | ----- | ---- | --------- | --------- | -------- | --- | --- | --- | --- |
13//! ChangeableRespAddr |   *   |   *  |     *     |     *     |    *     |  *  |  *  |  *  |  *  |
14//! ControlRoom        |       |   *  |           |           |          |     |     |     |     |
15//! OptionalSpdifCoax  |   *   |      |           |           |          |  *  |     |     |     |
16//! OptionalAesebuXlr  |   *   |      |           |           |          |     |     |     |     |
17//! Dsp                |   *   |   *  |     *     |     *     |    *     |     |     |     |     |
18//! Fpga               |   *   |      |           |           |          |  *  |  *  |  *  |  *  |
19//! PhantomPowering    |       |      |           |           |          |     |  *  |     |     |
20//! OutputMapping      |       |      |           |           |          |     |  *  |  *  |     |
21//! InputGain          |       |      |           |     *     |          |     |     |     |     |
22//! OptionalSpdifOpt   |       |      |           |           |          |  *  |     |     |     |
23//! OptionalAdatOpt    |       |      |           |           |          |  *  |     |     |     |
24//! NominalInput       |       |      |           |           |          |     |     |     |     |
25//! NominalOutput      |       |      |           |           |          |     |     |     |     |
26//! SoftClip           |       |      |           |           |          |     |     |     |     |
27//! RobotGuitar        |       |      |           |           |          |     |     |     |  *  |
28//! GuitarCharging     |       |      |           |           |          |     |     |     |  *  |
29
30use super::*;
31
32const CATEGORY_HWINFO: u32 = 0;
33
34const CMD_HWINFO: u32 = 0;
35const CMD_METER: u32 = 1;
36const CMD_CHANGE_RESP_ADDR: u32 = 2;
37const CMD_READ_SESSION_BLOCK: u32 = 3;
38
39/// Information of hardware.
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct HwInfo {
42    pub caps: Vec<HwCap>,
43    pub guid: u64,
44    pub hw_type: u32,
45    pub hw_version: u32,
46    pub vendor_name: String,
47    pub model_name: String,
48    pub clk_srcs: Vec<ClkSrc>,
49    pub rx_channels: [usize; 3],
50    pub tx_channels: [usize; 3],
51    pub phys_outputs: Vec<PhysGroupEntry>,
52    pub phys_inputs: Vec<PhysGroupEntry>,
53    pub midi_outputs: usize,
54    pub midi_inputs: usize,
55    pub clk_rates: Vec<u32>,
56    pub dsp_version: u32,
57    pub arm_version: u32,
58    pub mixer_playbacks: usize,
59    pub mixer_captures: usize,
60    pub fpga_version: u32,
61}
62
63impl Default for HwInfo {
64    fn default() -> Self {
65        Self {
66            caps: Vec::new(),
67            guid: 0,
68            hw_type: 0,
69            hw_version: 0,
70            vendor_name: String::new(),
71            model_name: String::new(),
72            clk_srcs: Vec::new(),
73            rx_channels: [0; 3],
74            tx_channels: [0; 3],
75            phys_outputs: Vec::new(),
76            phys_inputs: Vec::new(),
77            midi_outputs: 0,
78            midi_inputs: 0,
79            clk_rates: Vec::new(),
80            dsp_version: 0,
81            arm_version: 0,
82            mixer_playbacks: 0,
83            mixer_captures: 0,
84            fpga_version: 0,
85        }
86    }
87}
88
89// Known models.
90#[allow(dead_code)]
91const HW_TYPE_O400F: u32 = 0x0000400f;
92#[allow(dead_code)]
93const HW_TYPE_O1200F: u32 = 0x0001200f;
94#[allow(dead_code)]
95const HW_TYPE_AF2: u32 = 0x00000af2;
96#[allow(dead_code)]
97const HW_TYPE_AF4: u32 = 0x00000af4;
98#[allow(dead_code)]
99const HW_TYPE_AF8: u32 = 0x00000af8;
100#[allow(dead_code)]
101const HW_TYPE_AF9: u32 = 0x00000af9;
102#[allow(dead_code)]
103const HW_TYPE_AF12: u32 = 0x0000af12;
104#[allow(dead_code)]
105const HW_TYPE_RIP: u32 = 0x00afb9;
106
107impl HwInfo {
108    fn parse(&mut self, quads: &[u32]) -> Result<(), Error> {
109        self.caps = Self::parse_caps(quads[0]);
110        self.guid = ((quads[1] as u64) << 32) | (quads[2] as u64);
111        self.hw_type = quads[3];
112        self.hw_version = quads[4];
113        self.vendor_name = Self::parse_text(&quads[5..13])?;
114        self.model_name = Self::parse_text(&quads[13..21])?;
115        self.clk_srcs = Self::parse_supported_clk_srcs(quads[21]);
116        self.rx_channels = [quads[22] as usize, quads[45] as usize, quads[47] as usize];
117        self.tx_channels = [quads[23] as usize, quads[46] as usize, quads[48] as usize];
118        self.phys_outputs = Self::parse_phys_groups(&quads[26..31]);
119        self.phys_inputs = Self::parse_phys_groups(&quads[31..36]);
120        self.midi_outputs = quads[36] as usize;
121        self.midi_inputs = quads[37] as usize;
122        self.clk_rates = Self::parse_supported_clk_rates(quads[38], quads[39]);
123        self.dsp_version = quads[40];
124        self.arm_version = quads[41];
125        self.mixer_playbacks = quads[42] as usize;
126        self.mixer_captures = quads[43] as usize;
127        self.fpga_version = quads[44];
128
129        Ok(())
130    }
131
132    fn parse_caps(flags: u32) -> Vec<HwCap> {
133        (0..16)
134            .filter(|i| (1 << i) & flags > 0)
135            .map(|i| {
136                let mut cap = HwCap::default();
137                deserialize_hw_cap(&mut cap, i);
138                cap
139            })
140            .collect()
141    }
142
143    fn parse_text(quads: &[u32]) -> Result<String, Error> {
144        let mut literal = Vec::new();
145        quads.iter().for_each(|quad| {
146            literal.extend_from_slice(&quad.to_be_bytes());
147        });
148        if let Ok(text) = std::str::from_utf8(&literal) {
149            if let Some(pos) = text.find('\0') {
150                return Ok(text[0..pos].to_string());
151            }
152        }
153        Err(Error::new(FileError::Io, "Fail to parse string."))
154    }
155
156    fn parse_supported_clk_srcs(flags: u32) -> Vec<ClkSrc> {
157        (0..6)
158            .filter(|&i| (1 << i) & flags > 0)
159            .map(|i| {
160                let mut src = ClkSrc::default();
161                deserialize_clock_source(&mut src, i as u32);
162                src
163            })
164            .collect()
165    }
166
167    fn parse_supported_clk_rates(max: u32, min: u32) -> Vec<u32> {
168        [32000, 44100, 48000, 88200, 96000, 176400, 192000]
169            .iter()
170            .filter(|&r| *r >= min && *r <= max)
171            .copied()
172            .collect()
173    }
174
175    fn parse_phys_groups(quads: &[u32]) -> Vec<PhysGroupEntry> {
176        let count = quads[0] as usize;
177
178        let mut bytes = Vec::new();
179        quads[1..].iter().for_each(|quad| {
180            bytes.extend_from_slice(&quad.to_be_bytes());
181        });
182
183        (0..count)
184            .map(|i| {
185                let pos = i * 2;
186                let mut entry = PhysGroupEntry::default();
187                deserialize_phys_group_type(&mut entry.group_type, bytes[pos]);
188                entry.group_count = bytes[pos + 1] as usize;
189                entry
190            })
191            .collect()
192    }
193}
194
195const HWINFO_QUADS: usize = 65;
196
197impl<O, P> EfwWhollyCachableParamsOperation<P, HwInfo> for O
198where
199    O: EfwHardwareSpecification,
200    P: EfwProtocolExtManual,
201{
202    fn cache_wholly(proto: &mut P, states: &mut HwInfo, timeout_ms: u32) -> Result<(), Error> {
203        let mut params = vec![0; HWINFO_QUADS];
204        proto
205            .transaction(CATEGORY_HWINFO, CMD_HWINFO, &[], &mut params, timeout_ms)
206            .and_then(|_| states.parse(&params))
207    }
208}
209
210/// Hardware meter.
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct HwMeter {
213    pub detected_clk_srcs: Vec<(ClkSrc, bool)>,
214    pub detected_midi_inputs: [bool; 2],
215    pub detected_midi_outputs: [bool; 2],
216    pub guitar_charging: bool,
217    pub guitar_stereo_connect: bool,
218    pub guitar_hex_signal: bool,
219    pub phys_output_meters: Vec<i32>,
220    pub phys_input_meters: Vec<i32>,
221}
222
223impl Default for HwMeter {
224    fn default() -> Self {
225        Self {
226            detected_clk_srcs: Vec::new(),
227            detected_midi_inputs: [false; 2],
228            detected_midi_outputs: [false; 2],
229            guitar_charging: false,
230            guitar_stereo_connect: false,
231            guitar_hex_signal: false,
232            phys_output_meters: Vec::new(),
233            phys_input_meters: Vec::new(),
234        }
235    }
236}
237
238impl HwMeter {
239    /// The constructor for structure expressing hardware meter.
240    pub fn new(clk_srcs: &[ClkSrc], phys_inputs: usize, phys_outputs: usize) -> Self {
241        let mut meter = Self::default();
242
243        meter.detected_clk_srcs = clk_srcs.iter().map(|&src| (src, false)).collect();
244        meter.phys_output_meters = vec![0; phys_outputs];
245        meter.phys_input_meters = vec![0; phys_inputs];
246
247        meter
248    }
249
250    fn parse(&mut self, quads: &[u32]) {
251        let flags = quads[0];
252
253        self.detected_clk_srcs
254            .iter_mut()
255            .for_each(|(src, detected)| {
256                let pos = serialize_clock_source(src);
257                *detected = (1 << pos) & flags > 0
258            });
259
260        // I note that quads[1..4] is reserved space and it stores previous set command for FPGA device.
261
262        self.detected_midi_inputs
263            .iter_mut()
264            .enumerate()
265            .for_each(|(i, detected)| *detected = (1 << (8 + i)) & flags > 0);
266        self.detected_midi_outputs
267            .iter_mut()
268            .enumerate()
269            .for_each(|(i, detected)| *detected = (1 << (8 + i)) & flags > 0);
270
271        self.guitar_charging = (1 << 29) & flags > 0;
272        self.guitar_stereo_connect = (1 << 30) & flags > 0;
273        self.guitar_hex_signal = (1 << 31) & flags > 0;
274
275        let phys_outputs = quads[5] as usize;
276        let phys_inputs = quads[6] as usize;
277        self.phys_output_meters
278            .iter_mut()
279            .take(phys_outputs)
280            .enumerate()
281            .for_each(|(i, val)| *val = (quads[9 + i] >> 8) as i32);
282        self.phys_input_meters
283            .iter_mut()
284            .take(phys_inputs)
285            .enumerate()
286            .for_each(|(i, val)| *val = (quads[9 + i + phys_outputs] >> 8) as i32);
287    }
288}
289
290const METER_QUADS: usize = 110;
291
292impl<O, P> EfwWhollyCachableParamsOperation<P, HwMeter> for O
293where
294    O: EfwHardwareSpecification,
295    P: EfwProtocolExtManual,
296{
297    fn cache_wholly(proto: &mut P, states: &mut HwMeter, timeout_ms: u32) -> Result<(), Error> {
298        let mut params = vec![0; METER_QUADS];
299        proto
300            .transaction(CATEGORY_HWINFO, CMD_METER, &[], &mut params, timeout_ms)
301            .map(|_| states.parse(&params))
302    }
303}
304
305/// The parameter of response address.
306#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
307pub struct EfwRespAddr(u64);
308
309impl<O, P> EfwWhollyUpdatableParamsOperation<P, EfwRespAddr> for O
310where
311    O: EfwHardwareSpecification,
312    P: EfwProtocolExtManual,
313{
314    fn update_wholly(proto: &mut P, states: &EfwRespAddr, timeout_ms: u32) -> Result<(), Error> {
315        let args = [(states.0 >> 32) as u32, (states.0 & 0xffffffff) as u32];
316        let mut params = Vec::new();
317        proto.transaction(
318            CATEGORY_HWINFO,
319            CMD_CHANGE_RESP_ADDR,
320            &args,
321            &mut params,
322            timeout_ms,
323        )
324    }
325}
326
327/// The parameter of session block.
328#[derive(Default, Debug, Clone, PartialEq, Eq)]
329pub struct EfwSessionBlock {
330    /// The offset in session block.
331    pub offset: u32,
332    /// The content of session block.
333    pub data: Vec<u32>,
334}
335
336impl<O, P> EfwWhollyCachableParamsOperation<P, EfwSessionBlock> for O
337where
338    O: EfwHardwareSpecification,
339    P: EfwProtocolExtManual,
340{
341    fn cache_wholly(
342        proto: &mut P,
343        states: &mut EfwSessionBlock,
344        timeout_ms: u32,
345    ) -> Result<(), Error> {
346        assert_eq!(states.offset % 4, 0);
347
348        // The first argument should be quadlet count.
349        let args = [states.offset / 4, states.data.len() as u32];
350        let mut params = vec![0; 2 + states.data.len()];
351        proto
352            .transaction(
353                CATEGORY_HWINFO,
354                CMD_READ_SESSION_BLOCK,
355                &args,
356                &mut params,
357                timeout_ms,
358            )
359            .map(|_| states.data.copy_from_slice(&params[2..]))
360    }
361}