firewire_digi00x_protocols/
lib.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4#![doc = include_str!("../README.md")]
5
6use glib::{Error, FileError};
7
8use hinawa::{prelude::FwReqExtManual, FwNode, FwReq, FwTcode};
9
10/// The protocol implementation for Digi 002.
11#[derive(Default, Debug)]
12pub struct Digi002Protocol;
13
14impl Dg00xHardwareSpecification for Digi002Protocol {
15    const SAMPLING_CLOCK_SOURCES: &'static [ClockSource] =
16        &[ClockSource::Internal, ClockSource::Spdif, ClockSource::Adat];
17}
18
19/// The protocol implementation for Digi 003.
20#[derive(Default, Debug)]
21pub struct Digi003Protocol;
22
23impl Dg00xHardwareSpecification for Digi003Protocol {
24    const SAMPLING_CLOCK_SOURCES: &'static [ClockSource] = &[
25        ClockSource::Internal,
26        ClockSource::Spdif,
27        ClockSource::Adat,
28        ClockSource::WordClock,
29    ];
30}
31
32const BASE_OFFSET: u64 = 0xffffe0000000;
33
34fn read_quadlet(
35    req: &mut FwReq,
36    node: &mut FwNode,
37    offset: u64,
38    timeout_ms: u32,
39) -> Result<u32, Error> {
40    let mut quadlet = [0; 4];
41    req.transaction_sync(
42        node,
43        FwTcode::ReadQuadletRequest,
44        BASE_OFFSET + offset,
45        quadlet.len(),
46        &mut quadlet,
47        timeout_ms,
48    )
49    .map(|_| u32::from_be_bytes(quadlet))
50}
51
52fn write_quadlet(
53    req: &mut FwReq,
54    node: &mut FwNode,
55    offset: u64,
56    val: u32,
57    timeout_ms: u32,
58) -> Result<(), Error> {
59    let mut quadlet = [0; 4];
60    quadlet.copy_from_slice(&val.to_be_bytes());
61    req.transaction_sync(
62        node,
63        FwTcode::WriteQuadletRequest,
64        BASE_OFFSET + offset,
65        quadlet.len(),
66        &mut quadlet,
67        timeout_ms,
68    )
69}
70
71/// The specification of hardware.
72pub trait Dg00xHardwareSpecification {
73    const SAMPLING_CLOCK_SOURCES: &'static [ClockSource];
74    const SAMPLING_CLOCK_RATES: &'static [u32] = &[44100, 48000, 88200, 96000];
75
76    const MONITOR_SOURCE_GAIN_MIN: u8 = 0;
77    const MONITOR_SOURCE_GAIN_MAX: u8 = 0x80;
78    const MONITOR_SOURCE_GAIN_STEP: u8 = 1;
79}
80
81/// Cache whole parameters.
82pub trait Dg00xWhollyCachableParamsOperation<T>: Dg00xHardwareSpecification {
83    fn cache_wholly(
84        req: &mut FwReq,
85        node: &mut FwNode,
86        states: &mut T,
87        timeout_ms: u32,
88    ) -> Result<(), Error>;
89}
90
91/// Update the part of parameters.
92pub trait Dg00xPartiallyUpdatableParamsOperation<T>: Dg00xHardwareSpecification {
93    fn update_partially(
94        req: &mut FwReq,
95        node: &mut FwNode,
96        params: &mut T,
97        update: T,
98        timeout_ms: u32,
99    ) -> Result<(), Error>;
100}
101
102/// Update whole parameters.
103pub trait Dg00xWhollyUpdatableParamsOperation<T>: Dg00xHardwareSpecification {
104    fn update_wholly(
105        req: &mut FwReq,
106        node: &mut FwNode,
107        states: &T,
108        timeout_ms: u32,
109    ) -> Result<(), Error>;
110}
111
112/// Nominal frequency of media clock.
113#[derive(Debug, Copy, Clone, Eq, PartialEq)]
114pub enum ClockRate {
115    R44100,
116    R48000,
117    R88200,
118    R96000,
119}
120
121impl Default for ClockRate {
122    fn default() -> Self {
123        Self::R44100
124    }
125}
126
127/// Signal source of sampling clock.
128#[derive(Debug, Copy, Clone, Eq, PartialEq)]
129pub enum ClockSource {
130    Internal,
131    Spdif,
132    Adat,
133    WordClock,
134}
135
136impl Default for ClockSource {
137    fn default() -> Self {
138        Self::Internal
139    }
140}
141
142const SAMPLING_CLOCK_SOURCE_OFFSET: u64 = 0x0118;
143const MEDIA_CLOCK_RATE_OFFSET: u64 = 0x0110;
144const EXTERNAL_CLOCK_RATE_OFFSET: u64 = 0x0114;
145const OPTICAL_INTERFACE_MODE_OFFSET: u64 = 0x011c;
146const EXTERNAL_CLOCK_SOURCE_DETECTION_OFFSET: u64 = 0x012c;
147
148/// The parameters for sampling clock.
149#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
150pub struct Dg00xSamplingClockParameters {
151    /// The source.
152    pub source: ClockSource,
153}
154
155impl<O> Dg00xWhollyCachableParamsOperation<Dg00xSamplingClockParameters> for O
156where
157    O: Dg00xHardwareSpecification,
158{
159    fn cache_wholly(
160        req: &mut FwReq,
161        node: &mut FwNode,
162        states: &mut Dg00xSamplingClockParameters,
163        timeout_ms: u32,
164    ) -> Result<(), Error> {
165        read_quadlet(req, node, SAMPLING_CLOCK_SOURCE_OFFSET, timeout_ms).and_then(|val| {
166            let pos = val as usize;
167            Self::SAMPLING_CLOCK_SOURCES
168                .iter()
169                .nth(pos)
170                .ok_or_else(|| {
171                    let msg = format!("Unexpected clock source: {}", pos);
172                    Error::new(FileError::Io, &msg)
173                })
174                .map(|&s| states.source = s)
175        })
176    }
177}
178
179impl<O> Dg00xWhollyUpdatableParamsOperation<Dg00xSamplingClockParameters> for O
180where
181    O: Dg00xHardwareSpecification,
182{
183    fn update_wholly(
184        req: &mut FwReq,
185        node: &mut FwNode,
186        params: &Dg00xSamplingClockParameters,
187        timeout_ms: u32,
188    ) -> Result<(), Error> {
189        let pos = Self::SAMPLING_CLOCK_SOURCES
190            .iter()
191            .position(|&s| s.eq(&params.source))
192            .ok_or_else(|| {
193                let msg = format!("Invalid argument for clock source: {:?}", params.source);
194                Error::new(FileError::Inval, &msg)
195            })?;
196        let val = pos as u32;
197        write_quadlet(req, node, SAMPLING_CLOCK_SOURCE_OFFSET, val, timeout_ms)
198    }
199}
200
201/// The parameters for media clock.
202#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
203pub struct Dg00xMediaClockParameters {
204    /// The rate.
205    pub rate: ClockRate,
206}
207
208const CLOCK_RATE_TABLE: &[ClockRate] = &[
209    ClockRate::R44100,
210    ClockRate::R48000,
211    ClockRate::R88200,
212    ClockRate::R96000,
213];
214
215fn serialize_clock_rate(rate: &ClockRate) -> u32 {
216    CLOCK_RATE_TABLE
217        .iter()
218        .position(|r| rate.eq(r))
219        .map(|pos| pos as u32)
220        .unwrap()
221}
222
223fn deserialize_clock_rate(rate: &mut ClockRate, val: u32) -> Result<(), Error> {
224    *rate = match val {
225        0 => Ok(ClockRate::R44100),
226        1 => Ok(ClockRate::R48000),
227        2 => Ok(ClockRate::R88200),
228        3 => Ok(ClockRate::R96000),
229        _ => {
230            let msg = format!("Unexpected value for clock rate: {}", val);
231            Err(Error::new(FileError::Inval, &msg))
232        }
233    }?;
234    Ok(())
235}
236
237impl<O> Dg00xWhollyCachableParamsOperation<Dg00xMediaClockParameters> for O
238where
239    O: Dg00xHardwareSpecification,
240{
241    fn cache_wholly(
242        req: &mut FwReq,
243        node: &mut FwNode,
244        states: &mut Dg00xMediaClockParameters,
245        timeout_ms: u32,
246    ) -> Result<(), Error> {
247        read_quadlet(req, node, MEDIA_CLOCK_RATE_OFFSET, timeout_ms)
248            .and_then(|val| deserialize_clock_rate(&mut states.rate, val))
249    }
250}
251
252impl<O> Dg00xWhollyUpdatableParamsOperation<Dg00xMediaClockParameters> for O
253where
254    O: Dg00xHardwareSpecification,
255{
256    fn update_wholly(
257        req: &mut FwReq,
258        node: &mut FwNode,
259        params: &Dg00xMediaClockParameters,
260        timeout_ms: u32,
261    ) -> Result<(), Error> {
262        let val = serialize_clock_rate(&params.rate);
263        write_quadlet(req, node, SAMPLING_CLOCK_SOURCE_OFFSET, val, timeout_ms)
264    }
265}
266
267/// Mode of optical interface.
268#[derive(Debug, Copy, Clone, PartialEq, Eq)]
269pub enum OpticalInterfaceMode {
270    Adat,
271    Spdif,
272}
273
274impl Default for OpticalInterfaceMode {
275    fn default() -> Self {
276        Self::Adat
277    }
278}
279
280impl Dg00xWhollyCachableParamsOperation<OpticalInterfaceMode> for Digi003Protocol {
281    fn cache_wholly(
282        req: &mut FwReq,
283        node: &mut FwNode,
284        states: &mut OpticalInterfaceMode,
285        timeout_ms: u32,
286    ) -> Result<(), Error> {
287        read_quadlet(req, node, OPTICAL_INTERFACE_MODE_OFFSET, timeout_ms).map(|val| {
288            *states = if val > 0 {
289                OpticalInterfaceMode::Spdif
290            } else {
291                OpticalInterfaceMode::Adat
292            };
293        })
294    }
295}
296
297impl Dg00xWhollyUpdatableParamsOperation<OpticalInterfaceMode> for Digi003Protocol {
298    fn update_wholly(
299        req: &mut FwReq,
300        node: &mut FwNode,
301        params: &OpticalInterfaceMode,
302        timeout_ms: u32,
303    ) -> Result<(), Error> {
304        let val = match params {
305            OpticalInterfaceMode::Adat => 0,
306            OpticalInterfaceMode::Spdif => 1,
307        };
308        write_quadlet(req, node, OPTICAL_INTERFACE_MODE_OFFSET, val, timeout_ms)
309    }
310}
311
312/// The parameters for media clock.
313#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
314pub struct Dg00xExternalClockParameters {
315    /// The rate detected in input of external source. Once the source of sampling clock is
316    /// configured to any external source, the function can return detected frequency. Once losing
317    /// the input, it returns None.
318    pub rate: Option<ClockRate>,
319}
320
321impl<O> Dg00xWhollyCachableParamsOperation<Dg00xExternalClockParameters> for O
322where
323    O: Dg00xHardwareSpecification,
324{
325    fn cache_wholly(
326        req: &mut FwReq,
327        node: &mut FwNode,
328        states: &mut Dg00xExternalClockParameters,
329        timeout_ms: u32,
330    ) -> Result<(), Error> {
331        let detected = read_quadlet(
332            req,
333            node,
334            EXTERNAL_CLOCK_SOURCE_DETECTION_OFFSET,
335            timeout_ms,
336        )?;
337
338        if detected > 0 {
339            let val = read_quadlet(req, node, EXTERNAL_CLOCK_RATE_OFFSET, timeout_ms)?;
340            let mut rate = ClockRate::default();
341            deserialize_clock_rate(&mut rate, val).map(|_| states.rate = Some(rate))
342        } else {
343            states.rate = None;
344            Ok(())
345        }
346    }
347}
348
349const MONITOR_DST_COUNT: usize = 2;
350const MONITOR_SRC_COUNT: usize = 18;
351
352/// State of monitor. At offline mode (no packet streaming runs), the monitor function is disabled
353/// and is not configurable. When packet streaming starts, the monitor function becomes
354/// configurable with reset state.
355#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
356pub struct Dg00xMonitorState {
357    /// Whether to enable monitor mixer or not.
358    pub enabled: bool,
359    /// The gain of monitor inputs. The value is between 0x00 and 0x80 for -48.0 and 0.0 dB.
360    pub src_gains: [[u8; MONITOR_SRC_COUNT]; MONITOR_DST_COUNT],
361}
362
363const ENABLE_OFFSET: u64 = 0x0124;
364const MONITOR_SRC_GAIN_OFFSET: u64 = 0x0300;
365
366const DST_STEP: usize = 4;
367const SRC_STEP: usize = 8;
368
369fn compute_source_offset(dst: usize, src: usize) -> u64 {
370    (dst * DST_STEP + src * SRC_STEP) as u64
371}
372
373impl<O> Dg00xWhollyCachableParamsOperation<Dg00xMonitorState> for O
374where
375    O: Dg00xHardwareSpecification,
376{
377    fn cache_wholly(
378        req: &mut FwReq,
379        node: &mut FwNode,
380        states: &mut Dg00xMonitorState,
381        timeout_ms: u32,
382    ) -> Result<(), Error> {
383        read_quadlet(req, node, ENABLE_OFFSET, timeout_ms).map(|val| states.enabled = val > 0)?;
384        states
385            .src_gains
386            .iter_mut()
387            .enumerate()
388            .try_for_each(|(dst, gains)| {
389                gains.iter_mut().enumerate().try_for_each(|(src, gain)| {
390                    let offset = MONITOR_SRC_GAIN_OFFSET + compute_source_offset(dst, src);
391                    read_quadlet(req, node, offset, timeout_ms).map(|val| *gain = (val >> 24) as u8)
392                })
393            })
394    }
395}
396
397impl<O> Dg00xWhollyUpdatableParamsOperation<Dg00xMonitorState> for O
398where
399    O: Dg00xHardwareSpecification,
400{
401    fn update_wholly(
402        req: &mut FwReq,
403        node: &mut FwNode,
404        states: &Dg00xMonitorState,
405        timeout_ms: u32,
406    ) -> Result<(), Error> {
407        write_quadlet(req, node, ENABLE_OFFSET, states.enabled as u32, timeout_ms)?;
408        states
409            .src_gains
410            .iter()
411            .enumerate()
412            .try_for_each(|(dst, gains)| {
413                gains.iter().enumerate().try_for_each(|(src, &gain)| {
414                    let offset = MONITOR_SRC_GAIN_OFFSET + compute_source_offset(dst, src);
415                    let val = (gain as u32) << 24;
416                    write_quadlet(req, node, offset, val, timeout_ms)
417                })
418            })
419    }
420}
421
422impl<O> Dg00xPartiallyUpdatableParamsOperation<Dg00xMonitorState> for O
423where
424    O: Dg00xHardwareSpecification,
425{
426    fn update_partially(
427        req: &mut FwReq,
428        node: &mut FwNode,
429        states: &mut Dg00xMonitorState,
430        updates: Dg00xMonitorState,
431        timeout_ms: u32,
432    ) -> Result<(), Error> {
433        if states.enabled != updates.enabled {
434            write_quadlet(req, node, ENABLE_OFFSET, updates.enabled as u32, timeout_ms)
435                .map(|_| states.enabled = updates.enabled)?;
436        }
437
438        states
439            .src_gains
440            .iter_mut()
441            .zip(updates.src_gains.iter())
442            .enumerate()
443            .try_for_each(|(dst, (state, update))| {
444                state
445                    .iter_mut()
446                    .zip(update.iter())
447                    .enumerate()
448                    .filter(|(_, (o, n))| !o.eq(n))
449                    .try_for_each(|(src, (g, &gain))| {
450                        let offset = MONITOR_SRC_GAIN_OFFSET + compute_source_offset(dst, src);
451                        let val = (gain as u32) << 24;
452                        write_quadlet(req, node, offset, val, timeout_ms).map(|_| *g = gain)
453                    })
454            })
455    }
456}