1#![doc = include_str!("../README.md")]
5
6pub mod command_dsp;
7pub mod config_rom;
8pub mod register_dsp;
9pub mod version_1;
10pub mod version_2;
11pub mod version_3;
12
13use {
14 glib::{Error, FileError},
15 hinawa::{
16 prelude::{FwNodeExt, FwReqExtManual},
17 FwNode, FwReq, FwTcode,
18 },
19 std::{thread, time},
20};
21
22pub trait MotuWhollyCacheableParamsOperation<T> {
24 fn cache_wholly(
26 req: &mut FwReq,
27 node: &mut FwNode,
28 params: &mut T,
29 timeout_ms: u32,
30 ) -> Result<(), Error>;
31}
32
33pub trait MotuWhollyUpdatableParamsOperation<T> {
35 fn update_wholly(
37 req: &mut FwReq,
38 node: &mut FwNode,
39 params: &T,
40 timeout_ms: u32,
41 ) -> Result<(), Error>;
42}
43
44pub trait MotuPartiallyUpdatableParamsOperation<T> {
46 fn update_partially(
48 req: &mut FwReq,
49 node: &mut FwNode,
50 params: &mut T,
51 updates: T,
52 timeout_ms: u32,
53 ) -> Result<(), Error>;
54}
55
56const BASE_OFFSET: u64 = 0xfffff0000000;
57const OFFSET_CLK: u32 = 0x0b14;
58const OFFSET_PORT: u32 = 0x0c04;
59const OFFSET_CLK_DISPLAY: u32 = 0x0c60;
60
61fn read_quad(req: &FwReq, node: &mut FwNode, offset: u32, timeout_ms: u32) -> Result<u32, Error> {
62 let mut frame = [0; 4];
63 req.transaction_sync(
64 node,
65 FwTcode::ReadQuadletRequest,
66 BASE_OFFSET + offset as u64,
67 4,
68 &mut frame,
69 timeout_ms,
70 )
71 .map(|_| u32::from_be_bytes(frame))
72}
73
74fn write_quad(
79 req: &FwReq,
80 node: &mut FwNode,
81 offset: u32,
82 quad: u32,
83 timeout_ms: u32,
84) -> Result<(), Error> {
85 let mut frame = [0; 4];
86 frame.copy_from_slice(&quad.to_be_bytes());
87 req.transaction_sync(
88 node,
89 FwTcode::WriteQuadletRequest,
90 BASE_OFFSET + offset as u64,
91 4,
92 &mut frame,
93 timeout_ms,
94 )
95 .or_else(|err| {
96 thread::sleep(time::Duration::from_millis(BUSY_DURATION));
98 req.transaction_sync(
99 node,
100 FwTcode::WriteQuadletRequest,
101 BASE_OFFSET + offset as u64,
102 4,
103 &mut frame,
104 timeout_ms,
105 )
106 .and_then(|_| {
107 if u32::from_be_bytes(frame) == quad {
108 Ok(())
109 } else {
110 Err(err)
111 }
112 })
113 })
114}
115
116fn serialize_flag<T: Copy + Eq>(
117 flag: &T,
118 quad: &mut u32,
119 mask: u32,
120 shift: usize,
121 flags: &[T],
122 vals: &[u8],
123 label: &str,
124) -> Result<(), Error> {
125 flags
126 .iter()
127 .zip(vals)
128 .find(|(f, _)| flag.eq(f))
129 .ok_or_else(|| {
130 let label = format!(
131 "Invalid argument for {}, 0x{:08x}, 0x{:08x}",
132 label, *quad, mask
133 );
134 Error::new(FileError::Io, &label)
135 })
136 .map(|(_, &val)| {
137 *quad &= !mask;
138 *quad |= (val as u32) << shift;
139 })
140}
141
142fn deserialize_flag<T: Copy + Eq>(
143 flag: &mut T,
144 quad: &u32,
145 mask: u32,
146 shift: usize,
147 flags: &[T],
148 vals: &[u8],
149 label: &str,
150) -> Result<(), Error> {
151 let val = ((*quad & mask) >> shift) as u8;
152 flags
153 .iter()
154 .zip(vals)
155 .find(|(_, v)| val.eq(v))
156 .ok_or_else(|| {
157 let label = format!(
158 "Invalid value for {}, 0x{:08x}, 0x{:08x}",
159 label, quad, mask
160 );
161 Error::new(FileError::Io, &label)
162 })
163 .map(|(&f, _)| *flag = f)
164}
165
166#[derive(Debug, Copy, Clone, PartialEq, Eq)]
168pub enum ClkRate {
169 R44100,
171 R48000,
173 R88200,
175 R96000,
177 R176400,
179 R192000,
181}
182
183impl Default for ClkRate {
184 fn default() -> Self {
185 Self::R44100
186 }
187}
188
189const BUSY_DURATION: u64 = 150;
190const DISPLAY_CHARS: usize = 4 * 4;
191
192#[derive(Default, Debug, Clone, PartialEq, Eq)]
194pub struct ClockNameDisplayParameters(pub String);
195
196pub trait MotuClockNameDisplaySpecification {}
198
199impl<O> MotuWhollyUpdatableParamsOperation<ClockNameDisplayParameters> for O
200where
201 O: MotuClockNameDisplaySpecification,
202{
203 fn update_wholly(
204 req: &mut FwReq,
205 node: &mut FwNode,
206 params: &ClockNameDisplayParameters,
207 timeout_ms: u32,
208 ) -> Result<(), Error> {
209 let mut chars = [0x20; DISPLAY_CHARS];
210 chars
211 .iter_mut()
212 .zip(params.0.bytes())
213 .for_each(|(c, l)| *c = l);
214
215 (0..(DISPLAY_CHARS / 4)).try_for_each(|i| {
216 let mut frame = [0; 4];
217 frame.copy_from_slice(&chars[(i * 4)..(i * 4 + 4)]);
218 frame.reverse();
219 let quad = u32::from_ne_bytes(frame);
220 let offset = OFFSET_CLK_DISPLAY + 4 * i as u32;
221 write_quad(req, node, offset, quad, timeout_ms)
222 })
223 }
224}
225
226pub trait MotuPortAssignSpecification {
228 const ASSIGN_PORT_TARGETS: &'static [TargetPort];
229 const ASSIGN_PORT_VALS: &'static [u8];
230}
231
232const PORT_PHONE_LABEL: &str = "phone-assign";
233const PORT_PHONE_MASK: u32 = 0x0000000f;
234const PORT_PHONE_SHIFT: usize = 0;
235
236#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
238pub struct PhoneAssignParameters(pub TargetPort);
239
240impl<O> MotuWhollyCacheableParamsOperation<PhoneAssignParameters> for O
241where
242 O: MotuPortAssignSpecification,
243{
244 fn cache_wholly(
245 req: &mut FwReq,
246 node: &mut FwNode,
247 params: &mut PhoneAssignParameters,
248 timeout_ms: u32,
249 ) -> Result<(), Error> {
250 let quad = read_quad(req, node, OFFSET_PORT, timeout_ms)?;
251
252 deserialize_flag(
253 &mut params.0,
254 &quad,
255 PORT_PHONE_MASK,
256 PORT_PHONE_SHIFT,
257 Self::ASSIGN_PORT_TARGETS,
258 Self::ASSIGN_PORT_VALS,
259 PORT_PHONE_LABEL,
260 )
261 }
262}
263
264impl<O> MotuWhollyUpdatableParamsOperation<PhoneAssignParameters> for O
265where
266 O: MotuPortAssignSpecification,
267{
268 fn update_wholly(
269 req: &mut FwReq,
270 node: &mut FwNode,
271 params: &PhoneAssignParameters,
272 timeout_ms: u32,
273 ) -> Result<(), Error> {
274 let mut quad = read_quad(req, node, OFFSET_PORT, timeout_ms)?;
275
276 serialize_flag(
277 ¶ms.0,
278 &mut quad,
279 PORT_PHONE_MASK,
280 PORT_PHONE_SHIFT,
281 Self::ASSIGN_PORT_TARGETS,
282 Self::ASSIGN_PORT_VALS,
283 PORT_PHONE_LABEL,
284 )?;
285
286 write_quad(req, node, OFFSET_PORT, quad, timeout_ms)
287 }
288}
289
290#[derive(Debug, Copy, Clone, PartialEq, Eq)]
292pub enum WordClkSpeedMode {
293 ForceLowRate,
295 FollowSystemClk,
297}
298
299impl Default for WordClkSpeedMode {
300 fn default() -> Self {
301 Self::FollowSystemClk
302 }
303}
304
305const WORD_OUT_LABEL: &str = "word-out";
306const WORD_OUT_MASK: u32 = 0x08000000;
307const WORD_OUT_SHIFT: usize = 27;
308
309pub trait MotuWordClockOutputSpecification {
311 const WORD_CLOCK_OUTPUT_SPEED_MODES: &'static [WordClkSpeedMode] = &[
312 WordClkSpeedMode::ForceLowRate,
313 WordClkSpeedMode::FollowSystemClk,
314 ];
315}
316
317const WORD_CLOCK_OUTPUT_SPEED_MODE_VALS: &[u8] = &[0x00, 0x01];
318
319impl<O: MotuWordClockOutputSpecification> MotuWhollyCacheableParamsOperation<WordClkSpeedMode>
320 for O
321{
322 fn cache_wholly(
323 req: &mut FwReq,
324 node: &mut FwNode,
325 params: &mut WordClkSpeedMode,
326 timeout_ms: u32,
327 ) -> Result<(), Error> {
328 let quad = read_quad(req, node, OFFSET_CLK, timeout_ms)?;
329
330 deserialize_flag(
331 params,
332 &quad,
333 WORD_OUT_MASK,
334 WORD_OUT_SHIFT,
335 Self::WORD_CLOCK_OUTPUT_SPEED_MODES,
336 WORD_CLOCK_OUTPUT_SPEED_MODE_VALS,
337 WORD_OUT_LABEL,
338 )
339 }
340}
341
342impl<O: MotuWordClockOutputSpecification> MotuWhollyUpdatableParamsOperation<WordClkSpeedMode>
343 for O
344{
345 fn update_wholly(
346 req: &mut FwReq,
347 node: &mut FwNode,
348 params: &WordClkSpeedMode,
349 timeout_ms: u32,
350 ) -> Result<(), Error> {
351 let mut quad = read_quad(req, node, OFFSET_CLK, timeout_ms)?;
352
353 serialize_flag(
354 params,
355 &mut quad,
356 WORD_OUT_MASK,
357 WORD_OUT_SHIFT,
358 Self::WORD_CLOCK_OUTPUT_SPEED_MODES,
359 WORD_CLOCK_OUTPUT_SPEED_MODE_VALS,
360 WORD_OUT_LABEL,
361 )?;
362
363 write_quad(req, node, OFFSET_CLK, quad, timeout_ms)
364 }
365}
366
367#[derive(Debug, Copy, Clone, PartialEq, Eq)]
369pub enum AesebuRateConvertMode {
370 None,
372 InputToSystem,
374 OutputDependsInput,
376 OutputDoubleSystem,
378}
379
380impl Default for AesebuRateConvertMode {
381 fn default() -> Self {
382 Self::None
383 }
384}
385
386const AESEBU_RATE_CONVERT_LABEL: &str = "aesebu-rate-convert";
387
388pub trait MotuAesebuRateConvertSpecification {
390 const AESEBU_RATE_CONVERT_MASK: u32;
391 const AESEBU_RATE_CONVERT_SHIFT: usize;
392
393 const AESEBU_RATE_CONVERT_MODES: &'static [AesebuRateConvertMode] = &[
394 AesebuRateConvertMode::None,
395 AesebuRateConvertMode::InputToSystem,
396 AesebuRateConvertMode::OutputDependsInput,
397 AesebuRateConvertMode::OutputDoubleSystem,
398 ];
399}
400
401const AESEBU_RATE_CONVERT_VALS: &[u8] = &[0x00, 0x01, 0x02, 0x03];
402
403impl<O> MotuWhollyCacheableParamsOperation<AesebuRateConvertMode> for O
404where
405 O: MotuAesebuRateConvertSpecification,
406{
407 fn cache_wholly(
408 req: &mut FwReq,
409 node: &mut FwNode,
410 params: &mut AesebuRateConvertMode,
411 timeout_ms: u32,
412 ) -> Result<(), Error> {
413 let quad = read_quad(req, node, OFFSET_CLK, timeout_ms)?;
414
415 deserialize_flag(
416 params,
417 &quad,
418 Self::AESEBU_RATE_CONVERT_MASK,
419 Self::AESEBU_RATE_CONVERT_SHIFT,
420 Self::AESEBU_RATE_CONVERT_MODES,
421 AESEBU_RATE_CONVERT_VALS,
422 AESEBU_RATE_CONVERT_LABEL,
423 )
424 }
425}
426
427impl<O> MotuWhollyUpdatableParamsOperation<AesebuRateConvertMode> for O
428where
429 O: MotuAesebuRateConvertSpecification,
430{
431 fn update_wholly(
432 req: &mut FwReq,
433 node: &mut FwNode,
434 params: &AesebuRateConvertMode,
435 timeout_ms: u32,
436 ) -> Result<(), Error> {
437 let mut quad = read_quad(req, node, OFFSET_CLK, timeout_ms)?;
438
439 serialize_flag(
440 params,
441 &mut quad,
442 Self::AESEBU_RATE_CONVERT_MASK,
443 Self::AESEBU_RATE_CONVERT_SHIFT,
444 Self::AESEBU_RATE_CONVERT_MODES,
445 AESEBU_RATE_CONVERT_VALS,
446 AESEBU_RATE_CONVERT_LABEL,
447 )?;
448
449 write_quad(req, node, OFFSET_CLK, quad, timeout_ms)
450 }
451}
452
453#[derive(Debug, Copy, Clone, PartialEq, Eq)]
455pub enum LevelMetersHoldTimeMode {
456 Off,
458 Sec2,
460 Sec4,
462 Sec10,
464 Sec60,
466 Sec300,
468 Sec480,
470 Infinite,
472}
473
474impl Default for LevelMetersHoldTimeMode {
475 fn default() -> Self {
476 Self::Off
477 }
478}
479
480#[derive(Debug, Copy, Clone, PartialEq, Eq)]
482pub enum LevelMetersProgrammableMode {
483 AnalogOutput,
485 AdatAInput,
487 AdatAOutput,
489 AdatBInput,
491 AdatBOutput,
493 AesEbuInputOutput,
495}
496
497impl Default for LevelMetersProgrammableMode {
498 fn default() -> Self {
499 Self::AnalogOutput
500 }
501}
502
503#[derive(Debug, Copy, Clone, PartialEq, Eq)]
505pub enum LevelMetersAesebuMode {
506 Input,
508 Output,
510}
511
512impl Default for LevelMetersAesebuMode {
513 fn default() -> Self {
514 Self::Input
515 }
516}
517
518#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
520pub struct LevelMetersParameters {
521 pub peak_hold_time: LevelMetersHoldTimeMode,
523 pub clip_hold_time: LevelMetersHoldTimeMode,
525 pub aesebu_mode: LevelMetersAesebuMode,
527 pub programmable_mode: LevelMetersProgrammableMode,
529}
530
531const LEVEL_METERS_OFFSET: u32 = 0x0b24;
532
533const LEVEL_METERS_PEAK_HOLD_TIME_MASK: u32 = 0x00003800;
534const LEVEL_METERS_PEAK_HOLD_TIME_SHIFT: usize = 11;
535
536const LEVEL_METERS_CLIP_HOLD_TIME_MASK: u32 = 0x00000700;
537const LEVEL_METERS_CLIP_HOLD_TIME_SHIFT: usize = 8;
538
539const LEVEL_METERS_HOLD_TIME_VALS: &[u8] = &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
540
541const LEVEL_METERS_AESEBU_MASK: u32 = 0x00000004;
542const LEVEL_METERS_AESEBU_SHIFT: usize = 2;
543
544const LEVEL_METERS_AESEBU_VALS: &[u8] = &[0x00, 0x01];
545
546const LEVEL_METERS_PROGRAMMABLE_MASK: u32 = 0x00000003;
547const LEVEL_METERS_PROGRAMMABLE_SHIFT: usize = 0;
548const LEVEL_METERS_PROGRAMMABLE_VALS: &[u8] = &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
549
550const LEVEL_METERS_PEAK_HOLD_TIME_LABEL: &str = "level-meters-peak-hold-time";
551const LEVEL_METERS_CLIP_HOLD_TIME_LABEL: &str = "level-meters-clip-hold-time";
552const LEVEL_METERS_PROGRAMMABLE_LABEL: &str = "level-meters-programmable";
553const LEVEL_METERS_AESEBU_LABEL: &str = "level-meters-aesebu";
554
555pub trait MotuLevelMetersSpecification {
557 const LEVEL_METERS_HOLD_TIME_MODES: &'static [LevelMetersHoldTimeMode] = &[
558 LevelMetersHoldTimeMode::Off,
559 LevelMetersHoldTimeMode::Sec2,
560 LevelMetersHoldTimeMode::Sec4,
561 LevelMetersHoldTimeMode::Sec10,
562 LevelMetersHoldTimeMode::Sec60,
563 LevelMetersHoldTimeMode::Sec300,
564 LevelMetersHoldTimeMode::Sec480,
565 LevelMetersHoldTimeMode::Infinite,
566 ];
567
568 const LEVEL_METERS_AESEBU_MODES: &'static [LevelMetersAesebuMode] =
569 &[LevelMetersAesebuMode::Output, LevelMetersAesebuMode::Input];
570
571 const LEVEL_METERS_PROGRAMMABLE_MODES: &'static [LevelMetersProgrammableMode];
572}
573
574impl<O> MotuWhollyCacheableParamsOperation<LevelMetersParameters> for O
575where
576 O: MotuLevelMetersSpecification,
577{
578 fn cache_wholly(
579 req: &mut FwReq,
580 node: &mut FwNode,
581 params: &mut LevelMetersParameters,
582 timeout_ms: u32,
583 ) -> Result<(), Error> {
584 let quad = read_quad(req, node, LEVEL_METERS_OFFSET, timeout_ms)?;
585
586 deserialize_flag(
587 &mut params.peak_hold_time,
588 &quad,
589 LEVEL_METERS_PEAK_HOLD_TIME_MASK,
590 LEVEL_METERS_PEAK_HOLD_TIME_SHIFT,
591 Self::LEVEL_METERS_HOLD_TIME_MODES,
592 LEVEL_METERS_HOLD_TIME_VALS,
593 LEVEL_METERS_PEAK_HOLD_TIME_LABEL,
594 )?;
595
596 deserialize_flag(
597 &mut params.clip_hold_time,
598 &quad,
599 LEVEL_METERS_CLIP_HOLD_TIME_MASK,
600 LEVEL_METERS_CLIP_HOLD_TIME_SHIFT,
601 Self::LEVEL_METERS_HOLD_TIME_MODES,
602 LEVEL_METERS_HOLD_TIME_VALS,
603 LEVEL_METERS_CLIP_HOLD_TIME_LABEL,
604 )?;
605
606 deserialize_flag(
607 &mut params.aesebu_mode,
608 &quad,
609 LEVEL_METERS_AESEBU_MASK,
610 LEVEL_METERS_AESEBU_SHIFT,
611 Self::LEVEL_METERS_AESEBU_MODES,
612 LEVEL_METERS_AESEBU_VALS,
613 LEVEL_METERS_AESEBU_LABEL,
614 )?;
615
616 deserialize_flag(
617 &mut params.programmable_mode,
618 &quad,
619 LEVEL_METERS_PROGRAMMABLE_MASK,
620 LEVEL_METERS_PROGRAMMABLE_SHIFT,
621 Self::LEVEL_METERS_PROGRAMMABLE_MODES,
622 LEVEL_METERS_PROGRAMMABLE_VALS,
623 LEVEL_METERS_PROGRAMMABLE_LABEL,
624 )?;
625
626 Ok(())
627 }
628}
629
630impl<O> MotuWhollyUpdatableParamsOperation<LevelMetersParameters> for O
631where
632 O: MotuLevelMetersSpecification,
633{
634 fn update_wholly(
635 req: &mut FwReq,
636 node: &mut FwNode,
637 params: &LevelMetersParameters,
638 timeout_ms: u32,
639 ) -> Result<(), Error> {
640 let mut quad = read_quad(req, node, LEVEL_METERS_OFFSET, timeout_ms)?;
641
642 serialize_flag(
643 ¶ms.peak_hold_time,
644 &mut quad,
645 LEVEL_METERS_PEAK_HOLD_TIME_MASK,
646 LEVEL_METERS_PEAK_HOLD_TIME_SHIFT,
647 Self::LEVEL_METERS_HOLD_TIME_MODES,
648 LEVEL_METERS_HOLD_TIME_VALS,
649 LEVEL_METERS_PEAK_HOLD_TIME_LABEL,
650 )?;
651
652 serialize_flag(
653 ¶ms.clip_hold_time,
654 &mut quad,
655 LEVEL_METERS_CLIP_HOLD_TIME_MASK,
656 LEVEL_METERS_CLIP_HOLD_TIME_SHIFT,
657 Self::LEVEL_METERS_HOLD_TIME_MODES,
658 LEVEL_METERS_HOLD_TIME_VALS,
659 LEVEL_METERS_CLIP_HOLD_TIME_LABEL,
660 )?;
661
662 serialize_flag(
663 ¶ms.aesebu_mode,
664 &mut quad,
665 LEVEL_METERS_AESEBU_MASK,
666 LEVEL_METERS_AESEBU_SHIFT,
667 Self::LEVEL_METERS_AESEBU_MODES,
668 LEVEL_METERS_AESEBU_VALS,
669 LEVEL_METERS_AESEBU_LABEL,
670 )?;
671
672 serialize_flag(
673 ¶ms.programmable_mode,
674 &mut quad,
675 LEVEL_METERS_PROGRAMMABLE_MASK,
676 LEVEL_METERS_PROGRAMMABLE_SHIFT,
677 Self::LEVEL_METERS_PROGRAMMABLE_MODES,
678 LEVEL_METERS_PROGRAMMABLE_VALS,
679 LEVEL_METERS_PROGRAMMABLE_LABEL,
680 )?;
681
682 write_quad(req, node, LEVEL_METERS_OFFSET, quad, timeout_ms)
683 }
684}
685
686#[derive(Debug, Copy, Clone, PartialEq, Eq)]
688pub enum TargetPort {
689 Disabled,
690 AnalogPair(usize),
691 AesEbuPair,
692 PhonePair,
693 MainPair,
694 SpdifPair,
695 AdatPair(usize),
696 Analog6Pairs,
697 Analog8Pairs,
698 OpticalAPair(usize),
699 OpticalBPair(usize),
700 Analog(usize),
701 AesEbu(usize),
702 Phone(usize),
703 Main(usize),
704 Spdif(usize),
705 Adat(usize),
706 OpticalA(usize),
707 OpticalB(usize),
708}
709
710impl Default for TargetPort {
711 fn default() -> Self {
712 Self::Disabled
713 }
714}
715
716#[derive(Debug, Copy, Clone, PartialEq, Eq)]
718pub enum NominalSignalLevel {
719 Consumer,
721 Professional,
723}
724
725impl Default for NominalSignalLevel {
726 fn default() -> Self {
727 Self::Consumer
728 }
729}
730
731#[cfg(test)]
732mod test {
733 use super::*;
734
735 #[test]
736 fn flag_serdes() {
737 const TEST0_MASK: u32 = 0x000000ff;
738 const TEST0_SHIFT: usize = 0;
739 const TEST0_LABEL: &'static str = "test0";
740 const TEST0_TARGETS: &[TargetPort] = &[
741 TargetPort::AesEbuPair,
742 TargetPort::PhonePair,
743 TargetPort::MainPair,
744 TargetPort::SpdifPair,
745 ];
746 const TEST0_VALS: &[u8] = &[0x01, 0x02, 0x04, 0x08];
747
748 const TEST1_MASK: u32 = 0x0000ff00;
749 const TEST1_SHIFT: usize = 8;
750 const TEST1_LABEL: &'static str = "test1";
751 const TEST1_TARGETS: &[LevelMetersHoldTimeMode] = &[
752 LevelMetersHoldTimeMode::Off,
753 LevelMetersHoldTimeMode::Sec2,
754 LevelMetersHoldTimeMode::Sec4,
755 LevelMetersHoldTimeMode::Sec10,
756 LevelMetersHoldTimeMode::Sec60,
757 LevelMetersHoldTimeMode::Sec300,
758 LevelMetersHoldTimeMode::Sec480,
759 ];
760 const TEST1_VALS: &[u8] = &[0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
761
762 let orig0 = TargetPort::SpdifPair;
763 let mut quad = 0;
764 serialize_flag(
765 &orig0,
766 &mut quad,
767 TEST0_MASK,
768 TEST0_SHIFT,
769 TEST0_TARGETS,
770 TEST0_VALS,
771 TEST0_LABEL,
772 )
773 .unwrap();
774 assert_eq!(quad, 0x00000008);
775
776 let orig1 = LevelMetersHoldTimeMode::Off;
777 serialize_flag(
778 &orig1,
779 &mut quad,
780 TEST1_MASK,
781 TEST1_SHIFT,
782 TEST1_TARGETS,
783 TEST1_VALS,
784 TEST1_LABEL,
785 )
786 .unwrap();
787 assert_eq!(quad, 0x00000108);
788
789 let mut target0 = TargetPort::default();
790 deserialize_flag(
791 &mut target0,
792 &quad,
793 TEST0_MASK,
794 TEST0_SHIFT,
795 TEST0_TARGETS,
796 TEST0_VALS,
797 TEST0_LABEL,
798 )
799 .unwrap();
800 assert_eq!(target0, orig0);
801
802 let mut target1 = LevelMetersHoldTimeMode::default();
803 deserialize_flag(
804 &mut target1,
805 &quad,
806 TEST1_MASK,
807 TEST1_SHIFT,
808 TEST1_TARGETS,
809 TEST1_VALS,
810 TEST1_LABEL,
811 )
812 .unwrap();
813 assert_eq!(target1, orig1);
814 }
815}