1use super::*;
7
8#[derive(Default, Debug)]
10pub struct Ff400Protocol;
11
12const MIXER_OFFSET: u64 = 0x000080080000;
13const OUTPUT_OFFSET: u64 = 0x000080080f80;
14const METER_OFFSET: u64 = 0x000080100000;
15const CFG_OFFSET: u64 = 0x000080100514;
16const STATUS_OFFSET: u64 = 0x0000801c0000;
17const AMP_OFFSET: u64 = 0x0000801c0180;
18
19#[allow(dead_code)]
21const LTC_STATUS_OFFSET: usize = 0x0000801f0000;
22
23const AMP_MIC_IN_CH_OFFSET: u8 = 0;
24const AMP_LINE_IN_CH_OFFSET: u8 = 2;
25const AMP_OUT_CH_OFFSET: u8 = 4;
26
27impl RmeFfFormerSpecification for Ff400Protocol {
28 const ANALOG_INPUT_COUNT: usize = 8;
29 const SPDIF_INPUT_COUNT: usize = 2;
30 const ADAT_INPUT_COUNT: usize = 8;
31 const STREAM_INPUT_COUNT: usize = 18;
32
33 const ANALOG_OUTPUT_COUNT: usize = 8;
34 const SPDIF_OUTPUT_COUNT: usize = 2;
35 const ADAT_OUTPUT_COUNT: usize = 8;
36}
37
38impl RmeFfFormerMeterSpecification for Ff400Protocol {
39 const METER_OFFSET: u64 = METER_OFFSET;
40}
41
42fn write_amp_cmd(
43 req: &mut FwReq,
44 node: &mut FwNode,
45 ch: u8,
46 level: i8,
47 timeout_ms: u32,
48) -> Result<(), Error> {
49 let cmd = ((ch as u32) << 16) | ((level as u32) & 0xff);
50 let mut raw = [0; 4];
51 raw.copy_from_slice(&cmd.to_le_bytes());
52 req.transaction_sync(
53 node,
54 FwTcode::WriteQuadletRequest,
55 AMP_OFFSET,
56 raw.len(),
57 &mut raw,
58 timeout_ms,
59 )
60}
61
62#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
64pub struct Ff400InputGainStatus {
65 pub mic: [i8; 2],
68 pub line: [i8; 2],
71}
72
73const KNOB_IS_SIGNAL_LEVEL_FLAG: u32 = 0x04000000;
74const KNOB_IS_STEREO_PAIRED_FLAG: u32 = 0x02000000;
75const KNOB_IS_RIGHT_CHANNEL_FLAG: u32 = 0x08000000;
76
77const MIDI_PORT_0_FLAG: u32 = 0x00000100;
78const MIDI_PORT_0_BYTE_MASK: u32 = 0x000000ff;
79const MIDI_PORT_0_BYTE_SHIFT: usize = 0;
80const MIDI_PORT_1_FLAG: u32 = 0x01000000;
81const MIDI_PORT_1_BYTE_MASK: u32 = 0x00ff0000;
82const MIDI_PORT_1_BYTE_SHIFT: usize = 16;
83
84const KNOB_TARGET_MASK: u32 = 0xf0000000;
85const KNOB_TARGET_MIC_INPUT_PAIR_0: u32 = 0x00000000;
86const KNOB_TARGET_LINE_INPUT_PAIR_1: u32 = 0x10000000;
87const KNOB_TARGET_LINE_OUTPUT_PAIR_0: u32 = 0x20000000;
88const KNOB_TARGET_LINE_OUTPUT_PAIR_1: u32 = 0x30000000;
89const KNOB_TARGET_LINE_OUTPUT_PAIR_2: u32 = 0x40000000;
90const KNOB_TARGET_HP_OUTPUT_PAIR: u32 = 0x50000000;
91const KNOB_TARGET_SPDIF_OUTPUT_PAIR: u32 = 0x60000000;
92const KNOB_TARGET_ADAT_OUTPUT_PAIR_0: u32 = 0x70000000;
93const KNOB_TARGET_ADAT_OUTPUT_PAIR_1: u32 = 0x80000000;
94const KNOB_TARGET_ADAT_OUTPUT_PAIR_2: u32 = 0x90000000;
95const KNOB_TARGET_ADAT_OUTPUT_PAIR_3: u32 = 0xa0000000;
96
97const KNOB_SIGNAL_LEVEL_MASK: u32 = 0x00fffc00;
98const KNOB_SIGNAL_LEVEL_SHIFT: u32 = 10;
99
100#[derive(Debug, Copy, Clone, PartialEq, Eq)]
102pub struct Ff400MidiMessage {
103 pub port: u8,
104 pub byte: u8,
105}
106
107impl Default for Ff400MidiMessage {
108 fn default() -> Self {
109 Self {
110 port: u8::MAX,
111 byte: u8::MAX,
112 }
113 }
114}
115
116pub trait Ff400MessageParse<T> {
118 fn parse_message(params: &mut T, message: u32) -> bool;
120}
121
122impl Ff400MessageParse<Ff400MidiMessage> for Ff400Protocol {
123 fn parse_message(params: &mut Ff400MidiMessage, message: u32) -> bool {
124 if message & KNOB_IS_SIGNAL_LEVEL_FLAG == 0 {
125 [
126 (
127 MIDI_PORT_0_FLAG,
128 MIDI_PORT_0_BYTE_MASK,
129 MIDI_PORT_0_BYTE_SHIFT,
130 ),
131 (
132 MIDI_PORT_1_FLAG,
133 MIDI_PORT_1_BYTE_MASK,
134 MIDI_PORT_1_BYTE_SHIFT,
135 ),
136 ]
137 .iter()
138 .enumerate()
139 .find(|(_, (flag, _, _))| message & flag > 0)
140 .map_or(false, |(p, (_, mask, shift))| {
141 params.port = p as u8;
142 params.byte = ((message & mask) >> shift) as u8;
143 true
144 })
145 } else {
146 false
147 }
148 }
149}
150
151impl Ff400MessageParse<Ff400InputGainStatus> for Ff400Protocol {
152 fn parse_message(params: &mut Ff400InputGainStatus, message: u32) -> bool {
153 if message & KNOB_IS_SIGNAL_LEVEL_FLAG > 0 {
154 let target = message & KNOB_TARGET_MASK;
155 [
156 (&mut params.mic[..], KNOB_TARGET_MIC_INPUT_PAIR_0),
157 (&mut params.line[..], KNOB_TARGET_LINE_INPUT_PAIR_1),
158 ]
159 .iter_mut()
160 .find(|(_, t)| target.eq(t))
161 .map_or(false, |(gains, _)| {
162 let val = ((message & KNOB_SIGNAL_LEVEL_MASK) >> KNOB_SIGNAL_LEVEL_SHIFT) as i8;
163 if message & KNOB_IS_STEREO_PAIRED_FLAG > 0 {
164 gains.fill(val);
165 } else {
166 let ch = if message & KNOB_IS_RIGHT_CHANNEL_FLAG > 0 {
167 1
168 } else {
169 0
170 };
171 gains[ch] = val;
172 }
173 true
174 })
175 } else {
176 false
177 }
178 }
179}
180
181impl Ff400MessageParse<FormerOutputVolumeState> for Ff400Protocol {
182 fn parse_message(params: &mut FormerOutputVolumeState, message: u32) -> bool {
183 assert_eq!(
184 params.0.len(),
185 Self::ANALOG_OUTPUT_COUNT + Self::SPDIF_OUTPUT_COUNT + Self::ADAT_OUTPUT_COUNT
186 );
187
188 if message & KNOB_IS_SIGNAL_LEVEL_FLAG > 0 {
189 let target = message & KNOB_TARGET_MASK;
190
191 [
192 KNOB_TARGET_LINE_OUTPUT_PAIR_0,
193 KNOB_TARGET_LINE_OUTPUT_PAIR_1,
194 KNOB_TARGET_LINE_OUTPUT_PAIR_2,
195 KNOB_TARGET_HP_OUTPUT_PAIR,
196 KNOB_TARGET_SPDIF_OUTPUT_PAIR,
197 KNOB_TARGET_ADAT_OUTPUT_PAIR_0,
198 KNOB_TARGET_ADAT_OUTPUT_PAIR_1,
199 KNOB_TARGET_ADAT_OUTPUT_PAIR_2,
200 KNOB_TARGET_ADAT_OUTPUT_PAIR_3,
201 ]
202 .iter()
203 .position(|t| target.eq(t))
204 .map_or(false, |pos| {
205 let val = ((message & KNOB_SIGNAL_LEVEL_MASK) >> KNOB_SIGNAL_LEVEL_SHIFT) as i8;
206 let vol = amp_to_vol_value(val);
207 let mut ch = pos * 2;
208
209 if message & KNOB_IS_STEREO_PAIRED_FLAG > 0 {
210 params.0[ch] = vol;
211 params.0[ch + 1] = vol;
212 } else {
213 if message & KNOB_IS_RIGHT_CHANNEL_FLAG > 0 {
214 ch += 1;
215 }
216 params.0[ch] = vol;
217 }
218 true
219 })
220 } else {
221 false
222 }
223 }
224}
225
226impl Ff400Protocol {
227 pub const MIC_INPUT_GAIN_MIN: i8 = 0;
229 pub const MIC_INPUT_GAIN_MAX: i8 = 65;
231 pub const MIC_INPUT_GAIN_STEP: i8 = 1;
233
234 pub const LINE_INPUT_GAIN_MIN: i8 = 0;
236 pub const LINE_INPUT_GAIN_MAX: i8 = 36;
238 pub const LINE_INPUT_GAIN_STEP: i8 = 1;
240}
241
242impl RmeFfWhollyUpdatableParamsOperation<Ff400InputGainStatus> for Ff400Protocol {
243 fn update_wholly(
244 req: &mut FwReq,
245 node: &mut FwNode,
246 params: &Ff400InputGainStatus,
247 timeout_ms: u32,
248 ) -> Result<(), Error> {
249 [
250 (¶ms.mic, AMP_MIC_IN_CH_OFFSET),
251 (¶ms.line, AMP_LINE_IN_CH_OFFSET),
252 ]
253 .iter()
254 .try_for_each(|(gains, offset)| {
255 gains.iter().enumerate().try_for_each(|(i, &gain)| {
256 write_amp_cmd(req, node, offset + i as u8, gain, timeout_ms)
257 })
258 })
259 }
260}
261
262impl RmeFfPartiallyUpdatableParamsOperation<Ff400InputGainStatus> for Ff400Protocol {
263 fn update_partially(
264 req: &mut FwReq,
265 node: &mut FwNode,
266 params: &mut Ff400InputGainStatus,
267 update: Ff400InputGainStatus,
268 timeout_ms: u32,
269 ) -> Result<(), Error> {
270 [
271 (&mut params.mic, update.mic, AMP_MIC_IN_CH_OFFSET),
272 (&mut params.line, update.line, AMP_LINE_IN_CH_OFFSET),
273 ]
274 .iter_mut()
275 .try_for_each(|(states, changes, offset)| {
276 states
277 .iter_mut()
278 .zip(changes.iter())
279 .enumerate()
280 .filter(|(_, (s, c))| !s.eq(c))
281 .try_for_each(|(i, (s, c))| {
282 write_amp_cmd(req, node, *offset + i as u8, *c, timeout_ms).map(|_| *s = *c)
283 })
284 })
285 }
286}
287
288const OUTPUT_AMP_MAX: i8 = 0x3f;
289
290fn vol_to_amp_value(vol: i32) -> i8 {
292 ((OUTPUT_AMP_MAX as u64) * ((Ff400Protocol::VOL_MAX - vol) as u64)
293 / (Ff400Protocol::VOL_MAX as u64)) as i8
294}
295
296fn amp_to_vol_value(amp: i8) -> i32 {
297 ((Ff400Protocol::VOL_MAX as u64) * ((OUTPUT_AMP_MAX - amp) as u64) / (OUTPUT_AMP_MAX as u64))
298 as i32
299}
300
301fn write_output_amp_cmd(
302 req: &mut FwReq,
303 node: &mut FwNode,
304 ch: usize,
305 raw: &[u8],
306 timeout_ms: u32,
307) -> Result<(), Error> {
308 assert_eq!(raw.len(), 4);
309
310 let mut quadlet = [0; 4];
311 quadlet.copy_from_slice(&raw);
312 let vol = i32::from_le_bytes(quadlet);
313
314 let val = vol_to_amp_value(vol);
315 let amp_offset = AMP_OUT_CH_OFFSET + ch as u8;
316 write_amp_cmd(req, node, amp_offset, val, timeout_ms)
317}
318
319impl RmeFfWhollyUpdatableParamsOperation<FormerOutputVolumeState> for Ff400Protocol {
320 fn update_wholly(
321 req: &mut FwReq,
322 node: &mut FwNode,
323 params: &FormerOutputVolumeState,
324 timeout_ms: u32,
325 ) -> Result<(), Error> {
326 let mut raw = Self::serialize_offsets(params);
327 req.transaction_sync(
328 node,
329 FwTcode::WriteBlockRequest,
330 OUTPUT_OFFSET,
331 raw.len(),
332 &mut raw,
333 timeout_ms,
334 )?;
335
336 (0..Self::PHYS_OUTPUT_COUNT).try_for_each(|i| {
337 let pos = i * 4;
338 write_output_amp_cmd(req, node, i, &raw[pos..(pos + 4)], timeout_ms)
339 })
340 }
341}
342
343impl RmeFfPartiallyUpdatableParamsOperation<FormerOutputVolumeState> for Ff400Protocol {
344 fn update_partially(
345 req: &mut FwReq,
346 node: &mut FwNode,
347 params: &mut FormerOutputVolumeState,
348 update: FormerOutputVolumeState,
349 timeout_ms: u32,
350 ) -> Result<(), Error> {
351 let old = Self::serialize_offsets(params);
352 let mut new = Self::serialize_offsets(&update);
353
354 (0..(new.len() / 4))
355 .try_for_each(|i| {
356 let pos = i * 4;
357 if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
358 req.transaction_sync(
359 node,
360 FwTcode::WriteBlockRequest,
361 OUTPUT_OFFSET + pos as u64,
362 4,
363 &mut new[pos..(pos + 4)],
364 timeout_ms,
365 )
366 .and_then(|_| {
367 write_output_amp_cmd(req, node, i, &new[pos..(pos + 4)], timeout_ms)
368 })
369 } else {
370 Ok(())
371 }
372 })
373 .map(|_| *params = update)
374 }
375}
376
377impl RmeFormerMixerSpecification for Ff400Protocol {
378 const MIXER_OFFSET: u64 = MIXER_OFFSET;
379 const AVAIL_COUNT: usize = 18;
380}
381
382#[derive(Debug, Copy, Clone, PartialEq, Eq)]
384pub enum Ff400ClkSrc {
385 Internal,
386 WordClock,
387 Adat,
388 Spdif,
389 Ltc,
390}
391
392impl Default for Ff400ClkSrc {
393 fn default() -> Self {
394 Self::Internal
395 }
396}
397
398const Q0_SYNC_WORD_CLOCK_MASK: u32 = 0x40000000;
400const Q0_LOCK_WORD_CLOCK_MASK: u32 = 0x20000000;
401const Q0_EXT_CLK_RATE_MASK: u32 = 0x1e000000;
402const Q0_EXT_CLK_RATE_192000_FLAG: u32 = 0x12000000;
403const Q0_EXT_CLK_RATE_176400_FLAG: u32 = 0x10000000;
404const Q0_EXT_CLK_RATE_128000_FLAG: u32 = 0x0c000000;
405const Q0_EXT_CLK_RATE_96000_FLAG: u32 = 0x0e000000;
406const Q0_EXT_CLK_RATE_88200_FLAG: u32 = 0x0a000000;
407const Q0_EXT_CLK_RATE_64000_FLAG: u32 = 0x08000000;
408const Q0_EXT_CLK_RATE_48000_FLAG: u32 = 0x06000000;
409const Q0_EXT_CLK_RATE_44100_FLAG: u32 = 0x04000000;
410const Q0_EXT_CLK_RATE_32000_FLAG: u32 = 0x02000000;
411const Q0_ACTIVE_CLK_SRC_MASK: u32 = 0x01c00000;
412const Q0_ACTIVE_CLK_SRC_INTERNAL_FLAG: u32 = 0x01c00000;
413const Q0_ACTIVE_CLK_SRC_LTC_FLAG: u32 = 0x01400000;
414const Q0_ACTIVE_CLK_SRC_WORD_CLK_FLAG: u32 = 0x01000000;
415const Q0_ACTIVE_CLK_SRC_SPDIF_FLAG: u32 = 0x00c00000;
416const Q0_ACTIVE_CLK_SRC_ADAT_FLAG: u32 = 0x00000000;
417const Q0_SYNC_SPDIF_MASK: u32 = 0x00100000;
418const Q0_LOCK_SPDIF_MASK: u32 = 0x00040000;
419const Q0_SPDIF_RATE_MASK: u32 = 0x0003c000;
420const Q0_SPDIF_RATE_192000_FLAG: u32 = 0x00024000;
421const Q0_SPDIF_RATE_176400_FLAG: u32 = 0x00020000;
422const Q0_SPDIF_RATE_128000_FLAG: u32 = 0x0001c000;
423const Q0_SPDIF_RATE_96000_FLAG: u32 = 0x00018000;
424const Q0_SPDIF_RATE_88200_FLAG: u32 = 0x00014000;
425const Q0_SPDIF_RATE_64000_FLAG: u32 = 0x00010000;
426const Q0_SPDIF_RATE_48000_FLAG: u32 = 0x0000c000;
427const Q0_SPDIF_RATE_44100_FLAG: u32 = 0x00008000;
428const Q0_SPDIF_RATE_32000_FLAG: u32 = 0x00004000;
429const Q0_LOCK_ADAT_MASK: u32 = 0x00001000;
430const Q0_SYNC_ADAT_MASK: u32 = 0x00000400;
431
432const Q1_WORD_OUT_SINGLE_MASK: u32 = 0x00002000;
434const Q1_CONF_CLK_SRC_MASK: u32 = 0x00001c01;
435const Q1_CONF_CLK_SRC_LTC_FLAG: u32 = 0x00001400;
436const Q1_CONF_CLK_SRC_WORD_CLK_FLAG: u32 = 0x00001000;
437const Q1_CONF_CLK_SRC_SPDIF_FLAG: u32 = 0x00000c00;
438const Q1_CONF_CLK_SRC_INTERNAL_FLAG: u32 = 0x00000001;
439const Q1_CONF_CLK_SRC_ADAT_FLAG: u32 = 0x00000000;
440const Q1_SPDIF_IN_IFACE_MASK: u32 = 0x00000200;
441const Q1_OPT_OUT_SIGNAL_MASK: u32 = 0x00000100;
442const Q1_SPDIF_OUT_EMPHASIS_MASK: u32 = 0x00000040;
443const Q1_SPDIF_OUT_FMT_MASK: u32 = 0x00000020;
444const Q1_CONF_CLK_RATE_MASK: u32 = 0x0000001e;
445const Q1_CONF_CLK_RATE_192000_FLAG: u32 = 0x00000016;
446const Q1_CONF_CLK_RATE_176400_FLAG: u32 = 0x00000010;
447const Q1_CONF_CLK_RATE_128000_FLAG: u32 = 0x00000012;
448const Q1_CONF_CLK_RATE_96000_FLAG: u32 = 0x0000000e;
449const Q1_CONF_CLK_RATE_88200_FLAG: u32 = 0x00000008;
450const Q1_CONF_CLK_RATE_64000_FLAG: u32 = 0x0000000a;
451const Q1_CONF_CLK_RATE_48000_FLAG: u32 = 0x00000006;
452const Q1_CONF_CLK_RATE_44100_FLAG: u32 = 0x00000000;
453const Q1_CONF_CLK_RATE_32000_FLAG: u32 = 0x00000002;
454
455#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
457pub struct Ff400ClkLockStatus {
458 pub adat: bool,
459 pub spdif: bool,
460 pub word_clock: bool,
461}
462
463impl Ff400ClkLockStatus {
464 const QUADLET_COUNT: usize = 1;
465}
466
467fn serialize_lock_status(status: &Ff400ClkLockStatus, quads: &mut [u32]) {
468 assert!(quads.len() >= Ff400ClkLockStatus::QUADLET_COUNT);
469
470 quads[0] &= !Q0_LOCK_ADAT_MASK;
471 if status.adat {
472 quads[0] |= Q0_LOCK_ADAT_MASK;
473 }
474
475 quads[0] &= !Q0_LOCK_SPDIF_MASK;
476 if status.spdif {
477 quads[0] |= Q0_LOCK_SPDIF_MASK;
478 }
479
480 quads[0] &= !Q0_LOCK_WORD_CLOCK_MASK;
481 if status.word_clock {
482 quads[0] |= Q0_LOCK_WORD_CLOCK_MASK;
483 }
484}
485
486fn deserialize_lock_status(status: &mut Ff400ClkLockStatus, quads: &[u32]) {
487 assert!(quads.len() >= Ff400ClkLockStatus::QUADLET_COUNT);
488
489 status.adat = quads[0] & Q0_LOCK_ADAT_MASK > 0;
490 status.spdif = quads[0] & Q0_LOCK_SPDIF_MASK > 0;
491 status.word_clock = quads[0] & Q0_LOCK_WORD_CLOCK_MASK > 0;
492}
493
494#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
496pub struct Ff400ClkSyncStatus {
497 pub adat: bool,
498 pub spdif: bool,
499 pub word_clock: bool,
500}
501
502impl Ff400ClkSyncStatus {
503 const QUADLET_COUNT: usize = 1;
504}
505
506fn serialize_sync_status(status: &Ff400ClkSyncStatus, quads: &mut [u32]) {
507 assert!(quads.len() >= Ff400ClkSyncStatus::QUADLET_COUNT);
508
509 quads[0] &= !Q0_SYNC_ADAT_MASK;
510 if status.adat {
511 quads[0] |= Q0_SYNC_ADAT_MASK;
512 }
513
514 quads[0] &= !Q0_SYNC_SPDIF_MASK;
515 if status.spdif {
516 quads[0] |= Q0_SYNC_SPDIF_MASK;
517 }
518
519 quads[0] &= !Q0_SYNC_WORD_CLOCK_MASK;
520 if status.word_clock {
521 quads[0] |= Q0_SYNC_WORD_CLOCK_MASK;
522 }
523}
524
525fn deserialize_sync_status(status: &mut Ff400ClkSyncStatus, quads: &[u32]) {
526 assert!(quads.len() >= Ff400ClkSyncStatus::QUADLET_COUNT);
527
528 status.adat = quads[0] & Q0_SYNC_ADAT_MASK > 0;
529 status.spdif = quads[0] & Q0_SYNC_SPDIF_MASK > 0;
530 status.word_clock = quads[0] & Q0_SYNC_WORD_CLOCK_MASK > 0;
531}
532
533#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
535pub struct Ff400Status {
536 pub spdif_in: SpdifInput,
538 pub spdif_out: FormerSpdifOutput,
540 pub opt_out_signal: OpticalOutputSignal,
542 pub word_out_single: bool,
544 pub sync: Ff400ClkSyncStatus,
546 pub lock: Ff400ClkLockStatus,
548
549 pub spdif_rate: Option<ClkNominalRate>,
550 pub active_clk_src: Ff400ClkSrc,
551 pub external_clk_rate: Option<ClkNominalRate>,
552 pub configured_clk_src: Ff400ClkSrc,
553 pub configured_clk_rate: ClkNominalRate,
554}
555
556impl Ff400Status {
557 const QUADLET_COUNT: usize = FORMER_STATUS_SIZE / 4;
558}
559
560impl RmeFfOffsetParamsSerialize<Ff400Status> for Ff400Protocol {
561 fn serialize_offsets(params: &Ff400Status) -> Vec<u8> {
562 let mut quads = [0; Ff400Status::QUADLET_COUNT];
563
564 serialize_lock_status(¶ms.lock, &mut quads);
565 serialize_sync_status(¶ms.sync, &mut quads);
566
567 quads[0] &= !Q0_SPDIF_RATE_MASK;
568 if let Some(rate) = ¶ms.spdif_rate {
569 let flag = match rate {
570 ClkNominalRate::R32000 => Q0_SPDIF_RATE_32000_FLAG,
571 ClkNominalRate::R44100 => Q0_SPDIF_RATE_44100_FLAG,
572 ClkNominalRate::R48000 => Q0_SPDIF_RATE_48000_FLAG,
573 ClkNominalRate::R64000 => Q0_SPDIF_RATE_64000_FLAG,
574 ClkNominalRate::R88200 => Q0_SPDIF_RATE_88200_FLAG,
575 ClkNominalRate::R96000 => Q0_SPDIF_RATE_96000_FLAG,
576 ClkNominalRate::R128000 => Q0_SPDIF_RATE_128000_FLAG,
577 ClkNominalRate::R176400 => Q0_SPDIF_RATE_176400_FLAG,
578 ClkNominalRate::R192000 => Q0_SPDIF_RATE_192000_FLAG,
579 };
580 quads[0] |= flag;
581 }
582
583 quads[0] &= !Q0_ACTIVE_CLK_SRC_MASK;
584 let flag = match params.active_clk_src {
585 Ff400ClkSrc::Adat => Q0_ACTIVE_CLK_SRC_ADAT_FLAG,
586 Ff400ClkSrc::Spdif => Q0_ACTIVE_CLK_SRC_SPDIF_FLAG,
587 Ff400ClkSrc::WordClock => Q0_ACTIVE_CLK_SRC_WORD_CLK_FLAG,
588 Ff400ClkSrc::Ltc => Q0_ACTIVE_CLK_SRC_LTC_FLAG,
589 Ff400ClkSrc::Internal => Q0_ACTIVE_CLK_SRC_INTERNAL_FLAG,
590 };
591 quads[0] |= flag;
592
593 quads[0] &= !Q0_EXT_CLK_RATE_MASK;
594 if let Some(rate) = ¶ms.external_clk_rate {
595 let flag = match rate {
596 ClkNominalRate::R32000 => Q0_EXT_CLK_RATE_32000_FLAG,
597 ClkNominalRate::R44100 => Q0_EXT_CLK_RATE_44100_FLAG,
598 ClkNominalRate::R48000 => Q0_EXT_CLK_RATE_48000_FLAG,
599 ClkNominalRate::R64000 => Q0_EXT_CLK_RATE_64000_FLAG,
600 ClkNominalRate::R88200 => Q0_EXT_CLK_RATE_88200_FLAG,
601 ClkNominalRate::R96000 => Q0_EXT_CLK_RATE_96000_FLAG,
602 ClkNominalRate::R128000 => Q0_EXT_CLK_RATE_128000_FLAG,
603 ClkNominalRate::R176400 => Q0_EXT_CLK_RATE_176400_FLAG,
604 ClkNominalRate::R192000 => Q0_EXT_CLK_RATE_192000_FLAG,
605 };
606 quads[0] |= flag;
607 }
608
609 quads[1] &= !Q1_SPDIF_IN_IFACE_MASK;
610 if params.spdif_in.iface == SpdifIface::Optical {
611 quads[1] |= Q1_SPDIF_IN_IFACE_MASK;
612 }
613
614 quads[1] &= !Q1_SPDIF_OUT_FMT_MASK;
615 if params.spdif_out.format == SpdifFormat::Professional {
616 quads[1] |= Q1_SPDIF_OUT_FMT_MASK;
617 }
618
619 quads[1] &= !Q1_SPDIF_OUT_EMPHASIS_MASK;
620 if params.spdif_out.emphasis {
621 quads[1] |= Q1_SPDIF_OUT_EMPHASIS_MASK;
622 }
623
624 quads[1] &= !Q1_OPT_OUT_SIGNAL_MASK;
625 if params.opt_out_signal == OpticalOutputSignal::Spdif {
626 quads[1] |= Q1_OPT_OUT_SIGNAL_MASK;
627 }
628
629 quads[1] &= !Q1_WORD_OUT_SINGLE_MASK;
630 if params.word_out_single {
631 quads[1] |= Q1_WORD_OUT_SINGLE_MASK;
632 }
633
634 quads[1] &= !Q1_CONF_CLK_SRC_MASK;
635 let flag = match params.configured_clk_src {
636 Ff400ClkSrc::Internal => Q1_CONF_CLK_SRC_INTERNAL_FLAG,
637 Ff400ClkSrc::Spdif => Q1_CONF_CLK_SRC_SPDIF_FLAG,
638 Ff400ClkSrc::WordClock => Q1_CONF_CLK_SRC_WORD_CLK_FLAG,
639 Ff400ClkSrc::Ltc => Q1_CONF_CLK_SRC_LTC_FLAG,
640 Ff400ClkSrc::Adat => Q1_CONF_CLK_SRC_ADAT_FLAG,
641 };
642 quads[1] |= flag;
643
644 quads[1] &= !Q1_CONF_CLK_RATE_MASK;
645 let flag = match params.configured_clk_rate {
646 ClkNominalRate::R32000 => Q1_CONF_CLK_RATE_32000_FLAG,
647 ClkNominalRate::R48000 => Q1_CONF_CLK_RATE_48000_FLAG,
648 ClkNominalRate::R64000 => Q1_CONF_CLK_RATE_64000_FLAG,
649 ClkNominalRate::R88200 => Q1_CONF_CLK_RATE_88200_FLAG,
650 ClkNominalRate::R96000 => Q1_CONF_CLK_RATE_96000_FLAG,
651 ClkNominalRate::R128000 => Q1_CONF_CLK_RATE_128000_FLAG,
652 ClkNominalRate::R176400 => Q1_CONF_CLK_RATE_176400_FLAG,
653 ClkNominalRate::R192000 => Q1_CONF_CLK_RATE_192000_FLAG,
654 ClkNominalRate::R44100 => Q1_CONF_CLK_RATE_44100_FLAG,
655 };
656 quads[1] |= flag;
657
658 quads.iter().flat_map(|quad| quad.to_le_bytes()).collect()
659 }
660}
661
662impl RmeFfOffsetParamsDeserialize<Ff400Status> for Ff400Protocol {
663 fn deserialize_offsets(params: &mut Ff400Status, raw: &[u8]) {
664 assert!(raw.len() >= FORMER_STATUS_SIZE);
665
666 let mut quads = [0; Ff400Status::QUADLET_COUNT];
667 let mut quadlet = [0; 4];
668 quads.iter_mut().enumerate().for_each(|(i, quad)| {
669 let pos = i * 4;
670 quadlet.copy_from_slice(&raw[pos..(pos + 4)]);
671 *quad = u32::from_le_bytes(quadlet);
672 });
673
674 deserialize_lock_status(&mut params.lock, &mut quads);
675 deserialize_sync_status(&mut params.sync, &mut quads);
676
677 params.spdif_rate = match quads[0] & Q0_SPDIF_RATE_MASK {
678 Q0_SPDIF_RATE_32000_FLAG => Some(ClkNominalRate::R32000),
679 Q0_SPDIF_RATE_44100_FLAG => Some(ClkNominalRate::R44100),
680 Q0_SPDIF_RATE_48000_FLAG => Some(ClkNominalRate::R48000),
681 Q0_SPDIF_RATE_64000_FLAG => Some(ClkNominalRate::R64000),
682 Q0_SPDIF_RATE_88200_FLAG => Some(ClkNominalRate::R88200),
683 Q0_SPDIF_RATE_96000_FLAG => Some(ClkNominalRate::R96000),
684 Q0_SPDIF_RATE_128000_FLAG => Some(ClkNominalRate::R128000),
685 Q0_SPDIF_RATE_176400_FLAG => Some(ClkNominalRate::R176400),
686 Q0_SPDIF_RATE_192000_FLAG => Some(ClkNominalRate::R192000),
687 _ => None,
688 };
689
690 params.active_clk_src = match quads[0] & Q0_ACTIVE_CLK_SRC_MASK {
691 Q0_ACTIVE_CLK_SRC_ADAT_FLAG => Ff400ClkSrc::Adat,
692 Q0_ACTIVE_CLK_SRC_SPDIF_FLAG => Ff400ClkSrc::Spdif,
693 Q0_ACTIVE_CLK_SRC_WORD_CLK_FLAG => Ff400ClkSrc::WordClock,
694 Q0_ACTIVE_CLK_SRC_LTC_FLAG => Ff400ClkSrc::Ltc,
695 Q0_ACTIVE_CLK_SRC_INTERNAL_FLAG => Ff400ClkSrc::Internal,
696 _ => unreachable!(),
697 };
698
699 params.external_clk_rate = match quads[0] & Q0_EXT_CLK_RATE_MASK {
700 Q0_EXT_CLK_RATE_32000_FLAG => Some(ClkNominalRate::R32000),
701 Q0_EXT_CLK_RATE_44100_FLAG => Some(ClkNominalRate::R44100),
702 Q0_EXT_CLK_RATE_48000_FLAG => Some(ClkNominalRate::R48000),
703 Q0_EXT_CLK_RATE_64000_FLAG => Some(ClkNominalRate::R64000),
704 Q0_EXT_CLK_RATE_88200_FLAG => Some(ClkNominalRate::R88200),
705 Q0_EXT_CLK_RATE_96000_FLAG => Some(ClkNominalRate::R96000),
706 Q0_EXT_CLK_RATE_128000_FLAG => Some(ClkNominalRate::R128000),
707 Q0_EXT_CLK_RATE_176400_FLAG => Some(ClkNominalRate::R176400),
708 Q0_EXT_CLK_RATE_192000_FLAG => Some(ClkNominalRate::R192000),
709 _ => None,
710 };
711
712 params.spdif_in.iface = if quads[1] & Q1_SPDIF_IN_IFACE_MASK > 0 {
713 SpdifIface::Optical
714 } else {
715 SpdifIface::Coaxial
716 };
717
718 params.spdif_out.format = if quads[1] & Q1_SPDIF_OUT_FMT_MASK > 0 {
719 SpdifFormat::Professional
720 } else {
721 SpdifFormat::Consumer
722 };
723
724 params.spdif_out.emphasis = quads[1] & Q1_SPDIF_OUT_EMPHASIS_MASK > 0;
725
726 params.opt_out_signal = if quads[1] & Q1_OPT_OUT_SIGNAL_MASK > 0 {
727 OpticalOutputSignal::Spdif
728 } else {
729 OpticalOutputSignal::Adat
730 };
731
732 params.word_out_single = quads[1] & Q1_WORD_OUT_SINGLE_MASK > 0;
733
734 params.configured_clk_src = match quads[1] & Q1_CONF_CLK_SRC_MASK {
735 Q1_CONF_CLK_SRC_INTERNAL_FLAG => Ff400ClkSrc::Internal,
736 Q1_CONF_CLK_SRC_SPDIF_FLAG => Ff400ClkSrc::Spdif,
737 Q1_CONF_CLK_SRC_WORD_CLK_FLAG => Ff400ClkSrc::WordClock,
738 Q1_CONF_CLK_SRC_LTC_FLAG => Ff400ClkSrc::Ltc,
739 Q1_CONF_CLK_SRC_ADAT_FLAG | _ => Ff400ClkSrc::Adat,
740 };
741
742 params.configured_clk_rate = match quads[1] & Q1_CONF_CLK_RATE_MASK {
743 Q1_CONF_CLK_RATE_32000_FLAG => ClkNominalRate::R32000,
744 Q1_CONF_CLK_RATE_48000_FLAG => ClkNominalRate::R48000,
745 Q1_CONF_CLK_RATE_64000_FLAG => ClkNominalRate::R64000,
746 Q1_CONF_CLK_RATE_88200_FLAG => ClkNominalRate::R88200,
747 Q1_CONF_CLK_RATE_96000_FLAG => ClkNominalRate::R96000,
748 Q1_CONF_CLK_RATE_128000_FLAG => ClkNominalRate::R128000,
749 Q1_CONF_CLK_RATE_176400_FLAG => ClkNominalRate::R176400,
750 Q1_CONF_CLK_RATE_192000_FLAG => ClkNominalRate::R192000,
751 Q1_CONF_CLK_RATE_44100_FLAG | _ => ClkNominalRate::R44100,
752 };
753 }
754}
755
756impl RmeFfCacheableParamsOperation<Ff400Status> for Ff400Protocol {
757 fn cache_wholly(
758 req: &mut FwReq,
759 node: &mut FwNode,
760 params: &mut Ff400Status,
761 timeout_ms: u32,
762 ) -> Result<(), Error> {
763 read_status::<Ff400Protocol, Ff400Status>(req, node, STATUS_OFFSET, params, timeout_ms)
764 }
765}
766
767const Q0_HP_OUT_LEVEL_MASK: u32 = 0x00060000;
769const Q0_HP_OUT_LEVEL_HIGH_FLAG: u32 = 0x00040000;
770const Q0_HP_OUT_LEVEL_CON_FLAG: u32 = 0x00020000;
771const Q0_HP_OUT_LEVEL_PRO_FLAG: u32 = 0x00000000;
772const Q0_LINE_OUT_LEVEL_MASK: u32 = 0x00001c00;
773const Q0_LINE_OUT_LEVEL_CON_FLAG: u32 = 0x00001000;
774const Q0_LINE_OUT_LEVEL_PRO_FLAG: u32 = 0x00000800;
775const Q0_LINE_OUT_LEVEL_HIGH_FLAG: u32 = 0x00000400;
776const Q0_INPUT_2_INST_MASK: u32 = 0x00000200;
777const Q0_INPUT_2_PAD_MASK: u32 = 0x00000100;
778const Q0_INPUT_1_POWERING_MASK: u32 = 0x00000080;
779const Q0_LINE_IN_LEVEL_MASK: u32 = 0x00000038;
780const Q0_LINE_IN_LEVEL_CON_FLAG: u32 = 0x00000020;
781const Q0_LINE_IN_LEVEL_LOW_FLAG: u32 = 0x00000010;
782const Q0_LINE_IN_LEVEL_PRO_FLAG: u32 = 0x00000008;
783const Q0_INPUT_3_INST_MASK: u32 = 0x00000004;
784const Q0_INPUT_3_PAD_MASK: u32 = 0x00000002;
785const Q0_INPUT_0_POWERING_MASK: u32 = 0x00000001;
786
787const Q1_LINE_OUT_LEVEL_MASK: u32 = 0x00000018;
789const Q1_LINE_OUT_LEVEL_PRO_FLAG: u32 = 0x00000018;
790const Q1_LINE_OUT_LEVEL_HIGH_FLAG: u32 = 0x00000010;
791const Q1_LINE_OUT_LEVEL_CON_FLAG: u32 = 0x00000008;
792const Q1_LINE_IN_LEVEL_MASK: u32 = 0x00000003;
793const Q1_LINE_IN_LEVEL_CON_FLAG: u32 = 0x00000003;
794const Q1_LINE_IN_LEVEL_PRO_FLAG: u32 = 0x00000002;
795const Q1_LINE_IN_LEVEL_LOW_FLAG: u32 = 0x00000000;
796
797const Q2_CONTINUE_AT_ERRORS: u32 = 0x80000000;
799const Q2_SPDIF_IN_USE_PREEMBLE: u32 = 0x40000000;
800const Q2_MIDI_TX_LOW_OFFSET_MASK: u32 = 0x3c000000;
801const Q2_MIDI_TX_LOW_OFFSET_0180_FLAG: u32 = 0x20000000;
802const Q2_MIDI_TX_LOW_OFFSET_0100_FLAG: u32 = 0x10000000;
803const Q2_MIDI_TX_LOW_OFFSET_0080_FLAG: u32 = 0x08000000;
804const Q2_MIDI_TX_LOW_OFFSET_0000_FLAG: u32 = 0x04000000;
805const Q2_MIDI_TX_SUPPRESS_MASK: u32 = 0x03000000;
806const Q2_WORD_OUT_SINGLE_SPEED_MASK: u32 = 0x00002000;
807const Q2_CLK_SRC_MASK: u32 = 0x00001c01;
808const Q2_CLK_SRC_LTC_FLAG: u32 = 0x00001400;
809const Q2_CLK_SRC_WORD_CLK_FLAG: u32 = 0x00001000;
810const Q2_CLK_SRC_SPDIF_FLAG: u32 = 0x00000c00;
811const Q2_CLK_SRC_INTERNAL_FLAG: u32 = 0x00000001;
812const Q2_CLK_SRC_ADAT_FLAG: u32 = 0x00000000;
813const Q2_SPDIF_IN_IFACE_OPT_MASK: u32 = 0x00000200;
814const Q2_OPT_OUT_SIGNAL_MASK: u32 = 0x00000100;
815const Q2_SPDIF_OUT_NON_AUDIO_MASK: u32 = 0x00000080;
816const Q2_SPDIF_OUT_EMPHASIS_MASK: u32 = 0x00000040;
817const Q2_SPDIF_OUT_FMT_PRO_MASK: u32 = 0x00000020;
818const Q2_CLK_AVAIL_RATE_QUADRUPLE_MASK: u32 = 0x00000010;
819const Q2_CLK_AVAIL_RATE_DOUBLE_MASK: u32 = 0x00000008;
820const Q2_CLK_AVAIL_RATE_BASE_48000_MASK: u32 = 0x00000004;
821const Q2_CLK_AVAIL_RATE_BASE_44100_MASK: u32 = 0x00000002;
822
823#[derive(Debug, Copy, Clone, PartialEq, Eq)]
825pub struct Ff400ClkConfig {
826 pub primary_src: Ff400ClkSrc,
827 avail_rate_44100: bool,
828 avail_rate_48000: bool,
829 avail_rate_double: bool,
830 avail_rate_quadruple: bool,
831}
832
833impl Default for Ff400ClkConfig {
834 fn default() -> Self {
835 Self {
836 primary_src: Ff400ClkSrc::default(),
837 avail_rate_44100: true,
838 avail_rate_48000: true,
839 avail_rate_double: true,
840 avail_rate_quadruple: true,
841 }
842 }
843}
844
845impl Ff400ClkConfig {
846 const QUADLET_COUNT: usize = 3;
847}
848
849fn serialize_clock_config(config: &Ff400ClkConfig, quads: &mut [u32]) {
850 assert!(quads.len() >= Ff400ClkConfig::QUADLET_COUNT);
851
852 quads[2] &= !Q2_CLK_SRC_MASK;
853 let flag = match config.primary_src {
854 Ff400ClkSrc::Internal => Q2_CLK_SRC_INTERNAL_FLAG,
855 Ff400ClkSrc::Ltc => Q2_CLK_SRC_LTC_FLAG,
856 Ff400ClkSrc::WordClock => Q2_CLK_SRC_WORD_CLK_FLAG,
857 Ff400ClkSrc::Adat => Q2_CLK_SRC_ADAT_FLAG,
858 Ff400ClkSrc::Spdif => Q2_CLK_SRC_SPDIF_FLAG,
859 };
860 quads[2] |= flag;
861
862 quads[2] &= !Q2_CLK_AVAIL_RATE_BASE_44100_MASK;
863 if config.avail_rate_44100 {
864 quads[2] |= Q2_CLK_AVAIL_RATE_BASE_44100_MASK;
865 }
866
867 quads[2] &= !Q2_CLK_AVAIL_RATE_BASE_48000_MASK;
868 if config.avail_rate_48000 {
869 quads[2] |= Q2_CLK_AVAIL_RATE_BASE_48000_MASK;
870 }
871
872 quads[2] &= !Q2_CLK_AVAIL_RATE_DOUBLE_MASK;
873 if config.avail_rate_double {
874 quads[2] |= Q2_CLK_AVAIL_RATE_DOUBLE_MASK;
875 }
876
877 quads[2] &= !Q2_CLK_AVAIL_RATE_QUADRUPLE_MASK;
878 if config.avail_rate_quadruple {
879 quads[2] |= Q2_CLK_AVAIL_RATE_QUADRUPLE_MASK;
880 }
881}
882
883fn deserialize_clock_config(config: &mut Ff400ClkConfig, quads: &[u32]) {
884 assert!(quads.len() >= Ff400ClkConfig::QUADLET_COUNT);
885
886 config.primary_src = match quads[2] & Q2_CLK_SRC_MASK {
887 Q2_CLK_SRC_INTERNAL_FLAG => Ff400ClkSrc::Internal,
888 Q2_CLK_SRC_LTC_FLAG => Ff400ClkSrc::Ltc,
889 Q2_CLK_SRC_WORD_CLK_FLAG => Ff400ClkSrc::WordClock,
890 Q2_CLK_SRC_SPDIF_FLAG => Ff400ClkSrc::Spdif,
891 Q2_CLK_SRC_ADAT_FLAG | _ => Ff400ClkSrc::Adat,
892 };
893
894 config.avail_rate_44100 = quads[2] & Q2_CLK_AVAIL_RATE_BASE_44100_MASK > 0;
895 config.avail_rate_48000 = quads[2] & Q2_CLK_AVAIL_RATE_BASE_48000_MASK > 0;
896 config.avail_rate_double = quads[2] & Q2_CLK_AVAIL_RATE_DOUBLE_MASK > 0;
897 config.avail_rate_quadruple = quads[2] & Q2_CLK_AVAIL_RATE_QUADRUPLE_MASK > 0;
898}
899
900#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
902pub struct Ff400AnalogInConfig {
903 pub line_level: FormerLineInNominalLevel,
905 pub phantom_powering: [bool; 2],
907 pub insts: [bool; 2],
909 pub pad: [bool; 2],
911}
912
913impl Ff400AnalogInConfig {
914 const QUADLET_COUNT: usize = 2;
915}
916
917fn serialize_analog_input_config(config: &Ff400AnalogInConfig, quads: &mut [u32]) {
918 assert!(quads.len() >= Ff400AnalogInConfig::QUADLET_COUNT);
919
920 quads[0] &= !Q0_LINE_IN_LEVEL_MASK;
921 quads[1] &= !Q1_LINE_IN_LEVEL_MASK;
922 match config.line_level {
923 FormerLineInNominalLevel::Low => {
924 quads[0] |= Q0_LINE_IN_LEVEL_LOW_FLAG;
925 quads[1] |= Q1_LINE_IN_LEVEL_LOW_FLAG;
926 }
927 FormerLineInNominalLevel::Consumer => {
928 quads[0] |= Q0_LINE_IN_LEVEL_CON_FLAG;
929 quads[1] |= Q1_LINE_IN_LEVEL_CON_FLAG;
930 }
931 FormerLineInNominalLevel::Professional => {
932 quads[0] |= Q0_LINE_IN_LEVEL_PRO_FLAG;
933 quads[1] |= Q1_LINE_IN_LEVEL_PRO_FLAG;
934 }
935 }
936
937 if config.phantom_powering[0] {
938 quads[0] |= Q0_INPUT_0_POWERING_MASK;
939 }
940 if config.phantom_powering[1] {
941 quads[0] |= Q0_INPUT_1_POWERING_MASK;
942 }
943
944 if config.insts[0] {
945 quads[0] |= Q0_INPUT_2_INST_MASK;
946 }
947 if config.insts[1] {
948 quads[0] |= Q0_INPUT_3_INST_MASK;
949 }
950
951 if config.pad[0] {
952 quads[0] |= Q0_INPUT_2_PAD_MASK;
953 }
954 if config.pad[1] {
955 quads[0] |= Q0_INPUT_3_PAD_MASK;
956 }
957}
958
959fn deserialize_analog_input_config(config: &mut Ff400AnalogInConfig, quads: &[u32]) {
960 assert!(quads.len() >= Ff400AnalogInConfig::QUADLET_COUNT);
961
962 let pair = (
963 quads[0] & Q0_LINE_IN_LEVEL_MASK,
964 quads[1] & Q1_LINE_IN_LEVEL_MASK,
965 );
966 config.line_level = match pair {
967 (Q0_LINE_IN_LEVEL_LOW_FLAG, Q1_LINE_IN_LEVEL_LOW_FLAG) => FormerLineInNominalLevel::Low,
968 (Q0_LINE_IN_LEVEL_CON_FLAG, Q1_LINE_IN_LEVEL_CON_FLAG) => {
969 FormerLineInNominalLevel::Consumer
970 }
971 (Q0_LINE_IN_LEVEL_PRO_FLAG, Q1_LINE_IN_LEVEL_PRO_FLAG) => {
972 FormerLineInNominalLevel::Professional
973 }
974 _ => unreachable!(),
975 };
976
977 config.phantom_powering[0] = quads[0] & Q0_INPUT_0_POWERING_MASK > 0;
978 config.phantom_powering[1] = quads[0] & Q0_INPUT_1_POWERING_MASK > 0;
979
980 config.insts[0] = quads[0] & Q0_INPUT_2_INST_MASK > 0;
981 config.insts[1] = quads[0] & Q0_INPUT_3_INST_MASK > 0;
982
983 config.pad[0] = quads[0] & Q0_INPUT_2_PAD_MASK > 0;
984 config.pad[1] = quads[0] & Q0_INPUT_3_PAD_MASK > 0;
985}
986
987#[derive(Debug, Clone, Copy, PartialEq, Eq)]
989#[allow(dead_code)]
990enum Ff400MidiTxLowOffset {
991 A0000,
993 A0080,
995 A0100,
997 A0180,
999}
1000
1001impl Default for Ff400MidiTxLowOffset {
1002 fn default() -> Self {
1003 Self::A0000
1004 }
1005}
1006
1007impl Ff400MidiTxLowOffset {
1008 const QUADLET_COUNT: usize = 3;
1009}
1010
1011fn serialize_midi_tx_low_offset(offset: &Ff400MidiTxLowOffset, quads: &mut [u32]) {
1012 assert!(quads.len() >= Ff400MidiTxLowOffset::QUADLET_COUNT);
1013
1014 quads[2] &= !Q2_MIDI_TX_LOW_OFFSET_MASK;
1015 quads[2] |= match offset {
1016 Ff400MidiTxLowOffset::A0000 => Q2_MIDI_TX_LOW_OFFSET_0000_FLAG,
1017 Ff400MidiTxLowOffset::A0080 => Q2_MIDI_TX_LOW_OFFSET_0080_FLAG,
1018 Ff400MidiTxLowOffset::A0100 => Q2_MIDI_TX_LOW_OFFSET_0100_FLAG,
1019 Ff400MidiTxLowOffset::A0180 => Q2_MIDI_TX_LOW_OFFSET_0180_FLAG,
1020 };
1021}
1022
1023fn deserialize_midi_tx_low_offset(offset: &mut Ff400MidiTxLowOffset, quads: &[u32]) {
1024 assert!(quads.len() >= Ff400MidiTxLowOffset::QUADLET_COUNT);
1025
1026 *offset = match quads[2] & Q2_MIDI_TX_LOW_OFFSET_MASK {
1027 Q2_MIDI_TX_LOW_OFFSET_0180_FLAG => Ff400MidiTxLowOffset::A0180,
1028 Q2_MIDI_TX_LOW_OFFSET_0100_FLAG => Ff400MidiTxLowOffset::A0100,
1029 Q2_MIDI_TX_LOW_OFFSET_0080_FLAG => Ff400MidiTxLowOffset::A0080,
1030 Q2_MIDI_TX_LOW_OFFSET_0000_FLAG => Ff400MidiTxLowOffset::A0000,
1031 _ => unreachable!(),
1032 }
1033}
1034
1035#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1037pub struct Ff400Config {
1038 midi_tx_low_offset: Ff400MidiTxLowOffset,
1040 midi_tx_enable: bool,
1042 pub clk: Ff400ClkConfig,
1044 pub analog_in: Ff400AnalogInConfig,
1046 pub line_out_level: LineOutNominalLevel,
1048 pub hp_out_level: LineOutNominalLevel,
1050 pub spdif_in: SpdifInput,
1052 pub spdif_out: FormerSpdifOutput,
1054 pub opt_out_signal: OpticalOutputSignal,
1056 pub word_out_single: bool,
1058 continue_at_errors: bool,
1060}
1061
1062impl Default for Ff400Config {
1063 fn default() -> Self {
1064 Self {
1065 midi_tx_low_offset: Default::default(),
1066 midi_tx_enable: true,
1067 clk: Default::default(),
1068 analog_in: Default::default(),
1069 line_out_level: Default::default(),
1070 hp_out_level: Default::default(),
1071 spdif_in: Default::default(),
1072 spdif_out: Default::default(),
1073 opt_out_signal: Default::default(),
1074 word_out_single: Default::default(),
1075 continue_at_errors: true,
1076 }
1077 }
1078}
1079
1080impl Ff400Config {
1081 const QUADLET_COUNT: usize = FORMER_CONFIG_SIZE / 4;
1082
1083 pub fn init(&mut self, status: &Ff400Status) {
1086 self.clk.primary_src = status.configured_clk_src;
1087 self.spdif_in = status.spdif_in;
1088 self.spdif_out = status.spdif_out;
1089 self.opt_out_signal = status.opt_out_signal;
1090 self.word_out_single = status.word_out_single;
1091 }
1092}
1093
1094impl RmeFfOffsetParamsSerialize<Ff400Config> for Ff400Protocol {
1095 fn serialize_offsets(params: &Ff400Config) -> Vec<u8> {
1096 let mut quads = [0; Ff400Config::QUADLET_COUNT];
1097
1098 serialize_midi_tx_low_offset(¶ms.midi_tx_low_offset, &mut quads);
1099
1100 quads[2] &= !Q2_MIDI_TX_SUPPRESS_MASK;
1101 if !params.midi_tx_enable {
1102 quads[2] |= Q2_MIDI_TX_SUPPRESS_MASK;
1103 }
1104
1105 serialize_clock_config(¶ms.clk, &mut quads);
1106 serialize_analog_input_config(¶ms.analog_in, &mut quads);
1107
1108 quads[0] &= !Q0_LINE_OUT_LEVEL_MASK;
1109 quads[1] &= !Q1_LINE_OUT_LEVEL_MASK;
1110 match ¶ms.line_out_level {
1111 LineOutNominalLevel::High => {
1112 quads[0] |= Q0_LINE_OUT_LEVEL_HIGH_FLAG;
1113 quads[1] |= Q1_LINE_OUT_LEVEL_HIGH_FLAG;
1114 }
1115 LineOutNominalLevel::Consumer => {
1116 quads[0] |= Q0_LINE_OUT_LEVEL_CON_FLAG;
1117 quads[1] |= Q1_LINE_OUT_LEVEL_CON_FLAG;
1118 }
1119 LineOutNominalLevel::Professional => {
1120 quads[0] |= Q0_LINE_OUT_LEVEL_PRO_FLAG;
1121 quads[1] |= Q1_LINE_OUT_LEVEL_PRO_FLAG;
1122 }
1123 }
1124
1125 quads[0] &= !Q0_HP_OUT_LEVEL_MASK;
1126 match ¶ms.hp_out_level {
1127 LineOutNominalLevel::High => {
1128 quads[0] |= Q0_HP_OUT_LEVEL_HIGH_FLAG;
1129 }
1130 LineOutNominalLevel::Consumer => {
1131 quads[0] |= Q0_HP_OUT_LEVEL_CON_FLAG;
1132 }
1133 LineOutNominalLevel::Professional => {
1134 quads[0] |= Q0_HP_OUT_LEVEL_PRO_FLAG;
1135 }
1136 }
1137
1138 if params.spdif_in.iface == SpdifIface::Optical {
1139 quads[2] |= Q2_SPDIF_IN_IFACE_OPT_MASK;
1140 }
1141 if params.spdif_in.use_preemble {
1142 quads[2] |= Q2_SPDIF_IN_USE_PREEMBLE;
1143 }
1144
1145 if params.opt_out_signal == OpticalOutputSignal::Spdif {
1146 quads[2] |= Q2_OPT_OUT_SIGNAL_MASK;
1147 }
1148 if params.spdif_out.format == SpdifFormat::Professional {
1149 quads[2] |= Q2_SPDIF_OUT_FMT_PRO_MASK;
1150 }
1151 if params.spdif_out.emphasis {
1152 quads[2] |= Q2_SPDIF_OUT_EMPHASIS_MASK;
1153 }
1154 if params.spdif_out.non_audio {
1155 quads[2] |= Q2_SPDIF_OUT_NON_AUDIO_MASK;
1156 }
1157
1158 if params.word_out_single {
1159 quads[2] |= Q2_WORD_OUT_SINGLE_SPEED_MASK;
1160 }
1161
1162 if params.continue_at_errors {
1163 quads[2] |= Q2_CONTINUE_AT_ERRORS;
1164 }
1165
1166 quads.iter().flat_map(|quad| quad.to_le_bytes()).collect()
1167 }
1168}
1169
1170impl RmeFfOffsetParamsDeserialize<Ff400Config> for Ff400Protocol {
1171 fn deserialize_offsets(params: &mut Ff400Config, raw: &[u8]) {
1172 assert!(raw.len() >= FORMER_CONFIG_SIZE);
1173
1174 let mut quads = [0; Ff400Config::QUADLET_COUNT];
1175 let mut quadlet = [0; 4];
1176 quads.iter_mut().enumerate().for_each(|(i, quad)| {
1177 let pos = i * 4;
1178 quadlet.copy_from_slice(&raw[pos..(pos + 4)]);
1179 *quad = u32::from_le_bytes(quadlet);
1180 });
1181
1182 deserialize_midi_tx_low_offset(&mut params.midi_tx_low_offset, &quads);
1183 params.midi_tx_enable = quads[2] & Q2_MIDI_TX_SUPPRESS_MASK == 0;
1184
1185 deserialize_clock_config(&mut params.clk, &quads);
1186 deserialize_analog_input_config(&mut params.analog_in, &quads);
1187
1188 let pair = (
1189 quads[0] & Q0_LINE_OUT_LEVEL_MASK,
1190 quads[1] & Q1_LINE_OUT_LEVEL_MASK,
1191 );
1192 params.line_out_level = match pair {
1193 (Q0_LINE_OUT_LEVEL_HIGH_FLAG, Q1_LINE_OUT_LEVEL_HIGH_FLAG) => LineOutNominalLevel::High,
1194 (Q0_LINE_OUT_LEVEL_CON_FLAG, Q1_LINE_OUT_LEVEL_CON_FLAG) => {
1195 LineOutNominalLevel::Consumer
1196 }
1197 (Q0_LINE_OUT_LEVEL_PRO_FLAG, Q1_LINE_OUT_LEVEL_PRO_FLAG) => {
1198 LineOutNominalLevel::Professional
1199 }
1200 _ => unreachable!(),
1201 };
1202
1203 params.hp_out_level = match quads[0] & Q0_HP_OUT_LEVEL_MASK {
1204 Q0_HP_OUT_LEVEL_HIGH_FLAG => LineOutNominalLevel::High,
1205 Q0_HP_OUT_LEVEL_CON_FLAG => LineOutNominalLevel::Consumer,
1206 Q0_HP_OUT_LEVEL_PRO_FLAG => LineOutNominalLevel::Professional,
1207 _ => unreachable!(),
1208 };
1209
1210 params.spdif_in.iface = if quads[2] & Q2_SPDIF_IN_IFACE_OPT_MASK > 0 {
1211 SpdifIface::Optical
1212 } else {
1213 SpdifIface::Coaxial
1214 };
1215 params.spdif_in.use_preemble = quads[2] & Q2_SPDIF_IN_USE_PREEMBLE > 0;
1216
1217 params.spdif_out.format = if quads[2] & Q2_SPDIF_OUT_FMT_PRO_MASK > 0 {
1218 SpdifFormat::Professional
1219 } else {
1220 SpdifFormat::Consumer
1221 };
1222 params.spdif_out.emphasis = quads[2] & Q2_SPDIF_OUT_EMPHASIS_MASK > 0;
1223 params.spdif_out.non_audio = quads[2] & Q2_SPDIF_OUT_NON_AUDIO_MASK > 0;
1224
1225 params.opt_out_signal = if quads[2] & Q2_OPT_OUT_SIGNAL_MASK > 0 {
1226 OpticalOutputSignal::Spdif
1227 } else {
1228 OpticalOutputSignal::Adat
1229 };
1230
1231 params.word_out_single = quads[2] & Q2_WORD_OUT_SINGLE_SPEED_MASK > 0;
1232 params.continue_at_errors = quads[2] & Q2_CONTINUE_AT_ERRORS > 0;
1233 }
1234}
1235
1236impl RmeFfWhollyUpdatableParamsOperation<Ff400Config> for Ff400Protocol {
1237 fn update_wholly(
1238 req: &mut FwReq,
1239 node: &mut FwNode,
1240 params: &Ff400Config,
1241 timeout_ms: u32,
1242 ) -> Result<(), Error> {
1243 write_config::<Ff400Protocol, Ff400Config>(req, node, CFG_OFFSET, params, timeout_ms)
1244 }
1245}
1246
1247#[cfg(test)]
1248mod test {
1249 use super::*;
1250
1251 #[test]
1252 fn lock_status_serdes() {
1253 let orig = Ff400ClkLockStatus {
1254 adat: true,
1255 spdif: true,
1256 word_clock: true,
1257 };
1258 let mut quads = [0; Ff400ClkLockStatus::QUADLET_COUNT];
1259 serialize_lock_status(&orig, &mut quads);
1260 let mut target = Ff400ClkLockStatus::default();
1261 deserialize_lock_status(&mut target, &quads);
1262
1263 assert_eq!(target, orig);
1264 }
1265
1266 #[test]
1267 fn sync_status_serdes() {
1268 let orig = Ff400ClkSyncStatus {
1269 adat: true,
1270 spdif: true,
1271 word_clock: true,
1272 };
1273 let mut quads = [0; Ff400ClkSyncStatus::QUADLET_COUNT];
1274 serialize_sync_status(&orig, &mut quads);
1275 let mut target = Ff400ClkSyncStatus::default();
1276 deserialize_sync_status(&mut target, &quads);
1277
1278 assert_eq!(target, orig);
1279 }
1280
1281 #[test]
1282 fn status_serdes() {
1283 let orig = Ff400Status {
1284 spdif_in: SpdifInput {
1285 iface: SpdifIface::Optical,
1286 use_preemble: false,
1288 },
1289 spdif_out: FormerSpdifOutput {
1290 format: SpdifFormat::Professional,
1291 emphasis: true,
1292 non_audio: false,
1294 },
1295 opt_out_signal: OpticalOutputSignal::Spdif,
1296 word_out_single: true,
1297 spdif_rate: Some(ClkNominalRate::R96000),
1298 active_clk_src: Ff400ClkSrc::Ltc,
1299 external_clk_rate: Some(ClkNominalRate::R88200),
1300 configured_clk_src: Ff400ClkSrc::Spdif,
1301 configured_clk_rate: ClkNominalRate::R176400,
1302 ..Default::default()
1303 };
1304 let raw = Ff400Protocol::serialize_offsets(&orig);
1305 let mut target = Ff400Status::default();
1306 Ff400Protocol::deserialize_offsets(&mut target, &raw);
1307
1308 assert_eq!(target, orig);
1309 }
1310
1311 #[test]
1312 fn clock_config_serdes() {
1313 let orig = Ff400ClkConfig {
1314 primary_src: Ff400ClkSrc::Adat,
1315 avail_rate_44100: true,
1316 avail_rate_48000: true,
1317 avail_rate_double: true,
1318 avail_rate_quadruple: true,
1319 };
1320 let mut quads = [0; Ff400ClkConfig::QUADLET_COUNT];
1321 serialize_clock_config(&orig, &mut quads);
1322 let mut target = Ff400ClkConfig::default();
1323 deserialize_clock_config(&mut target, &quads);
1324
1325 assert_eq!(target, orig);
1326 }
1327
1328 #[test]
1329 fn analog_input_config_serdes() {
1330 let orig = Ff400AnalogInConfig {
1331 line_level: FormerLineInNominalLevel::Professional,
1332 phantom_powering: [true; 2],
1333 insts: [true; 2],
1334 pad: [true; 2],
1335 };
1336 let mut quads = [0; Ff400AnalogInConfig::QUADLET_COUNT];
1337 serialize_analog_input_config(&orig, &mut quads);
1338 let mut target = Ff400AnalogInConfig::default();
1339 deserialize_analog_input_config(&mut target, &quads);
1340
1341 assert_eq!(target, orig);
1342 }
1343
1344 #[test]
1345 fn midi_tx_low_offset_serdes() {
1346 let orig = Ff400MidiTxLowOffset::A0180;
1347 let mut quads = [0; Ff400MidiTxLowOffset::QUADLET_COUNT];
1348 serialize_midi_tx_low_offset(&orig, &mut quads);
1349 let mut target = Ff400MidiTxLowOffset::default();
1350 deserialize_midi_tx_low_offset(&mut target, &quads);
1351
1352 assert_eq!(target, orig);
1353 }
1354
1355 #[test]
1356 fn config_serdes() {
1357 let orig = Ff400Config::default();
1358 let raw = Ff400Protocol::serialize_offsets(&orig);
1359 let mut target = Ff400Config::default();
1360 Ff400Protocol::deserialize_offsets(&mut target, &raw);
1361
1362 assert_eq!(target, orig);
1363 }
1364
1365 #[test]
1366 fn message_parse() {
1367 let expected = Ff400InputGainStatus {
1368 mic: [0, 0],
1369 line: [0x1c, 0x1c],
1370 };
1371 let msg = KNOB_IS_SIGNAL_LEVEL_FLAG
1372 | KNOB_IS_STEREO_PAIRED_FLAG
1373 | KNOB_TARGET_LINE_INPUT_PAIR_1
1374 | ((0x1c << KNOB_SIGNAL_LEVEL_SHIFT) & KNOB_SIGNAL_LEVEL_MASK);
1375 let mut target = Ff400InputGainStatus::default();
1376 let res = Ff400Protocol::parse_message(&mut target, msg);
1377 assert_eq!(res, true);
1378 assert_eq!(target, expected);
1379
1380 let expected = FormerOutputVolumeState(vec![
1381 0,
1382 0,
1383 0,
1384 0,
1385 0,
1386 0, 0,
1388 amp_to_vol_value(0x32), 0,
1390 0, 0,
1392 0,
1393 0,
1394 0,
1395 0,
1396 0,
1397 0,
1398 0, ]);
1400 let msg = KNOB_IS_SIGNAL_LEVEL_FLAG
1401 | KNOB_IS_RIGHT_CHANNEL_FLAG
1402 | KNOB_TARGET_HP_OUTPUT_PAIR
1403 | ((0x32 << KNOB_SIGNAL_LEVEL_SHIFT) & KNOB_SIGNAL_LEVEL_MASK);
1404 let mut target = FormerOutputVolumeState(vec![0; 18]);
1405 let res = Ff400Protocol::parse_message(&mut target, msg);
1406 assert_eq!(res, true);
1407 assert_eq!(target, expected);
1408
1409 let expected = Ff400MidiMessage {
1410 port: 1,
1411 byte: 0x5a,
1412 };
1413 let msg = MIDI_PORT_1_FLAG | ((0x5a << MIDI_PORT_1_BYTE_SHIFT) & MIDI_PORT_1_BYTE_MASK);
1414 let mut target = Ff400MidiMessage::default();
1415 let res = Ff400Protocol::parse_message(&mut target, msg);
1416 assert_eq!(res, true);
1417 assert_eq!(target, expected);
1418 }
1419}