firewire_bebob_protocols/
lib.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (c) 2021 Takashi Sakamoto
3
4#![doc = include_str!("../README.md")]
5
6pub mod bridgeco;
7
8pub mod apogee;
9pub mod behringer;
10pub mod digidesign;
11pub mod esi;
12pub mod focusrite;
13pub mod icon;
14pub mod maudio;
15pub mod presonus;
16pub mod roland;
17pub mod stanton;
18pub mod terratec;
19pub mod yamaha_terratec;
20
21use {
22    self::bridgeco::{ExtendedStreamFormatSingle, *},
23    glib::{Error, FileError, IsA},
24    hinawa::{
25        prelude::{FwFcpExt, FwFcpExtManual, FwReqExtManual},
26        FwFcp, FwNode, FwReq, FwTcode,
27    },
28    ta1394_avc_audio::{amdtp::*, *},
29    ta1394_avc_ccm::*,
30    ta1394_avc_general::{general::*, *},
31    ta1394_avc_stream_format::*,
32};
33
34/// The offset for specific purposes in DM1000/DM1100/DM1500 ASICs.
35const DM_APPL_OFFSET: u64 = 0xffc700000000;
36const DM_APPL_METER_OFFSET: u64 = DM_APPL_OFFSET + 0x00600000;
37const DM_APPL_PARAM_OFFSET: u64 = DM_APPL_OFFSET + 0x00700000;
38const DM_BCO_OFFSET: u64 = 0xffffc8000000;
39const DM_BCO_BOOTLOADER_INFO_OFFSET: u64 = DM_BCO_OFFSET + 0x00020000;
40
41/// The implementation of AV/C transaction with quirks specific to BeBoB solution.
42///
43/// It seems a unique quirk that the status code in response frame for some AV/C commands is
44/// against AV/C general specification in control operation.
45#[derive(Default, Debug)]
46pub struct BebobAvc(FwFcp);
47
48impl Ta1394Avc<Error> for BebobAvc {
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    fn control<O: AvcOp + AvcControl>(
60        &self,
61        addr: &AvcAddr,
62        op: &mut O,
63        timeout_ms: u32,
64    ) -> Result<(), Ta1394AvcError<Error>> {
65        let operands =
66            AvcControl::build_operands(op, addr).map_err(|err| Ta1394AvcError::CmdBuild(err))?;
67        let command_frame =
68            Self::compose_command_frame(AvcCmdType::Control, addr, O::OPCODE, &operands)?;
69        let response_frame = self
70            .transaction(&command_frame, timeout_ms)
71            .map_err(|cause| Ta1394AvcError::CommunicationFailure(cause))?;
72        Self::detect_response_operands(&response_frame, addr, O::OPCODE)
73            .and_then(|(rcode, operands)| {
74                let expected = match O::OPCODE {
75                    InputPlugSignalFormat::OPCODE
76                    | OutputPlugSignalFormat::OPCODE
77                    | SignalSource::OPCODE => {
78                        // NOTE: quirk.
79                        rcode == AvcRespCode::Accepted || rcode == AvcRespCode::Reserved(0x00)
80                    }
81                    _ => rcode == AvcRespCode::Accepted,
82                };
83                if !expected {
84                    Err(AvcRespParseError::UnexpectedStatus)
85                } else {
86                    AvcControl::parse_operands(op, addr, &operands)
87                }
88            })
89            .map_err(|err| Ta1394AvcError::RespParse(err))
90    }
91}
92
93impl BebobAvc {
94    pub fn bind(&self, node: &impl IsA<FwNode>) -> Result<(), Error> {
95        self.0.bind(node)
96    }
97
98    pub fn control<O: AvcOp + AvcControl>(
99        &self,
100        addr: &AvcAddr,
101        op: &mut O,
102        timeout_ms: u32,
103    ) -> Result<(), Error> {
104        Ta1394Avc::<Error>::control(self, addr, op, timeout_ms).map_err(|err| from_avc_err(err))
105    }
106
107    pub fn status<O: AvcOp + AvcStatus>(
108        &self,
109        addr: &AvcAddr,
110        op: &mut O,
111        timeout_ms: u32,
112    ) -> Result<(), Error> {
113        Ta1394Avc::<Error>::status(self, addr, op, timeout_ms).map_err(|err| from_avc_err(err))
114    }
115}
116
117fn from_avc_err(err: Ta1394AvcError<Error>) -> Error {
118    match err {
119        Ta1394AvcError::CmdBuild(cause) => Error::new(FileError::Inval, &cause.to_string()),
120        Ta1394AvcError::CommunicationFailure(cause) => cause,
121        Ta1394AvcError::RespParse(cause) => Error::new(FileError::Io, &cause.to_string()),
122    }
123}
124
125/// The parameters of media clock.
126#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
127pub struct MediaClockParameters {
128    /// The index for entry in frequency list.
129    pub freq_idx: usize,
130}
131
132/// The trait of frequency operation for media clock.
133pub trait MediaClockFrequencyOperation {
134    /// The list of supported frequencies.
135    const FREQ_LIST: &'static [u32];
136
137    /// Cache the state of media clock to the parameters.
138    fn cache_freq(
139        avc: &BebobAvc,
140        params: &mut MediaClockParameters,
141        timeout_ms: u32,
142    ) -> Result<(), Error> {
143        let plug_addr =
144            BcoPlugAddr::new_for_unit(BcoPlugDirection::Output, BcoPlugAddrUnitType::Isoc, 0);
145        let mut op = ExtendedStreamFormatSingle::new(&plug_addr);
146
147        avc.status(&AvcAddr::Unit, &mut op, timeout_ms)?;
148
149        op.stream_format
150            .as_bco_compound_am824_stream()
151            .ok_or_else(|| {
152                let label = "Bco Compound AM824 stream is not available for the unit";
153                Error::new(FileError::Nxio, &label)
154            })
155            .and_then(|format| {
156                Self::FREQ_LIST
157                    .iter()
158                    .position(|&r| r == format.freq)
159                    .ok_or_else(|| {
160                        let msg = format!("Unexpected entry for source of clock: {}", format.freq);
161                        Error::new(FileError::Io, &msg)
162                    })
163            })
164            .map(|freq_idx| params.freq_idx = freq_idx)
165    }
166
167    /// Update the hardware by the given parameter. This operation can involve INTERIM AV/C
168    /// response to expand response time of AV/C transaction.
169    fn update_freq(
170        avc: &BebobAvc,
171        params: &MediaClockParameters,
172        old: &mut MediaClockParameters,
173        timeout_ms: u32,
174    ) -> Result<(), Error> {
175        let fdf = Self::FREQ_LIST
176            .iter()
177            .nth(params.freq_idx)
178            .ok_or_else(|| {
179                let msg = format!(
180                    "Invalid argument for index of frequency: {}",
181                    params.freq_idx
182                );
183                Error::new(FileError::Inval, &msg)
184            })
185            .map(|&freq| AmdtpFdf::new(AmdtpEventType::Am824, false, freq))?;
186
187        let mut op = InputPlugSignalFormat(PlugSignalFormat {
188            plug_id: 0,
189            fmt: FMT_IS_AMDTP,
190            fdf: fdf.into(),
191        });
192        avc.control(&AvcAddr::Unit, &mut op, timeout_ms)?;
193
194        let mut op = OutputPlugSignalFormat(PlugSignalFormat {
195            plug_id: 0,
196            fmt: FMT_IS_AMDTP,
197            fdf: fdf.into(),
198        });
199        avc.control(&AvcAddr::Unit, &mut op, timeout_ms)?;
200
201        *old = *params;
202
203        Ok(())
204    }
205}
206
207/// The parameters of sampling clock.
208#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
209pub struct SamplingClockParameters {
210    /// The index for entry in source list.
211    pub src_idx: usize,
212}
213
214/// The trait of source operation for sampling clock.
215pub trait SamplingClockSourceOperation {
216    // NOTE: all of bebob models support "SignalAddr::Unit(SignalUnitAddr::Isoc(0x00))" named as
217    // "PCR Compound Input" and "SignalAddr::Unit(SignalUnitAddr::Isoc(0x01))" named as
218    // "PCR Sync Input" for source of sampling clock. They are available to be synchronized to the
219    // series of syt field in incoming packets from the other unit on IEEE 1394 bus. However, the
220    // most of models doesn't work with it actually even if configured, therefore useless.
221    /// The destination plug address for source signal.
222    const DST: SignalAddr;
223    /// The list of supported sources expressed by plug address.
224    const SRC_LIST: &'static [SignalAddr];
225
226    /// Cache the state of sampling clock to the parameters.
227    fn cache_src(
228        avc: &BebobAvc,
229        params: &mut SamplingClockParameters,
230        timeout_ms: u32,
231    ) -> Result<(), Error> {
232        let mut op = SignalSource::new(&Self::DST);
233
234        avc.status(&AvcAddr::Unit, &mut op, timeout_ms)?;
235
236        Self::SRC_LIST
237            .iter()
238            .position(|&s| s == op.src)
239            .ok_or_else(|| {
240                let label = "Unexpected entry for source of clock";
241                Error::new(FileError::Io, &label)
242            })
243            .map(|src_idx| params.src_idx = src_idx)
244    }
245
246    /// Update the hardware by the given parameter. This operation can involve INTERIM AV/C
247    /// response to expand response time of AV/C transaction.
248    fn update_src(
249        avc: &BebobAvc,
250        params: &SamplingClockParameters,
251        old: &mut SamplingClockParameters,
252        timeout_ms: u32,
253    ) -> Result<(), Error> {
254        let src = Self::SRC_LIST
255            .iter()
256            .nth(params.src_idx)
257            .ok_or_else(|| {
258                let label = "Invalid value for source of clock";
259                Error::new(FileError::Inval, &label)
260            })
261            .copied()?;
262
263        let mut op = SignalSource::new(&Self::DST);
264        op.src = src;
265
266        avc.control(&AvcAddr::Unit, &mut op, timeout_ms)
267            .map(|_| *old = *params)
268    }
269}
270
271/// The specification of Feature Function Blocks of AV/C Audio subunit.
272pub trait AvcAudioFeatureSpecification {
273    /// The entries of pair of function block identifier and audio channel.
274    const ENTRIES: &'static [(u8, AudioCh)];
275}
276
277/// The parameters of signal level. The `Default` trait should be implemented to call
278/// `AvcLevelOperation::create_level_parameters()`.
279#[derive(Debug, Clone, PartialEq, Eq)]
280pub struct AvcLevelParameters {
281    /// The signal levels.
282    pub levels: Vec<i16>,
283}
284
285/// The trait of level operation for audio function blocks by AV/C transaction.
286pub trait AvcLevelOperation: AvcAudioFeatureSpecification {
287    /// The minimum value of signal level.
288    const LEVEL_MIN: i16 = VolumeData::VALUE_NEG_INFINITY;
289    /// The maximum value of signal level.
290    const LEVEL_MAX: i16 = VolumeData::VALUE_ZERO;
291    /// The step value of signal level.
292    const LEVEL_STEP: i16 = 0x100;
293
294    /// Instantiate parameters.
295    fn create_level_parameters() -> AvcLevelParameters {
296        AvcLevelParameters {
297            levels: vec![Default::default(); Self::ENTRIES.len()],
298        }
299    }
300
301    /// Cache state of hardware to the parameters.
302    fn cache_levels(
303        avc: &BebobAvc,
304        params: &mut AvcLevelParameters,
305        timeout_ms: u32,
306    ) -> Result<(), Error> {
307        assert_eq!(params.levels.len(), Self::ENTRIES.len());
308
309        params
310            .levels
311            .iter_mut()
312            .zip(Self::ENTRIES)
313            .try_for_each(|(level, entry)| {
314                let &(func_block_id, audio_ch) = entry;
315                let mut op = AudioFeature::new(
316                    func_block_id,
317                    CtlAttr::Current,
318                    audio_ch,
319                    FeatureCtl::Volume(VolumeData::new(1)),
320                );
321                avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
322                    .map(|_| {
323                        if let FeatureCtl::Volume(data) = op.ctl {
324                            *level = data.0[0]
325                        }
326                    })
327            })
328    }
329
330    /// Update the hardware when detecting any changes in the parameters.
331    fn update_levels(
332        avc: &BebobAvc,
333        params: &AvcLevelParameters,
334        old: &mut AvcLevelParameters,
335        timeout_ms: u32,
336    ) -> Result<(), Error> {
337        assert_eq!(params.levels.len(), Self::ENTRIES.len());
338        assert_eq!(old.levels.len(), Self::ENTRIES.len());
339
340        old.levels
341            .iter_mut()
342            .zip(params.levels.iter())
343            .zip(Self::ENTRIES)
344            .filter(|((old, new), _)| !new.eq(old))
345            .try_for_each(|((old, new), entry)| {
346                let &(func_block_id, audio_ch) = entry;
347                let mut op = AudioFeature::new(
348                    func_block_id,
349                    CtlAttr::Current,
350                    audio_ch,
351                    FeatureCtl::Volume(VolumeData(vec![*new])),
352                );
353                avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
354                    .map(|_| *old = *new)
355            })
356    }
357}
358
359/// The parameters of L/R balance. The `Default` trait should be implemented to call
360/// `AvcLrBalanceOperation::create_lr_balance_parameters()`.
361#[derive(Debug, Clone, PartialEq, Eq)]
362pub struct AvcLrBalanceParameters {
363    /// The L/R balances.
364    pub balances: Vec<i16>,
365}
366
367/// The trait of LR balance operation for audio function blocks.
368pub trait AvcLrBalanceOperation: AvcAudioFeatureSpecification {
369    /// The minimum value of L/R balance.
370    const BALANCE_MIN: i16 = LrBalanceData::VALUE_LEFT_NEG_INFINITY;
371    /// The maximum value of L/R balance.
372    const BALANCE_MAX: i16 = LrBalanceData::VALUE_LEFT_MAX;
373    /// The step value of L/R balance.
374    const BALANCE_STEP: i16 = 0x80;
375
376    /// Instantiate parameters.
377    fn create_lr_balance_parameters() -> AvcLrBalanceParameters {
378        AvcLrBalanceParameters {
379            balances: vec![Default::default(); Self::ENTRIES.len()],
380        }
381    }
382
383    /// Cache state of hardware to the parameters.
384    fn cache_lr_balances(
385        avc: &BebobAvc,
386        params: &mut AvcLrBalanceParameters,
387        timeout_ms: u32,
388    ) -> Result<(), Error> {
389        assert_eq!(params.balances.len(), Self::ENTRIES.len());
390
391        params
392            .balances
393            .iter_mut()
394            .zip(Self::ENTRIES)
395            .try_for_each(|(balance, entry)| {
396                let &(func_block_id, audio_ch) = entry;
397                let mut op = AudioFeature::new(
398                    func_block_id,
399                    CtlAttr::Current,
400                    audio_ch,
401                    FeatureCtl::LrBalance(Default::default()),
402                );
403                avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
404                    .map(|_| {
405                        if let FeatureCtl::LrBalance(data) = op.ctl {
406                            *balance = data.0;
407                        }
408                    })
409            })
410    }
411
412    /// Update the hardware when detecting any changes in the parameters.
413    fn update_lr_balances(
414        avc: &BebobAvc,
415        params: &AvcLrBalanceParameters,
416        old: &mut AvcLrBalanceParameters,
417        timeout_ms: u32,
418    ) -> Result<(), Error> {
419        assert_eq!(params.balances.len(), Self::ENTRIES.len());
420        assert_eq!(old.balances.len(), Self::ENTRIES.len());
421
422        old.balances
423            .iter_mut()
424            .zip(params.balances.iter())
425            .zip(Self::ENTRIES)
426            .filter(|((o, n), _)| !o.eq(n))
427            .try_for_each(|((old, &new), entry)| {
428                let &(func_block_id, audio_ch) = entry;
429                let mut op = AudioFeature::new(
430                    func_block_id,
431                    CtlAttr::Current,
432                    audio_ch,
433                    FeatureCtl::LrBalance(LrBalanceData(new)),
434                );
435                avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
436                    .map(|_| *old = new)
437            })
438    }
439}
440
441/// The parameters of mute. The `Default` trait should be implemented to call
442/// `AvcMuteOperation::create_mute_parameters()`.
443#[derive(Debug, Clone, PartialEq, Eq)]
444pub struct AvcMuteParameters {
445    /// Muted or not.
446    pub mutes: Vec<bool>,
447}
448
449/// The trait of mute operation for audio function blocks.
450pub trait AvcMuteOperation: AvcAudioFeatureSpecification {
451    /// Instantiate parameters.
452    fn create_mute_parameters() -> AvcMuteParameters {
453        AvcMuteParameters {
454            mutes: vec![Default::default(); Self::ENTRIES.len()],
455        }
456    }
457
458    /// Cache state of hardware to the parameters.
459    fn cache_mutes(
460        avc: &BebobAvc,
461        params: &mut AvcMuteParameters,
462        timeout_ms: u32,
463    ) -> Result<(), Error> {
464        assert_eq!(params.mutes.len(), Self::ENTRIES.len());
465
466        params
467            .mutes
468            .iter_mut()
469            .zip(Self::ENTRIES)
470            .try_for_each(|(mute, entry)| {
471                let &(func_block_id, audio_ch) = entry;
472
473                let mut op = AudioFeature::new(
474                    func_block_id,
475                    CtlAttr::Current,
476                    audio_ch,
477                    FeatureCtl::Mute(vec![false]),
478                );
479                avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
480                    .map(|_| {
481                        if let FeatureCtl::Mute(data) = op.ctl {
482                            *mute = data[0];
483                        }
484                    })
485            })
486    }
487
488    /// Update the hardware when detecting any changes in the parameters.
489    fn update_mutes(
490        avc: &BebobAvc,
491        params: &AvcMuteParameters,
492        old: &mut AvcMuteParameters,
493        timeout_ms: u32,
494    ) -> Result<(), Error> {
495        assert_eq!(params.mutes.len(), Self::ENTRIES.len());
496        assert_eq!(old.mutes.len(), Self::ENTRIES.len());
497
498        old.mutes
499            .iter_mut()
500            .zip(params.mutes.iter())
501            .zip(Self::ENTRIES)
502            .filter(|((o, n), _)| !n.eq(o))
503            .try_for_each(|((old, &new), entry)| {
504                let &(func_block_id, audio_ch) = entry;
505
506                let mut op = AudioFeature::new(
507                    func_block_id,
508                    CtlAttr::Current,
509                    audio_ch,
510                    FeatureCtl::Mute(vec![new]),
511                );
512                avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
513                    .map(|_| *old = new)
514            })
515    }
516}
517
518/// The parameter of selectors. The `Default` trait should be implemented to call
519/// `AvcSelectorOperation::create_selector_parameters()`.
520#[derive(Debug, Clone, PartialEq, Eq)]
521pub struct AvcSelectorParameters {
522    /// The index for entry in the list of function block.
523    pub selectors: Vec<usize>,
524}
525
526/// The trait of select operation for audio function block.
527pub trait AvcSelectorOperation {
528    /// The list of function block identifier.
529    const FUNC_BLOCK_ID_LIST: &'static [u8];
530    /// The list of plug identifier.
531    const INPUT_PLUG_ID_LIST: &'static [u8];
532
533    /// Instantiate parameters.
534    fn create_selector_parameters() -> AvcSelectorParameters {
535        AvcSelectorParameters {
536            selectors: vec![Default::default(); Self::FUNC_BLOCK_ID_LIST.len()],
537        }
538    }
539
540    /// Cache state of hardware to the parameters.
541    fn cache_selectors(
542        avc: &BebobAvc,
543        params: &mut AvcSelectorParameters,
544        timeout_ms: u32,
545    ) -> Result<(), Error> {
546        assert_eq!(params.selectors.len(), Self::FUNC_BLOCK_ID_LIST.len());
547
548        params
549            .selectors
550            .iter_mut()
551            .zip(Self::FUNC_BLOCK_ID_LIST)
552            .try_for_each(|(selector, &func_block_id)| {
553                let mut op = AudioSelector::new(func_block_id, CtlAttr::Current, 0xff);
554                avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
555
556                Self::INPUT_PLUG_ID_LIST
557                    .iter()
558                    .position(|&input_plug_id| input_plug_id == op.input_plug_id)
559                    .ok_or_else(|| {
560                        let msg = format!(
561                            "Unexpected index of input plug number: {}",
562                            op.input_plug_id
563                        );
564                        Error::new(FileError::Io, &msg)
565                    })
566                    .map(|pos| *selector = pos)
567            })
568    }
569
570    /// Update the hardware when detecting any changes in the parameters.
571    fn update_selectors(
572        avc: &BebobAvc,
573        params: &AvcSelectorParameters,
574        old: &mut AvcSelectorParameters,
575        timeout_ms: u32,
576    ) -> Result<(), Error> {
577        assert_eq!(params.selectors.len(), Self::FUNC_BLOCK_ID_LIST.len());
578        assert_eq!(old.selectors.len(), Self::FUNC_BLOCK_ID_LIST.len());
579
580        old.selectors
581            .iter_mut()
582            .zip(params.selectors.iter())
583            .zip(Self::FUNC_BLOCK_ID_LIST)
584            .filter(|((o, n), _)| !o.eq(n))
585            .try_for_each(|((old, &new), &func_block_id)| {
586                let mut op = AudioSelector::new(func_block_id, CtlAttr::Current, new as u8);
587                avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
588                    .map(|_| *old = new)
589            })
590    }
591}