1pub mod liquids56;
10pub mod spro14;
11pub mod spro24;
12pub mod spro24dsp;
13pub mod spro26;
14pub mod spro40;
15
16use super::{
17 tcat::{extension::*, *},
18 *,
19};
20
21pub trait SaffireproSwNoticeOperation: TcatExtensionOperation {
23 const SW_NOTICE_OFFSET: usize;
24
25 fn write_sw_notice(
26 req: &FwReq,
27 node: &FwNode,
28 sections: &ExtensionSections,
29 notice: u32,
30 timeout_ms: u32,
31 ) -> Result<(), Error> {
32 let mut raw = [0; 4];
33 serialize_u32(¬ice, &mut raw);
34 Self::write_extension(
35 req,
36 node,
37 §ions.application,
38 Self::SW_NOTICE_OFFSET,
39 &mut raw,
40 timeout_ms,
41 )
42 }
43}
44
45#[derive(Default, Debug, Clone, PartialEq, Eq)]
48pub struct OutGroupState {
49 pub vols: Vec<i8>,
51
52 pub vol_mutes: Vec<bool>,
54
55 pub vol_hwctls: Vec<bool>,
57
58 pub mute_enabled: bool,
60
61 pub mute_hwctls: Vec<bool>,
63
64 pub dim_enabled: bool,
66
67 pub dim_hwctls: Vec<bool>,
69
70 pub hw_knob_value: i8,
73}
74
75const OUT_GROUP_STATE_SIZE: usize = 0x50;
76
77const VOL_MIN: i8 = 0;
78const VOL_MAX: i8 = i8::MAX;
79
80fn serialize_out_group_state(state: &OutGroupState, raw: &mut [u8]) -> Result<(), String> {
81 assert!(raw.len() >= OUT_GROUP_STATE_SIZE);
82
83 serialize_bool(&state.mute_enabled, &mut raw[0x00..0x04]);
84 serialize_bool(&state.dim_enabled, &mut raw[0x04..0x08]);
85
86 (0..(state.vols.len() / 2)).for_each(|i| {
87 let mut val = 0u32;
88 state.vols[(i * 2)..(i * 2 + 2)]
89 .iter()
90 .enumerate()
91 .for_each(|(j, &vol)| {
92 let v = VOL_MAX - vol;
94 val |= (v as u32) << (8 * j);
95 });
96 let pos = 0x08 + i * 4;
97 serialize_u32(&val, &mut raw[pos..(pos + 4)]);
98 });
99
100 (0..(state.vol_hwctls.len() / 2)).for_each(|i| {
101 let mut val = 0u32;
102 let idx = i * 2;
103
104 state.vol_hwctls[idx..(idx + 2)]
105 .iter()
106 .enumerate()
107 .filter(|(_, &vol_hwctl)| vol_hwctl)
108 .for_each(|(i, _)| val |= 1 << i);
109
110 state.vol_mutes[idx..(idx + 2)]
111 .iter()
112 .enumerate()
113 .filter(|(_, &vol_mute)| vol_mute)
114 .for_each(|(i, _)| val |= 1 << (i + 2));
115
116 let pos = 0x1c + i * 4;
117 serialize_u32(&val, &mut raw[pos..(pos + 4)]);
118 });
119
120 let mut val = 0u32;
121 state
122 .dim_hwctls
123 .iter()
124 .enumerate()
125 .filter(|(_, &assigned)| assigned)
126 .for_each(|(i, _)| val |= 1 << (i + 10));
127 state
128 .mute_hwctls
129 .iter()
130 .enumerate()
131 .filter(|(_, &assigned)| assigned)
132 .for_each(|(i, _)| val |= 1 << i);
133 serialize_u32(&val, &mut raw[0x30..0x34]);
134
135 serialize_i8(&state.hw_knob_value, &mut raw[0x48..0x4c]);
136
137 Ok(())
138}
139
140fn deserialize_out_group_state(state: &mut OutGroupState, raw: &[u8]) -> Result<(), String> {
141 assert!(raw.len() >= OUT_GROUP_STATE_SIZE);
142
143 deserialize_bool(&mut state.mute_enabled, &raw[0x00..0x04]);
144 deserialize_bool(&mut state.dim_enabled, &raw[0x04..0x08]);
145
146 (0..(state.vols.len() / 2)).for_each(|i| {
147 let pos = 0x08 + i * 4;
148 let mut val = 0u32;
149 deserialize_u32(&mut val, &raw[pos..(pos + 4)]);
150 state.vols[(i * 2)..(i * 2 + 2)]
151 .iter_mut()
152 .enumerate()
153 .for_each(|(j, vol)| {
154 let v = ((val >> (j * 8)) & 0xff) as i8;
155 *vol = VOL_MAX - v;
157 });
158 });
159
160 (0..(state.vol_hwctls.len() / 2)).for_each(|i| {
161 let pos = 0x1c + i * 4;
162 let mut val = 0u32;
163 deserialize_u32(&mut val, &raw[pos..(pos + 4)]);
164 let idx = i * 2;
165
166 state.vol_hwctls[idx..(idx + 2)]
167 .iter_mut()
168 .enumerate()
169 .for_each(|(i, vol_hwctl)| *vol_hwctl = val & (1 << i) > 0);
170
171 state.vol_mutes[idx..(idx + 2)]
172 .iter_mut()
173 .enumerate()
174 .for_each(|(i, vol_mute)| {
175 *vol_mute = val & (1 << (i + 2)) > 0;
176 });
177 });
178
179 let mut val = 0u32;
180 deserialize_u32(&mut val, &raw[0x30..0x34]);
181 state
182 .dim_hwctls
183 .iter_mut()
184 .enumerate()
185 .for_each(|(i, assign)| *assign = val & (1 << (i + 10)) > 0);
186 state
187 .mute_hwctls
188 .iter_mut()
189 .enumerate()
190 .for_each(|(i, assign)| *assign = val & (1 << i) > 0);
191
192 deserialize_i8(&mut state.hw_knob_value, &raw[0x48..0x4c]);
193
194 Ok(())
195}
196
197const NOTIFY_DIM_MUTE_CHANGE: u32 = 0x00200000;
198const NOTIFY_VOL_CHANGE: u32 = 0x00400000;
199
200pub trait SaffireproOutGroupSpecification: SaffireproSwNoticeOperation {
202 const OUT_GROUP_STATE_OFFSET: usize;
204
205 const ENTRY_COUNT: usize;
207
208 const HAS_VOL_HWCTL: bool;
210
211 const SRC_NOTICE: u32;
213
214 const DIM_MUTE_NOTICE: u32;
216
217 const VOL_MIN: i8 = VOL_MIN;
219
220 const VOL_MAX: i8 = VOL_MAX;
222
223 fn create_out_group_state() -> OutGroupState {
225 OutGroupState {
226 vols: vec![0; Self::ENTRY_COUNT],
227 vol_mutes: vec![false; Self::ENTRY_COUNT],
228 vol_hwctls: vec![false; Self::ENTRY_COUNT],
229 mute_enabled: false,
230 mute_hwctls: vec![false; Self::ENTRY_COUNT],
231 dim_enabled: false,
232 dim_hwctls: vec![false; Self::ENTRY_COUNT],
233 hw_knob_value: 0,
234 }
235 }
236}
237
238impl<O: TcatExtensionOperation + SaffireproOutGroupSpecification>
239 TcatExtensionSectionParamsOperation<OutGroupState> for O
240{
241 fn cache_extension_whole_params(
242 req: &FwReq,
243 node: &FwNode,
244 sections: &ExtensionSections,
245 _: &ExtensionCaps,
246 params: &mut OutGroupState,
247 timeout_ms: u32,
248 ) -> Result<(), Error> {
249 let mut raw = vec![0u8; OUT_GROUP_STATE_SIZE];
250 Self::read_extension(
251 req,
252 node,
253 §ions.application,
254 O::OUT_GROUP_STATE_OFFSET,
255 &mut raw,
256 timeout_ms,
257 )?;
258 deserialize_out_group_state(params, &raw)
259 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
260 }
261}
262
263impl<O: TcatExtensionOperation + SaffireproOutGroupSpecification>
264 TcatExtensionSectionPartialMutableParamsOperation<OutGroupState> for O
265{
266 fn update_extension_partial_params(
267 req: &FwReq,
268 node: &FwNode,
269 sections: &ExtensionSections,
270 _: &ExtensionCaps,
271 params: &OutGroupState,
272 prev: &mut OutGroupState,
273 timeout_ms: u32,
274 ) -> Result<(), Error> {
275 let mut new = vec![0u8; OUT_GROUP_STATE_SIZE];
276 serialize_out_group_state(params, &mut new)
277 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
278
279 let mut old = vec![0u8; OUT_GROUP_STATE_SIZE];
280 serialize_out_group_state(prev, &mut old)
281 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
282
283 (0..OUT_GROUP_STATE_SIZE).step_by(4).try_for_each(|pos| {
284 if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
285 Self::write_extension(
286 req,
287 node,
288 §ions.application,
289 Self::OUT_GROUP_STATE_OFFSET + pos,
290 &mut new[pos..(pos + 4)],
291 timeout_ms,
292 )
293 } else {
294 Ok(())
295 }
296 })?;
297
298 if new[..0x08] != old[..0x08] {
299 Self::write_sw_notice(req, node, sections, Self::DIM_MUTE_NOTICE, timeout_ms)?;
300 }
301
302 if new[0x08..0x34] != old[0x08..0x34] {
303 Self::write_sw_notice(req, node, sections, Self::SRC_NOTICE, timeout_ms)?;
304 }
305
306 deserialize_out_group_state(prev, &new)
307 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
308 }
309}
310
311impl<O: TcatExtensionOperation + SaffireproOutGroupSpecification>
312 TcatExtensionSectionNotifiedParamsOperation<OutGroupState> for O
313{
314 fn cache_extension_notified_params(
315 req: &FwReq,
316 node: &FwNode,
317 sections: &ExtensionSections,
318 caps: &ExtensionCaps,
319 params: &mut OutGroupState,
320 msg: u32,
321 timeout_ms: u32,
322 ) -> Result<(), Error> {
323 if msg & (NOTIFY_DIM_MUTE_CHANGE | NOTIFY_VOL_CHANGE) > 0 {
324 Self::cache_extension_whole_params(req, node, sections, caps, params, timeout_ms)?;
325
326 if msg & NOTIFY_VOL_CHANGE > 0 {
327 let vol_hwctls = params.vol_hwctls.clone();
328 let hw_knob_value = params.hw_knob_value;
329 params
330 .vols
331 .iter_mut()
332 .zip(vol_hwctls)
333 .filter(|(_, vol_hwctl)| *vol_hwctl)
334 .for_each(|(vol, _)| *vol = hw_knob_value);
335 }
336 }
337
338 Ok(())
339 }
340}
341
342#[derive(Debug, Copy, Clone, PartialEq, Eq)]
344pub enum SaffireproMicInputLevel {
345 Line,
347 Instrument,
349}
350
351impl Default for SaffireproMicInputLevel {
352 fn default() -> Self {
353 Self::Line
354 }
355}
356
357#[derive(Debug, Copy, Clone, PartialEq, Eq)]
359pub enum SaffireproLineInputLevel {
360 Low,
362 High,
364}
365
366impl Default for SaffireproLineInputLevel {
367 fn default() -> Self {
368 Self::Low
369 }
370}
371
372#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
374pub struct SaffireproInputParams {
375 pub mic_levels: [SaffireproMicInputLevel; 2],
377 pub line_levels: [SaffireproLineInputLevel; 2],
379}
380
381const MIC_INPUT_LEVEL_INSTRUMENT_FLAG: u16 = 0x0002;
382const LINE_INPUT_LEVEL_HIGH_FLAG: u16 = 0x0001;
383
384const INPUT_PARAMS_SIZE: usize = 8;
385
386fn serialize_input_params(params: &SaffireproInputParams, raw: &mut [u8]) -> Result<(), String> {
387 assert!(raw.len() >= INPUT_PARAMS_SIZE);
388
389 let mut val = 0u32;
390 params.mic_levels.iter().enumerate().for_each(|(i, level)| {
391 if *level == SaffireproMicInputLevel::Instrument {
392 val |= (MIC_INPUT_LEVEL_INSTRUMENT_FLAG as u32) << (16 * i);
393 };
394 });
395 serialize_u32(&val, &mut raw[..4]);
396
397 let mut val = 0u32;
398 params
399 .line_levels
400 .iter()
401 .enumerate()
402 .for_each(|(i, level)| {
403 if *level == SaffireproLineInputLevel::High {
404 val |= (LINE_INPUT_LEVEL_HIGH_FLAG as u32) << (16 * i);
405 }
406 });
407 serialize_u32(&val, &mut raw[4..8]);
408
409 Ok(())
410}
411
412fn deserialize_input_params(params: &mut SaffireproInputParams, raw: &[u8]) -> Result<(), String> {
413 assert!(raw.len() >= INPUT_PARAMS_SIZE);
414
415 let mut val = 0u32;
416
417 deserialize_u32(&mut val, &raw[..4]);
418 params
419 .mic_levels
420 .iter_mut()
421 .enumerate()
422 .for_each(|(i, level)| {
423 let flag = (val >> (16 * i)) as u16;
424 *level = if flag & MIC_INPUT_LEVEL_INSTRUMENT_FLAG > 0 {
425 SaffireproMicInputLevel::Instrument
426 } else {
427 SaffireproMicInputLevel::Line
428 };
429 });
430
431 deserialize_u32(&mut val, &raw[4..8]);
432 params
433 .line_levels
434 .iter_mut()
435 .enumerate()
436 .for_each(|(i, level)| {
437 let flag = (val >> (16 * i)) as u16;
438 *level = if flag & LINE_INPUT_LEVEL_HIGH_FLAG > 0 {
439 SaffireproLineInputLevel::High
440 } else {
441 SaffireproLineInputLevel::Low
442 };
443 });
444
445 Ok(())
446}
447
448pub trait SaffireproInputSpecification: SaffireproSwNoticeOperation {
450 const INPUT_PARAMS_OFFSET: usize;
451
452 const SW_NOTICE: u32 = 0x00000004;
453
454 const MIC_INPUT_COUNT: usize = 2;
455 const LINE_INPUT_COUNT: usize = 2;
456}
457
458impl<O: TcatExtensionOperation + SaffireproInputSpecification>
459 TcatExtensionSectionParamsOperation<SaffireproInputParams> for O
460{
461 fn cache_extension_whole_params(
462 req: &FwReq,
463 node: &FwNode,
464 sections: &ExtensionSections,
465 _: &ExtensionCaps,
466 params: &mut SaffireproInputParams,
467 timeout_ms: u32,
468 ) -> Result<(), Error> {
469 let mut raw = vec![0u8; INPUT_PARAMS_SIZE];
470 Self::read_extension(
471 req,
472 node,
473 §ions.application,
474 O::INPUT_PARAMS_OFFSET,
475 &mut raw,
476 timeout_ms,
477 )?;
478 deserialize_input_params(params, &raw)
479 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
480 }
481}
482
483impl<O: TcatExtensionOperation + SaffireproInputSpecification>
484 TcatExtensionSectionPartialMutableParamsOperation<SaffireproInputParams> for O
485{
486 fn update_extension_partial_params(
487 req: &FwReq,
488 node: &FwNode,
489 sections: &ExtensionSections,
490 _: &ExtensionCaps,
491 params: &SaffireproInputParams,
492 prev: &mut SaffireproInputParams,
493 timeout_ms: u32,
494 ) -> Result<(), Error> {
495 let mut new = vec![0u8; INPUT_PARAMS_SIZE];
496 serialize_input_params(params, &mut new)
497 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
498
499 let mut old = vec![0u8; INPUT_PARAMS_SIZE];
500 serialize_input_params(prev, &mut old)
501 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
502
503 (0..INPUT_PARAMS_SIZE).step_by(4).try_for_each(|pos| {
504 if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
505 Self::write_extension(
506 req,
507 node,
508 §ions.application,
509 Self::INPUT_PARAMS_OFFSET + pos,
510 &mut new[pos..(pos + 4)],
511 timeout_ms,
512 )
513 } else {
514 Ok(())
515 }
516 })?;
517
518 if new != old {
519 Self::write_sw_notice(req, node, sections, Self::SW_NOTICE, timeout_ms)?;
520 }
521
522 deserialize_input_params(prev, &new)
523 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
524 }
525}
526
527#[derive(Debug, Copy, Clone, PartialEq, Eq)]
529pub enum OpticalOutIfaceMode {
530 Adat,
532 Spdif,
534 AesEbu,
536}
537
538impl Default for OpticalOutIfaceMode {
539 fn default() -> Self {
540 Self::Adat
541 }
542}
543
544impl OpticalOutIfaceMode {
545 const ADAT_VALUE: u32 = 0x00000000;
546 const SPDIF_VALUE: u32 = 0x00000001;
547 const AESEBU_VALUE: u32 = 0x00000002;
548}
549
550const OPTICAL_OUT_IFACE_MODE_MASK: u32 = 0x00000003;
551const MIC_AMP_TRANSFORMER_CH0_FLAG: u32 = 0x00000008;
552const MIC_AMP_TRANSFORMER_CH1_FLAG: u32 = 0x00000010;
553
554fn serialize_optical_out_iface_mode(
555 mode: &OpticalOutIfaceMode,
556 aesebu_is_supported: bool,
557 raw: &mut [u8],
558) -> Result<(), String> {
559 assert!(raw.len() >= 4);
560
561 let mut val = 0u32;
562 deserialize_u32(&mut val, raw);
563 val &= !OPTICAL_OUT_IFACE_MODE_MASK;
564
565 let v = match mode {
566 OpticalOutIfaceMode::Adat => OpticalOutIfaceMode::ADAT_VALUE,
567 OpticalOutIfaceMode::Spdif => OpticalOutIfaceMode::SPDIF_VALUE,
568 OpticalOutIfaceMode::AesEbu => {
569 if aesebu_is_supported {
570 OpticalOutIfaceMode::AESEBU_VALUE
571 } else {
572 Err(format!("AES/EBU is not supported but selected"))?
573 }
574 }
575 };
576 val |= v;
577
578 serialize_u32(&val, raw);
579
580 Ok(())
581}
582
583fn deserialize_optical_out_iface_mode(
584 mode: &mut OpticalOutIfaceMode,
585 aesebu_is_supported: bool,
586 raw: &[u8],
587) -> Result<(), String> {
588 assert!(raw.len() >= 4);
589
590 let mut val = 0u32;
591 deserialize_u32(&mut val, raw);
592 val &= OPTICAL_OUT_IFACE_MODE_MASK;
593
594 *mode = match val {
595 OpticalOutIfaceMode::ADAT_VALUE => OpticalOutIfaceMode::Adat,
596 OpticalOutIfaceMode::SPDIF_VALUE => OpticalOutIfaceMode::Spdif,
597 OpticalOutIfaceMode::AESEBU_VALUE => {
598 if aesebu_is_supported {
599 OpticalOutIfaceMode::AesEbu
600 } else {
601 Err(format!("AES/EBU is not supported but detected"))?
602 }
603 }
604 _ => Err(format!(
605 "Optical interface mode not found for value {}",
606 val
607 ))?,
608 };
609
610 Ok(())
611}
612
613const MIC_AMP_TRANSFORMER_FLAGS: [u32; 2] =
614 [MIC_AMP_TRANSFORMER_CH0_FLAG, MIC_AMP_TRANSFORMER_CH1_FLAG];
615
616fn serialize_mic_amp_transformers(transformers: &[bool; 2], raw: &mut [u8]) -> Result<(), String> {
617 assert!(raw.len() >= 4);
618
619 let mut val = 0u32;
620 deserialize_u32(&mut val, raw);
621 val &= !(MIC_AMP_TRANSFORMER_CH0_FLAG | MIC_AMP_TRANSFORMER_CH1_FLAG);
622
623 transformers
624 .iter()
625 .zip(MIC_AMP_TRANSFORMER_FLAGS)
626 .filter(|(&enabled, _)| enabled)
627 .for_each(|(_, flag)| val |= flag);
628
629 serialize_u32(&val, raw);
630
631 Ok(())
632}
633
634fn deserialize_mic_amp_transformers(
635 transformers: &mut [bool; 2],
636 raw: &[u8],
637) -> Result<(), String> {
638 assert!(raw.len() >= 4);
639
640 let mut val = 0u32;
641 deserialize_u32(&mut val, raw);
642 val &= MIC_AMP_TRANSFORMER_CH0_FLAG | MIC_AMP_TRANSFORMER_CH1_FLAG;
643
644 transformers
645 .iter_mut()
646 .zip(MIC_AMP_TRANSFORMER_FLAGS)
647 .for_each(|(enabled, flag)| *enabled = val & flag > 0);
648
649 Ok(())
650}
651
652#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
654pub struct SaffireproIoParams {
655 pub analog_out_0_1_pad: bool,
657 pub opt_out_iface_mode: OpticalOutIfaceMode,
659 pub mic_amp_transformers: [bool; 2],
661}
662
663const IO_PARAMS_OFFSET: usize = 0x40;
664const IO_PARAMS_SIZE: usize = 0x20;
665
666fn serialize_io_params(
667 params: &SaffireproIoParams,
668 aesebu_is_supported: bool,
669 raw: &mut [u8],
670) -> Result<(), String> {
671 assert!(raw.len() >= IO_PARAMS_SIZE);
672
673 serialize_bool(¶ms.analog_out_0_1_pad, &mut raw[..4]);
674 serialize_optical_out_iface_mode(
675 ¶ms.opt_out_iface_mode,
676 aesebu_is_supported,
677 &mut raw[0x1c..0x20],
678 )?;
679 serialize_mic_amp_transformers(¶ms.mic_amp_transformers, &mut raw[0x1c..0x20])?;
680
681 Ok(())
682}
683
684fn deserialize_io_params(
685 params: &mut SaffireproIoParams,
686 aesebu_is_supported: bool,
687 raw: &[u8],
688) -> Result<(), String> {
689 assert!(raw.len() >= IO_PARAMS_SIZE);
690
691 deserialize_bool(&mut params.analog_out_0_1_pad, &raw[..4]);
692 deserialize_optical_out_iface_mode(
693 &mut params.opt_out_iface_mode,
694 aesebu_is_supported,
695 &raw[0x1c..0x20],
696 )?;
697 deserialize_mic_amp_transformers(&mut params.mic_amp_transformers, &raw[0x1c..0x20])?;
698
699 Ok(())
700}
701
702pub trait SaffireproIoParamsSpecification: SaffireproSwNoticeOperation {
704 const AESEBU_IS_SUPPORTED: bool;
706
707 const MIC_PREAMP_TRANSFORMER_IS_SUPPORTED: bool;
709}
710
711impl<O: TcatExtensionOperation + SaffireproIoParamsSpecification>
712 TcatExtensionSectionParamsOperation<SaffireproIoParams> for O
713{
714 fn cache_extension_whole_params(
715 req: &FwReq,
716 node: &FwNode,
717 sections: &ExtensionSections,
718 _: &ExtensionCaps,
719 params: &mut SaffireproIoParams,
720 timeout_ms: u32,
721 ) -> Result<(), Error> {
722 let mut raw = vec![0u8; IO_PARAMS_SIZE];
723 Self::read_extension(
724 req,
725 node,
726 §ions.application,
727 IO_PARAMS_OFFSET,
728 &mut raw,
729 timeout_ms,
730 )?;
731 deserialize_io_params(params, O::AESEBU_IS_SUPPORTED, &raw)
732 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
733 }
734}
735
736impl<O: TcatExtensionOperation + SaffireproIoParamsSpecification>
737 TcatExtensionSectionPartialMutableParamsOperation<SaffireproIoParams> for O
738{
739 fn update_extension_partial_params(
740 req: &FwReq,
741 node: &FwNode,
742 sections: &ExtensionSections,
743 _: &ExtensionCaps,
744 params: &SaffireproIoParams,
745 prev: &mut SaffireproIoParams,
746 timeout_ms: u32,
747 ) -> Result<(), Error> {
748 let mut new = vec![0u8; IO_PARAMS_SIZE];
749 serialize_io_params(params, Self::AESEBU_IS_SUPPORTED, &mut new)
750 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
751
752 let mut old = vec![0u8; IO_PARAMS_SIZE];
753 serialize_io_params(prev, Self::AESEBU_IS_SUPPORTED, &mut old)
754 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))?;
755
756 (0..IO_PARAMS_SIZE).step_by(4).try_for_each(|pos| {
757 if new[pos..(pos + 4)] != old[pos..(pos + 4)] {
758 Self::write_extension(
759 req,
760 node,
761 §ions.application,
762 IO_PARAMS_OFFSET + pos,
763 &mut new[pos..(pos + 4)],
764 timeout_ms,
765 )
766 } else {
767 Ok(())
768 }
769 })?;
770
771 if new[..0x04] != old[..0x04] {
772 Self::write_sw_notice(req, node, sections, 0x00000003, timeout_ms)?;
773 }
774
775 if new[0x1c..0x20] != old[0x1c..0x20] {
776 let mut n = 0u32;
777 deserialize_u32(&mut n, &new[0x1c..0x20]);
778 let mut o = 0u32;
779 deserialize_u32(&mut o, &old[0x1c..0x20]);
780
781 if (n ^ o) & 0x00000003 > 0 {
782 Self::write_sw_notice(req, node, sections, 0x00000004, timeout_ms)?;
783 }
784
785 if (n ^ o) & 0x00000008 > 0 {
786 Self::write_sw_notice(req, node, sections, 0x00000008, timeout_ms)?;
787 }
788
789 if (n ^ o) & 0x00000010 > 0 {
790 Self::write_sw_notice(req, node, sections, 0x00000010, timeout_ms)?;
791 }
792 }
793
794 deserialize_io_params(prev, Self::AESEBU_IS_SUPPORTED, &new)
795 .map_err(|cause| Error::new(ProtocolExtensionError::Appl, &cause))
796 }
797}
798
799#[cfg(test)]
800mod test {
801 use super::*;
802
803 #[test]
804 fn input_params_serdes() {
805 let params = SaffireproInputParams {
806 mic_levels: [
807 SaffireproMicInputLevel::Instrument,
808 SaffireproMicInputLevel::Line,
809 ],
810 line_levels: [
811 SaffireproLineInputLevel::High,
812 SaffireproLineInputLevel::Low,
813 ],
814 };
815
816 let mut raw = vec![0u8; INPUT_PARAMS_SIZE];
817 serialize_input_params(¶ms, &mut raw).unwrap();
818
819 let mut p = SaffireproInputParams::default();
820 deserialize_input_params(&mut p, &raw).unwrap();
821
822 assert_eq!(params, p);
823 }
824
825 #[test]
826 fn out_group_serdes() {
827 let params = OutGroupState {
828 vols: vec![0, 1, 2, 3, 4, 5],
829 vol_mutes: vec![false, true, true, false, false, true],
830 vol_hwctls: vec![true, false, false, true, true, false],
831 mute_enabled: true,
832 mute_hwctls: vec![true, true, true, false, false, false],
833 dim_enabled: true,
834 dim_hwctls: vec![false, false, false, true, true, true],
835 hw_knob_value: 33,
836 };
837
838 let mut raw = vec![0; 0x100];
839 serialize_out_group_state(¶ms, &mut raw).unwrap();
840
841 let mut p = OutGroupState {
842 vols: vec![Default::default(); 6],
843 vol_mutes: vec![Default::default(); 6],
844 vol_hwctls: vec![Default::default(); 6],
845 mute_enabled: true,
846 mute_hwctls: vec![Default::default(); 6],
847 dim_enabled: true,
848 dim_hwctls: vec![Default::default(); 6],
849 hw_knob_value: 33,
850 };
851 deserialize_out_group_state(&mut p, &raw).unwrap();
852
853 assert_eq!(params, p);
854 }
855
856 #[test]
857 fn io_params_serdes() {
858 let params = SaffireproIoParams {
859 analog_out_0_1_pad: true,
860 opt_out_iface_mode: OpticalOutIfaceMode::Spdif,
861 mic_amp_transformers: [true, false],
862 };
863
864 let mut raw = vec![0u8; IO_PARAMS_SIZE];
865 serialize_io_params(¶ms, false, &mut raw).unwrap();
866
867 let mut p = SaffireproIoParams::default();
868 deserialize_io_params(&mut p, false, &raw).unwrap();
869
870 assert_eq!(params, p);
871 }
872}