firewire_dice_protocols/weiss/
avc.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2024 Takashi Sakamoto
3
4//! Protocol specific to Weiss Engineering AV/C models.
5//!
6//! The module includes structure, enumeration, and trait and its implementation for protocol
7//! defined by Weiss Engineering.
8//!
9//! MAN301 includes two units in the root directory of its configuration ROM. The first unit
10//! expresses AV/C protocol, and the second unit expresses TCAT protocol.
11//!
12//! ```text
13//! spdif-opt-input-1/2  ---+
14//! spdif-coax-input-1/2 --(or)--> digital-input-1/2 -----------------> stream-output-1/2
15//! aesebu-xlr-input-1/2 ---+
16//!
17//! stream-input-1/2 --------------------------+----------------------> spdif-coax-output-1/2
18//!                                            +----------------------> aesebu-xlr-output-1/2
19//!                                            +--analog-output-1/2 --> analog-xlr-output-1/2
20//!                                                       +-----------> analog-coax-output-1/2
21//! ```
22
23use {
24    super::*,
25    crate::{tcelectronic::*, *},
26    glib::{Error, FileError, IsA},
27    hinawa::{
28        prelude::{FwFcpExt, FwFcpExtManual},
29        FwFcp, FwNode,
30    },
31    ta1394_avc_general::{general::*, *},
32};
33
34/// Protocol implementation specific to MAN301.
35#[derive(Default, Debug)]
36pub struct WeissMan301Protocol;
37
38impl TcatOperation for WeissMan301Protocol {}
39
40// clock caps: 44100 48000 88200 96000 176400 192000
41// clock source names: AES/EBU (XLR)\S/PDIF (RCA)\S/PDIF (TOS)\Unused\Unused\Unused\Unused\Word Clock (BNC)\Unused\Unused\Unused\Unused\Internal\\
42impl TcatGlobalSectionSpecification for WeissMan301Protocol {}
43
44/// The implementation of AV/C transaction.
45#[derive(Default, Debug)]
46pub struct WeissAvc(FwFcp);
47
48impl Ta1394Avc<Error> for WeissAvc {
49    fn transaction(&self, command_frame: &[u8], timeout_ms: u32) -> Result<Vec<u8>, Error> {
50        let mut resp = vec![0; Self::FRAME_SIZE];
51        self.0
52            .avc_transaction(&command_frame, &mut resp, timeout_ms)
53            .map(|len| {
54                resp.truncate(len);
55                resp
56            })
57    }
58}
59
60fn from_avc_err(err: Ta1394AvcError<Error>) -> Error {
61    match err {
62        Ta1394AvcError::CmdBuild(cause) => Error::new(FileError::Inval, &cause.to_string()),
63        Ta1394AvcError::CommunicationFailure(cause) => cause,
64        Ta1394AvcError::RespParse(cause) => Error::new(FileError::Io, &cause.to_string()),
65    }
66}
67
68impl WeissAvc {
69    /// Bind FCP protocol to the given node for AV/C operation.
70    pub fn bind(&self, node: &impl IsA<FwNode>) -> Result<(), Error> {
71        self.0.bind(node)
72    }
73
74    /// Request AV/C control operation and wait for response.
75    pub fn control<O: AvcOp + AvcControl>(
76        &self,
77        addr: &AvcAddr,
78        op: &mut O,
79        timeout_ms: u32,
80    ) -> Result<(), Error> {
81        Ta1394Avc::<Error>::control(self, addr, op, timeout_ms).map_err(|err| from_avc_err(err))
82    }
83
84    /// Request AV/C status operation and wait for response.
85    pub fn status<O: AvcOp + AvcStatus>(
86        &self,
87        addr: &AvcAddr,
88        op: &mut O,
89        timeout_ms: u32,
90    ) -> Result<(), Error> {
91        Ta1394Avc::<Error>::status(self, addr, op, timeout_ms).map_err(|err| from_avc_err(err))
92    }
93}
94
95/// The content of command to operate parameter.
96#[derive(Debug)]
97pub struct WeissAvcParamCmd {
98    /// The numeric identifier of parameter.
99    pub numeric_id: u32,
100    /// The value of parameter.
101    pub value: u32,
102    /// For future use.
103    pub reserved: [u32; 4],
104    op: TcAvcCmd,
105}
106
107impl Default for WeissAvcParamCmd {
108    fn default() -> Self {
109        let mut op = TcAvcCmd::new(&WEISS_OUI);
110        op.class_id = 1;
111        op.sequence_id = u8::MAX;
112        op.command_id = 0x8002;
113        Self {
114            numeric_id: u32::MAX,
115            value: u32::MAX,
116            reserved: [u32::MAX; 4],
117            op: TcAvcCmd::new(&WEISS_OUI),
118        }
119    }
120}
121
122impl AvcOp for WeissAvcParamCmd {
123    const OPCODE: u8 = VendorDependent::OPCODE;
124}
125
126fn build_param_command_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), AvcCmdBuildError> {
127    cmd.op.arguments.resize(24, u8::MAX);
128    serialize_u32(&cmd.numeric_id, &mut cmd.op.arguments[..4]);
129    (0..4).for_each(|i| {
130        let pos = 8 + i * 4;
131        serialize_u32(&cmd.reserved[i], &mut cmd.op.arguments[pos..(pos + 4)]);
132    });
133    Ok(())
134}
135
136fn build_param_command_control_data(cmd: &mut WeissAvcParamCmd) -> Result<(), AvcCmdBuildError> {
137    cmd.op.arguments.resize(24, u8::MIN);
138    serialize_u32(&cmd.numeric_id, &mut cmd.op.arguments[..4]);
139    serialize_u32(&cmd.value, &mut cmd.op.arguments[4..8]);
140    (0..4).for_each(|i| {
141        let pos = 8 + i * 4;
142        serialize_u32(&cmd.reserved[i], &mut cmd.op.arguments[pos..(pos + 4)]);
143    });
144    Ok(())
145}
146
147fn parse_param_command_response_data(cmd: &mut WeissAvcParamCmd) -> Result<(), AvcRespParseError> {
148    if cmd.op.arguments.len() < 24 {
149        Err(AvcRespParseError::TooShortResp(24))?
150    }
151
152    deserialize_u32(&mut cmd.numeric_id, &cmd.op.arguments[..4]);
153    deserialize_u32(&mut cmd.value, &cmd.op.arguments[4..8]);
154    (0..4).for_each(|i| {
155        let pos = 8 + i * 4;
156        deserialize_u32(&mut cmd.reserved[i], &cmd.op.arguments[pos..(pos + 4)]);
157    });
158
159    Ok(())
160}
161
162impl AvcStatus for WeissAvcParamCmd {
163    fn build_operands(&mut self, addr: &AvcAddr) -> Result<Vec<u8>, AvcCmdBuildError> {
164        build_param_command_status_data(self)?;
165        AvcStatus::build_operands(&mut self.op, addr)
166    }
167
168    fn parse_operands(&mut self, addr: &AvcAddr, operands: &[u8]) -> Result<(), AvcRespParseError> {
169        AvcStatus::parse_operands(&mut self.op, addr, operands)?;
170        parse_param_command_response_data(self)
171    }
172}
173
174impl AvcControl for WeissAvcParamCmd {
175    fn build_operands(&mut self, addr: &AvcAddr) -> Result<Vec<u8>, AvcCmdBuildError> {
176        build_param_command_control_data(self)?;
177        AvcControl::build_operands(&mut self.op, addr)
178    }
179
180    fn parse_operands(&mut self, addr: &AvcAddr, operands: &[u8]) -> Result<(), AvcRespParseError> {
181        AvcControl::parse_operands(&mut self.op, addr, operands)?;
182        parse_param_command_response_data(self)
183    }
184}
185
186impl WeissMan301Protocol {
187    const PARAM_ID_DIGITAL_CAPTURE_SOURCE: u32 = 0;
188    const PARAM_ID_DIGITAL_OUTPUT_MODE: u32 = 1;
189    const PARAM_ID_WORD_CLOCK_OUTPUT_HALF_RATE: u32 = 2;
190    const PARAM_ID_AESEBU_XLR_OUTPUT_MUTE: u32 = 3;
191    const PARAM_ID_SPDIF_COAXIAL_OUTPUT_MUTE: u32 = 4;
192    const PARAM_ID_ANALOG_OUTPUT_POLARITY_INVERSION: u32 = 5;
193    const PARAM_ID_ANALOG_OUTPUT_FILTER_TYPE: u32 = 6;
194    const PARAM_ID_ANALOG_OUTPUT_MUTE: u32 = 7;
195    const PARAM_ID_ANALOG_OUTPUT_LEVEL: u32 = 8;
196}
197
198/// Convert between parameter and command.
199pub trait WeissAvcParamConvert<T> {
200    /// Build data for status command.
201    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error>;
202    /// Build data for control command.
203    fn build_control_data(param: &T, cmd: &mut WeissAvcParamCmd) -> Result<(), Error>;
204    /// Parse data for response.
205    fn parse_response_data(param: &mut T, cmd: &WeissAvcParamCmd) -> Result<(), Error>;
206}
207
208/// The source of digital input captured for output packet stream.
209#[derive(Debug, Copy, Clone, PartialEq, Eq)]
210pub enum WeissAvcDigitalCaptureSource {
211    /// AES/EBU signal in XLR input interface.
212    AesebuXlr,
213    /// S/PDIF signal in coaxial input interface.
214    SpdifCoaxial,
215    /// S/PDIF signal in optical input interface.
216    SpdifOptical,
217}
218
219impl Default for WeissAvcDigitalCaptureSource {
220    fn default() -> Self {
221        Self::AesebuXlr
222    }
223}
224
225impl WeissAvcParamConvert<WeissAvcDigitalCaptureSource> for WeissMan301Protocol {
226    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
227        cmd.numeric_id = Self::PARAM_ID_DIGITAL_CAPTURE_SOURCE;
228        cmd.value = u32::MAX;
229        Ok(())
230    }
231
232    fn build_control_data(
233        param: &WeissAvcDigitalCaptureSource,
234        cmd: &mut WeissAvcParamCmd,
235    ) -> Result<(), Error> {
236        cmd.numeric_id = Self::PARAM_ID_DIGITAL_CAPTURE_SOURCE;
237        cmd.value = match param {
238            WeissAvcDigitalCaptureSource::SpdifOptical => 2,
239            WeissAvcDigitalCaptureSource::SpdifCoaxial => 1,
240            WeissAvcDigitalCaptureSource::AesebuXlr => 0,
241        };
242        Ok(())
243    }
244
245    fn parse_response_data(
246        param: &mut WeissAvcDigitalCaptureSource,
247        cmd: &WeissAvcParamCmd,
248    ) -> Result<(), Error> {
249        assert_eq!(cmd.numeric_id, Self::PARAM_ID_DIGITAL_CAPTURE_SOURCE);
250        *param = match cmd.value {
251            2 => WeissAvcDigitalCaptureSource::SpdifOptical,
252            1 => WeissAvcDigitalCaptureSource::SpdifCoaxial,
253            _ => WeissAvcDigitalCaptureSource::AesebuXlr,
254        };
255        Ok(())
256    }
257}
258
259/// The mode of AES/EBU XLR output and S/PDIF coaxial output. When enabled, it is in "dual wire"
260/// mode at 176.4/192.0 kHz; i.e. the former is for the left audio channel, and the latter is for
261/// right audio channel.
262#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
263pub struct WeissAvcDigitalOutputMode(pub bool);
264
265impl WeissAvcParamConvert<WeissAvcDigitalOutputMode> for WeissMan301Protocol {
266    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
267        cmd.numeric_id = Self::PARAM_ID_DIGITAL_OUTPUT_MODE;
268        cmd.value = u32::MAX;
269        Ok(())
270    }
271
272    fn build_control_data(
273        param: &WeissAvcDigitalOutputMode,
274        cmd: &mut WeissAvcParamCmd,
275    ) -> Result<(), Error> {
276        cmd.numeric_id = Self::PARAM_ID_DIGITAL_OUTPUT_MODE;
277        cmd.value = if param.0 { 1 } else { 0 };
278        Ok(())
279    }
280
281    fn parse_response_data(
282        param: &mut WeissAvcDigitalOutputMode,
283        cmd: &WeissAvcParamCmd,
284    ) -> Result<(), Error> {
285        assert_eq!(cmd.numeric_id, Self::PARAM_ID_DIGITAL_OUTPUT_MODE);
286        param.0 = cmd.value > 0;
287        Ok(())
288    }
289}
290
291/// The mode of word clock at dual wire mode. When enabled, the output of BNC for word clock is at
292/// half of sampling rate.
293#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
294pub struct WeissAvcWordClockOutputHalfRate(pub bool);
295
296impl WeissAvcParamConvert<WeissAvcWordClockOutputHalfRate> for WeissMan301Protocol {
297    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
298        cmd.numeric_id = Self::PARAM_ID_WORD_CLOCK_OUTPUT_HALF_RATE;
299        cmd.value = u32::MAX;
300        Ok(())
301    }
302
303    fn build_control_data(
304        param: &WeissAvcWordClockOutputHalfRate,
305        cmd: &mut WeissAvcParamCmd,
306    ) -> Result<(), Error> {
307        cmd.numeric_id = Self::PARAM_ID_WORD_CLOCK_OUTPUT_HALF_RATE;
308        cmd.value = if param.0 { 1 } else { 0 };
309        Ok(())
310    }
311
312    fn parse_response_data(
313        param: &mut WeissAvcWordClockOutputHalfRate,
314        cmd: &WeissAvcParamCmd,
315    ) -> Result<(), Error> {
316        assert_eq!(cmd.numeric_id, Self::PARAM_ID_WORD_CLOCK_OUTPUT_HALF_RATE);
317        param.0 = cmd.value > 0;
318        Ok(())
319    }
320}
321
322/// Mute XLR output.
323#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
324pub struct WeissAvcAesebuXlrOutputMute(pub bool);
325
326impl WeissAvcParamConvert<WeissAvcAesebuXlrOutputMute> for WeissMan301Protocol {
327    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
328        cmd.numeric_id = Self::PARAM_ID_AESEBU_XLR_OUTPUT_MUTE;
329        cmd.value = u32::MAX;
330        Ok(())
331    }
332
333    fn build_control_data(
334        param: &WeissAvcAesebuXlrOutputMute,
335        cmd: &mut WeissAvcParamCmd,
336    ) -> Result<(), Error> {
337        cmd.numeric_id = Self::PARAM_ID_AESEBU_XLR_OUTPUT_MUTE;
338        cmd.value = if param.0 { 0 } else { 1 };
339        Ok(())
340    }
341
342    fn parse_response_data(
343        param: &mut WeissAvcAesebuXlrOutputMute,
344        cmd: &WeissAvcParamCmd,
345    ) -> Result<(), Error> {
346        assert_eq!(cmd.numeric_id, Self::PARAM_ID_AESEBU_XLR_OUTPUT_MUTE);
347        param.0 = cmd.value == 0;
348        Ok(())
349    }
350}
351
352/// Mute coaxial output.
353#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
354pub struct WeissAvcSpdifCoaxialOutputMute(pub bool);
355
356impl WeissAvcParamConvert<WeissAvcSpdifCoaxialOutputMute> for WeissMan301Protocol {
357    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
358        cmd.numeric_id = Self::PARAM_ID_SPDIF_COAXIAL_OUTPUT_MUTE;
359        cmd.value = u32::MAX;
360        Ok(())
361    }
362
363    fn build_control_data(
364        param: &WeissAvcSpdifCoaxialOutputMute,
365        cmd: &mut WeissAvcParamCmd,
366    ) -> Result<(), Error> {
367        cmd.numeric_id = Self::PARAM_ID_SPDIF_COAXIAL_OUTPUT_MUTE;
368        cmd.value = if param.0 { 0 } else { 1 };
369        Ok(())
370    }
371
372    fn parse_response_data(
373        param: &mut WeissAvcSpdifCoaxialOutputMute,
374        cmd: &WeissAvcParamCmd,
375    ) -> Result<(), Error> {
376        assert_eq!(cmd.numeric_id, Self::PARAM_ID_SPDIF_COAXIAL_OUTPUT_MUTE);
377        param.0 = cmd.value == 0;
378        Ok(())
379    }
380}
381
382/// Invert polarity of analog output.
383#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
384pub struct WeissAvcAnalogOutputPolarityInversion(pub bool);
385
386impl WeissAvcParamConvert<WeissAvcAnalogOutputPolarityInversion> for WeissMan301Protocol {
387    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
388        cmd.numeric_id = Self::PARAM_ID_ANALOG_OUTPUT_POLARITY_INVERSION;
389        cmd.value = u32::MAX;
390        Ok(())
391    }
392
393    fn build_control_data(
394        param: &WeissAvcAnalogOutputPolarityInversion,
395        cmd: &mut WeissAvcParamCmd,
396    ) -> Result<(), Error> {
397        cmd.numeric_id = Self::PARAM_ID_ANALOG_OUTPUT_POLARITY_INVERSION;
398        cmd.value = if param.0 { 1 } else { 0 };
399        Ok(())
400    }
401
402    fn parse_response_data(
403        param: &mut WeissAvcAnalogOutputPolarityInversion,
404        cmd: &WeissAvcParamCmd,
405    ) -> Result<(), Error> {
406        assert_eq!(
407            cmd.numeric_id,
408            Self::PARAM_ID_ANALOG_OUTPUT_POLARITY_INVERSION
409        );
410        param.0 = cmd.value > 0;
411        Ok(())
412    }
413}
414
415/// The type of oversampling filter in analog output.
416#[derive(Debug, Copy, Clone, PartialEq, Eq)]
417pub enum WeissAvcAnalogOutputFilterType {
418    A,
419    B,
420}
421
422impl Default for WeissAvcAnalogOutputFilterType {
423    fn default() -> Self {
424        Self::A
425    }
426}
427
428impl WeissAvcParamConvert<WeissAvcAnalogOutputFilterType> for WeissMan301Protocol {
429    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
430        cmd.numeric_id = Self::PARAM_ID_ANALOG_OUTPUT_FILTER_TYPE;
431        cmd.value = u32::MAX;
432        Ok(())
433    }
434
435    fn build_control_data(
436        param: &WeissAvcAnalogOutputFilterType,
437        cmd: &mut WeissAvcParamCmd,
438    ) -> Result<(), Error> {
439        cmd.numeric_id = Self::PARAM_ID_ANALOG_OUTPUT_FILTER_TYPE;
440        cmd.value = if param.eq(&WeissAvcAnalogOutputFilterType::B) {
441            1
442        } else {
443            0
444        };
445        Ok(())
446    }
447
448    fn parse_response_data(
449        param: &mut WeissAvcAnalogOutputFilterType,
450        cmd: &WeissAvcParamCmd,
451    ) -> Result<(), Error> {
452        assert_eq!(cmd.numeric_id, Self::PARAM_ID_ANALOG_OUTPUT_FILTER_TYPE);
453        *param = if cmd.value > 0 {
454            WeissAvcAnalogOutputFilterType::B
455        } else {
456            WeissAvcAnalogOutputFilterType::A
457        };
458        Ok(())
459    }
460}
461
462/// Mute analog output.
463#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
464pub struct WeissAvcAnalogOutputMute(pub bool);
465
466impl WeissAvcParamConvert<WeissAvcAnalogOutputMute> for WeissMan301Protocol {
467    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
468        cmd.numeric_id = Self::PARAM_ID_ANALOG_OUTPUT_MUTE;
469        cmd.value = u32::MAX;
470        Ok(())
471    }
472
473    fn build_control_data(
474        param: &WeissAvcAnalogOutputMute,
475        cmd: &mut WeissAvcParamCmd,
476    ) -> Result<(), Error> {
477        cmd.numeric_id = Self::PARAM_ID_ANALOG_OUTPUT_MUTE;
478        cmd.value = if param.0 { 0 } else { 1 };
479        Ok(())
480    }
481
482    fn parse_response_data(
483        param: &mut WeissAvcAnalogOutputMute,
484        cmd: &WeissAvcParamCmd,
485    ) -> Result<(), Error> {
486        assert_eq!(cmd.numeric_id, Self::PARAM_ID_ANALOG_OUTPUT_MUTE);
487        param.0 = cmd.value == 0;
488        Ok(())
489    }
490}
491
492/// The level of analog output.
493#[derive(Debug, Copy, Clone, PartialEq, Eq)]
494pub enum WeissAvcAnalogOutputLevel {
495    /// 0 dB.
496    Zero,
497    /// -10 dB.
498    NegativeTen,
499    /// -20 dB.
500    NegativeTwenty,
501    /// -30 dB.
502    NegativeThirty,
503}
504
505impl Default for WeissAvcAnalogOutputLevel {
506    fn default() -> Self {
507        Self::Zero
508    }
509}
510
511impl WeissAvcParamConvert<WeissAvcAnalogOutputLevel> for WeissMan301Protocol {
512    fn build_status_data(cmd: &mut WeissAvcParamCmd) -> Result<(), Error> {
513        cmd.numeric_id = Self::PARAM_ID_ANALOG_OUTPUT_LEVEL;
514        cmd.value = u32::MAX;
515        Ok(())
516    }
517
518    fn build_control_data(
519        param: &WeissAvcAnalogOutputLevel,
520        cmd: &mut WeissAvcParamCmd,
521    ) -> Result<(), Error> {
522        cmd.numeric_id = Self::PARAM_ID_ANALOG_OUTPUT_LEVEL;
523        cmd.value = match param {
524            WeissAvcAnalogOutputLevel::NegativeThirty => 3,
525            WeissAvcAnalogOutputLevel::NegativeTwenty => 2,
526            WeissAvcAnalogOutputLevel::NegativeTen => 1,
527            WeissAvcAnalogOutputLevel::Zero => 0,
528        };
529        Ok(())
530    }
531
532    fn parse_response_data(
533        param: &mut WeissAvcAnalogOutputLevel,
534        cmd: &WeissAvcParamCmd,
535    ) -> Result<(), Error> {
536        assert_eq!(cmd.numeric_id, Self::PARAM_ID_ANALOG_OUTPUT_LEVEL);
537        *param = match cmd.value {
538            3 => WeissAvcAnalogOutputLevel::NegativeThirty,
539            2 => WeissAvcAnalogOutputLevel::NegativeTwenty,
540            1 => WeissAvcAnalogOutputLevel::NegativeTen,
541            _ => WeissAvcAnalogOutputLevel::Zero,
542        };
543        Ok(())
544    }
545}
546
547/// The operation for parameters in Weiss AV/C protocol.
548pub trait WeissAvcParamOperation<T>: WeissAvcParamConvert<T> {
549    /// Cache current state of parameter.
550    fn cache_param(fcp: &WeissAvc, param: &mut T, timeout_ms: u32) -> Result<(), Error>;
551    /// Update the state of parameter.
552    fn update_param(fcp: &WeissAvc, param: &mut T, timeout_ms: u32) -> Result<(), Error>;
553}
554
555impl<T> WeissAvcParamOperation<T> for WeissMan301Protocol
556where
557    WeissMan301Protocol: WeissAvcParamConvert<T>,
558{
559    fn cache_param(fcp: &WeissAvc, param: &mut T, timeout_ms: u32) -> Result<(), Error> {
560        let mut cmd = WeissAvcParamCmd::default();
561        Self::build_status_data(&mut cmd)?;
562        fcp.status(&AvcAddr::Unit, &mut cmd, timeout_ms)?;
563        Self::parse_response_data(param, &cmd)?;
564        Ok(())
565    }
566
567    fn update_param(fcp: &WeissAvc, param: &mut T, timeout_ms: u32) -> Result<(), Error> {
568        let mut cmd = WeissAvcParamCmd::default();
569        Self::build_control_data(param, &mut cmd)?;
570        fcp.control(&AvcAddr::Unit, &mut cmd, timeout_ms)?;
571        Self::parse_response_data(param, &cmd)?;
572        Ok(())
573    }
574}
575
576#[cfg(test)]
577mod test {
578    use super::*;
579
580    #[test]
581    fn weiss_avc_param_command_operands() {
582        let operands = [
583            0x00, 0x1c, 0x6a, 0x01, 0x7f, 0x80, 0x02, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba,
584            0x98, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
585            0xff, 0xff, 0xff,
586        ];
587        let mut op = WeissAvcParamCmd::default();
588        AvcStatus::parse_operands(&mut op, &AvcAddr::Unit, &operands).unwrap();
589        assert_eq!(op.op.class_id, 0x01);
590        assert_eq!(op.op.sequence_id, 0x7f);
591        assert_eq!(op.op.command_id, 0x8002);
592        assert_eq!(op.op.arguments, &operands[7..]);
593        assert_eq!(op.numeric_id, 0x76543210);
594        assert_eq!(op.value, 0xfedcba98);
595
596        let target = AvcStatus::build_operands(&mut op, &AvcAddr::Unit).unwrap();
597        assert_eq!(&target[..4], &operands[..4]);
598        // The value of sequence_id field is never matched.
599        assert_eq!(&target[5..], &operands[5..]);
600
601        let target = AvcControl::build_operands(&mut op, &AvcAddr::Unit).unwrap();
602        assert_eq!(&target[..4], &operands[..4]);
603        // The value of sequence_id field is never matched.
604        assert_eq!(&target[5..], &operands[5..]);
605
606        let mut op = WeissAvcParamCmd::default();
607        AvcControl::parse_operands(&mut op, &AvcAddr::Unit, &operands).unwrap();
608        assert_eq!(op.op.class_id, 0x01);
609        assert_eq!(op.op.sequence_id, 0x7f);
610        assert_eq!(op.op.command_id, 0x8002);
611        assert_eq!(op.op.arguments, &operands[7..]);
612        assert_eq!(op.numeric_id, 0x76543210);
613        assert_eq!(op.value, 0xfedcba98);
614    }
615}