1#![doc = include_str!("../README.md")]
5
6use glib::{Error, FileError};
7
8use hinawa::{prelude::FwReqExtManual, FwNode, FwReq, FwTcode};
9
10#[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#[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
71pub 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
81pub 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
91pub 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
102pub 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#[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#[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#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
150pub struct Dg00xSamplingClockParameters {
151 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(¶ms.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#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
203pub struct Dg00xMediaClockParameters {
204 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(¶ms.rate);
263 write_quadlet(req, node, SAMPLING_CLOCK_SOURCE_OFFSET, val, timeout_ms)
264 }
265}
266
267#[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#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
314pub struct Dg00xExternalClockParameters {
315 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#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
356pub struct Dg00xMonitorState {
357 pub enabled: bool,
359 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}