firewire_dice_protocols/tcat/extension/
cmd_section.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2020 Takashi Sakamoto
3
4//! Command section in protocol extension defined by TCAT for ASICs of DICE.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for command
7//! section in protocol extension defined by TCAT for ASICs of DICE.
8use super::{caps_section::*, *};
9
10/// Mode of sampling transfer frequency.
11#[derive(Debug, Copy, Clone, PartialEq, Eq)]
12pub enum RateMode {
13    /// up to 48.0 kHz.
14    Low,
15    /// up to 96.0 kHz.
16    Middle,
17    /// up to 192.0 kHz.
18    High,
19}
20
21impl Default for RateMode {
22    fn default() -> Self {
23        Self::Low
24    }
25}
26
27impl RateMode {
28    const LOW_FLAG: u32 = 0x00010000;
29    const MIDDLE_FLAG: u32 = 0x00020000;
30    const HIGH_FLAG: u32 = 0x00040000;
31
32    /// Conversion from sampling transfer frequency.
33    pub fn from_sampling_transfer_frequency(freq: u32) -> Self {
34        match freq {
35            0..=48000 => Self::Low,
36            48001..=96000 => Self::Middle,
37            96001.. => Self::High,
38        }
39    }
40
41    /// Conversion from clock rate.
42    pub fn from_clock_rate(rate: ClockRate) -> Self {
43        match rate {
44            ClockRate::R32000
45            | ClockRate::R44100
46            | ClockRate::R48000
47            | ClockRate::AnyLow
48            | ClockRate::None
49            | ClockRate::Reserved(_) => RateMode::Low,
50            ClockRate::R88200 | ClockRate::R96000 | ClockRate::AnyMid => RateMode::Middle,
51            ClockRate::R176400 | ClockRate::R192000 | ClockRate::AnyHigh => RateMode::High,
52        }
53    }
54}
55
56/// Operation code of command.
57#[derive(Debug, Copy, Clone, PartialEq, Eq)]
58pub enum Opcode {
59    /// No operation.
60    NoOp,
61    /// Load router configuration to router section at given rate.
62    LoadRouter(RateMode),
63    /// Load stream format configuration to stream format section at given rate.
64    LoadStreamConfig(RateMode),
65    /// Load both router and stream format configurations at given rate.
66    LoadRouterStreamConfig(RateMode),
67    /// Load all configurations from on-board flash memory.
68    LoadConfigFromFlash,
69    /// Store all configurations to on-board flash memory.
70    StoreConfigToFlash,
71}
72
73impl Opcode {
74    const NOOP_VALUE: u16 = 0x0000;
75    const LOAD_ROUTER_VALUE: u16 = 0x0001;
76    const LOAD_STREAM_CONFIG_VALUE: u16 = 0x0002;
77    const LOAD_ROUTER_STREAM_CONFIG_VALUE: u16 = 0x0003;
78    const LOAD_FLASH_CONFIG_VALUE: u16 = 0x0004;
79    const STORE_FLASH_CONFIG_VALUE: u16 = 0x0005;
80}
81
82const EXECUTE_FLAG: u32 = 0x80000000;
83
84fn serialize_opcode(code: &Opcode, raw: &mut [u8]) {
85    assert!(raw.len() >= 4);
86
87    let mut val = match code {
88        Opcode::NoOp => Opcode::NOOP_VALUE as u32,
89        Opcode::LoadRouter(rate_mode)
90        | Opcode::LoadStreamConfig(rate_mode)
91        | Opcode::LoadRouterStreamConfig(rate_mode) => {
92            let val = match code {
93                Opcode::LoadRouter(_) => Opcode::LOAD_ROUTER_VALUE,
94                Opcode::LoadStreamConfig(_) => Opcode::LOAD_STREAM_CONFIG_VALUE,
95                Opcode::LoadRouterStreamConfig(_) => Opcode::LOAD_ROUTER_STREAM_CONFIG_VALUE,
96                _ => unreachable!(),
97            } as u32;
98
99            let flag = match rate_mode {
100                RateMode::Low => RateMode::LOW_FLAG,
101                RateMode::Middle => RateMode::MIDDLE_FLAG,
102                RateMode::High => RateMode::HIGH_FLAG,
103            };
104            flag | val
105        }
106        Opcode::LoadConfigFromFlash => Opcode::LOAD_FLASH_CONFIG_VALUE as u32,
107        Opcode::StoreConfigToFlash => Opcode::STORE_FLASH_CONFIG_VALUE as u32,
108    };
109
110    val |= EXECUTE_FLAG;
111
112    serialize_u32(&val, raw);
113}
114
115const OPCODE_OFFSET: usize = 0x00;
116const RETURN_OFFSET: usize = 0x04;
117
118/// Operation in command section of TCAT protocol extension.
119pub trait TcatExtensionCommandSectionOperation: TcatExtensionOperation {
120    /// Initiate command and wait for its completion.
121    fn initiate(
122        req: &FwReq,
123        node: &FwNode,
124        sections: &ExtensionSections,
125        caps: &ExtensionCaps,
126        opcode: Opcode,
127        timeout_ms: u32,
128    ) -> Result<u32, Error> {
129        if let Opcode::LoadRouter(_) = opcode {
130            if caps.mixer.is_readonly {
131                Err(Error::new(
132                    ProtocolExtensionError::Cmd,
133                    "Router configuration is immutable",
134                ))?
135            }
136        } else if let Opcode::LoadStreamConfig(_) = opcode {
137            if !caps.general.dynamic_stream_format {
138                Err(Error::new(
139                    ProtocolExtensionError::Cmd,
140                    "Stream format configuration is immutable",
141                ))?
142            }
143        } else if let Opcode::LoadRouterStreamConfig(_) = opcode {
144            if caps.mixer.is_readonly && !caps.general.dynamic_stream_format {
145                Err(Error::new(
146                    ProtocolExtensionError::Cmd,
147                    "Any configuration is immutable",
148                ))?
149            }
150        } else if opcode == Opcode::LoadConfigFromFlash {
151            if !caps.general.storage_avail {
152                Err(Error::new(
153                    ProtocolExtensionError::Cmd,
154                    "Storage is not available",
155                ))?
156            }
157        } else if opcode == Opcode::StoreConfigToFlash {
158            if !caps.general.storage_avail {
159                Err(Error::new(
160                    ProtocolExtensionError::Cmd,
161                    "Storage is not available",
162                ))?
163            }
164        }
165
166        let mut raw = [0; 4];
167        serialize_opcode(&opcode, &mut raw);
168        Self::write_extension(
169            req,
170            node,
171            &sections.cmd,
172            OPCODE_OFFSET,
173            &mut raw,
174            timeout_ms,
175        )?;
176
177        let mut count = 0;
178        while count < 10 {
179            std::thread::sleep(std::time::Duration::from_millis(50));
180
181            Self::read_extension(
182                req,
183                node,
184                &sections.cmd,
185                OPCODE_OFFSET,
186                &mut raw,
187                timeout_ms,
188            )
189            .map_err(|e| Error::new(ProtocolExtensionError::Cmd, &e.to_string()))?;
190
191            let mut val = 0u32;
192            deserialize_u32(&mut val, &raw);
193
194            if val & EXECUTE_FLAG == 0 {
195                Self::read_extension(
196                    req,
197                    node,
198                    &sections.cmd,
199                    RETURN_OFFSET,
200                    &mut raw,
201                    timeout_ms,
202                )
203                .map_err(|e| Error::new(ProtocolExtensionError::Cmd, &e.to_string()))?;
204                return Ok(u32::from_be_bytes(raw));
205            }
206            count += 1;
207        }
208
209        Err(Error::new(
210            ProtocolExtensionError::Cmd,
211            "Operation timeout.",
212        ))
213    }
214}
215
216impl<O: TcatExtensionOperation> TcatExtensionCommandSectionOperation for O {}