firewire_tascam_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
6//! ## Process events for control surface
7//!
8//! Tascam FW-1884, FW-1082, and FE-8 have control surface, in which the hardware make no
9//! superficial change to the surface according to user operation. Instead, the surface notifies
10//! the operation to system.
11//!
12//! In FE-8, the image consists of 32 quadlets. Asynchronous notification is simply sent for one
13//! of quadlet to which user operation effects.
14//!
15//! In FW-1884 and FW-1082, the state of surface is expressed as an image which consists of 64
16//! quadlets. One of the quadlets is multiplexed to data block in isochronous packet as well as
17//! PCM frame in order.
18//!
19//! FW-1884 and FW-1082 has shift button to divert some buttons. Furthermore, FW-1082 has some
20//! rotaries and buttons which change their role according to encoder mode. The module includes
21//! two stuffs to abstract the above design; surface state and machine state. The former is used
22//! used to parse surface imageg and detect event and operate LED. The latter is used to monitor
23//! current state of each surface item by handling the event, and generate normalized events. It's
24//! task of runtime implementation to prepare converter between the machine event and application
25//! specific message such as ALSA Sequencer and Open Sound Control.
26//!
27//! The relationship between the constrol surface, surface state, machine state, and message
28//! converter is illustrated in below diagram:
29//!
30//! ```text
31//!                       ++====================================================++
32//!                       ||                  Service runtime                   ||
33//!                       ||                                                    ||
34//! ++==========++  surface image    +---------+   machine event   +---------+  ||
35//! ||          || ----------------> |         | ----------------> |         |  ||
36//! || surface  ||        ||         | surface |                   | machine |  ||
37//! || hardware ||  LED operation    |  state  |   machine event   |  state  |  ||
38//! ||          || <---------------- |         | <---------------- |         |  ||
39//! ++==========++        ||         +---------+                   +---------+  ||
40//!                       ||                                         ^    |     ||
41//!                       ||                                      machine event ||
42//!                       ||                                         |    v     ||
43//!                       ||                                       +---------+  ||
44//!                       ||                                       |         |  ||
45//!                       ||                                       | message |  ||
46//!                       ||                                       |converter|  ||
47//!                       ||                                       |         |  ||
48//!                       ||                                       +---------+  ||
49//!                       ||                                         ^    |     ||
50//!                       ||                                   specific message ||
51//!                       ||                                         |    |     ||
52//!                       ++=========================================|====|=====++
53//!                                                                  |    |
54//!                                     Inter process communication  |    |
55//!                                     (ALSA Sequencer, OSC, etc.)  |    v
56//!                                                             ++=============++
57//!                                                             || application ||
58//!                                                             ++=============++
59//! ```
60
61pub mod asynch;
62pub mod isoch;
63
64pub mod config_rom;
65
66use {
67    glib::{Error, FileError},
68    hinawa::{prelude::*, *},
69};
70
71const BASE_OFFSET: u64 = 0xffff00000000;
72const HW_INFO_REGISTER_OFFSET: u64 = 0x00;
73const HW_INFO_FPGA_OFFSET: u64 = 0x04;
74const HW_INFO_ARM_OFFSET: u64 = 0x08;
75const HW_INFO_HW_OFFSET: u64 = 0x0c;
76const LED_OFFSET: u64 = 0x0404;
77
78fn read_quadlet(
79    req: &mut FwReq,
80    node: &mut FwNode,
81    offset: u64,
82    frames: &mut [u8],
83    timeout_ms: u32,
84) -> Result<(), Error> {
85    req.transaction_sync(
86        node,
87        FwTcode::ReadQuadletRequest,
88        BASE_OFFSET + offset,
89        4,
90        frames,
91        timeout_ms,
92    )
93}
94
95fn write_quadlet(
96    req: &mut FwReq,
97    node: &FwNode,
98    offset: u64,
99    frames: &mut [u8],
100    timeout_ms: u32,
101) -> Result<(), Error> {
102    req.transaction_sync(
103        node,
104        FwTcode::WriteQuadletRequest,
105        BASE_OFFSET + offset,
106        4,
107        frames,
108        timeout_ms,
109    )
110}
111
112/// Information of hardware.
113#[derive(Debug, Default, Copy, Clone)]
114pub struct HardwareInformation {
115    pub register: u32,
116    pub fpga: u32,
117    pub arm: u32,
118    pub hardware: u32,
119}
120
121/// The protocol implementaion commonly available to Tascam FireWire models.
122#[derive(Debug, Default)]
123pub struct HardwareInformationProtocol;
124
125/// The trait for oepration of hardware information.
126impl HardwareInformationProtocol {
127    pub fn read_hardware_information(
128        req: &mut FwReq,
129        node: &mut FwNode,
130        info: &mut HardwareInformation,
131        timeout_ms: u32,
132    ) -> Result<(), Error> {
133        let mut quads = [0; 4];
134        read_quadlet(req, node, HW_INFO_REGISTER_OFFSET, &mut quads, timeout_ms)
135            .map(|_| info.register = u32::from_be_bytes(quads))?;
136        read_quadlet(req, node, HW_INFO_FPGA_OFFSET, &mut quads, timeout_ms)
137            .map(|_| info.fpga = u32::from_be_bytes(quads))?;
138        read_quadlet(req, node, HW_INFO_ARM_OFFSET, &mut quads, timeout_ms)
139            .map(|_| info.arm = u32::from_be_bytes(quads))?;
140        read_quadlet(req, node, HW_INFO_HW_OFFSET, &mut quads, timeout_ms)
141            .map(|_| info.hardware = u32::from_be_bytes(quads))?;
142        Ok(())
143    }
144}
145
146/// The specification of hardware image.
147pub trait TascamHardwareImageSpecification {
148    const IMAGE_QUADLET_COUNT: usize;
149
150    fn create_hardware_image() -> Vec<u32> {
151        vec![0; Self::IMAGE_QUADLET_COUNT]
152    }
153}
154
155/// Items of surface.
156#[derive(Debug, Copy, Clone, PartialEq, Eq)]
157pub enum MachineItem {
158    // Channel section.
159    Master,
160    Ol(usize),
161    Rec(usize),
162    Signal(usize),
163    Rotary(usize),
164    Select(usize),
165    Solo(usize),
166    Mute(usize),
167    Input(usize),
168    Func(usize),
169    Pfl,
170
171    // Global section.
172    Read,
173    Wrt,
174    Tch,
175    Latch,
176    Wheel,
177    Shuttle,
178    Computer,
179    Clock,
180    Up,
181    Left,
182    Down,
183    Right,
184    NudgeLeft,
185    NudgeRight,
186    LocateLeft,
187    LocateRight,
188    Set,
189    In,
190    Out,
191
192    // Encoder section.
193    Flip,
194    Pan, // has bool value in FW-1884, has u16 value in FW-1082.
195    Aux(usize),
196    EncoderMode, // FW-1082 only.
197
198    // Equalizer section.
199    High,
200    HighMid,
201    LowMid,
202    Low,
203    Recall,
204    Gain,
205    Freq,
206    Q,
207
208    // Bank section.
209    Bank,
210
211    // Transport section.
212    Rew,
213    Fwd,
214    Stop,
215    Play,
216    Record,
217
218    // Shortcut section.
219    Panel,
220    Save,
221    Revert,
222    AllSafe,
223    ClrSolo,
224    Markers,
225    Loop,
226    Cut,
227    Del,
228    Copy,
229    Paste,
230    Alt,
231    Cmd,
232    Undo,
233    Shift,
234    Ctrl,
235}
236
237impl Default for MachineItem {
238    fn default() -> Self {
239        Self::Master
240    }
241}
242
243impl std::fmt::Display for MachineItem {
244    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
245        match self {
246            Self::Master => write!(f, "master"),
247            Self::Ol(ch) => write!(f, "ol {}", ch),
248            Self::Rec(ch) => write!(f, "rec {}", ch),
249            Self::Signal(ch) => write!(f, "signal {}", ch),
250            Self::Rotary(ch) => write!(f, "rotary {}", ch),
251            Self::Select(ch) => write!(f, "select {}", ch),
252            Self::Solo(ch) => write!(f, "solo {}", ch),
253            Self::Mute(ch) => write!(f, "mute {}", ch),
254            Self::Input(ch) => write!(f, "input {}", ch),
255            Self::Func(ch) => write!(f, "func {}", ch),
256            Self::Pfl => write!(f, "pfl"),
257            Self::Read => write!(f, "read"),
258            Self::Wrt => write!(f, "wrt"),
259            Self::Tch => write!(f, "tch"),
260            Self::Latch => write!(f, "latch"),
261            Self::Wheel => write!(f, "wheel"),
262            Self::Shuttle => write!(f, "Shuttle"),
263            Self::Computer => write!(f, "computer"),
264            Self::Clock => write!(f, "clock"),
265            Self::Up => write!(f, "up"),
266            Self::Left => write!(f, "left"),
267            Self::Down => write!(f, "down"),
268            Self::Right => write!(f, "right"),
269            Self::NudgeLeft => write!(f, "nudge left"),
270            Self::NudgeRight => write!(f, "nudge right"),
271            Self::LocateLeft => write!(f, "locate left"),
272            Self::LocateRight => write!(f, "locate right"),
273            Self::Set => write!(f, "set"),
274            Self::In => write!(f, "in"),
275            Self::Out => write!(f, "out"),
276            Self::Flip => write!(f, "flip"),
277            Self::Pan => write!(f, "pan"),
278            Self::Aux(ch) => write!(f, "aux {}", ch),
279            Self::EncoderMode => write!(f, "encoder model"),
280            Self::High => write!(f, "high"),
281            Self::HighMid => write!(f, "high-mid"),
282            Self::LowMid => write!(f, "low-mid"),
283            Self::Low => write!(f, "low"),
284            Self::Recall => write!(f, "recall"),
285            Self::Gain => write!(f, "gain"),
286            Self::Freq => write!(f, "freq"),
287            Self::Q => write!(f, "q"),
288            Self::Bank => write!(f, "bank"),
289            Self::Rew => write!(f, "rew"),
290            Self::Fwd => write!(f, "fwd"),
291            Self::Stop => write!(f, "stop"),
292            Self::Play => write!(f, "play"),
293            Self::Record => write!(f, "record"),
294            Self::Panel => write!(f, "panel"),
295            Self::Save => write!(f, "save"),
296            Self::Revert => write!(f, "revert"),
297            Self::AllSafe => write!(f, "all safe"),
298            Self::ClrSolo => write!(f, "clr solo"),
299            Self::Markers => write!(f, "markers"),
300            Self::Loop => write!(f, "loop"),
301            Self::Cut => write!(f, "cut"),
302            Self::Del => write!(f, "del"),
303            Self::Copy => write!(f, "copy"),
304            Self::Paste => write!(f, "paste"),
305            Self::Alt => write!(f, "alt"),
306            Self::Cmd => write!(f, "cmd"),
307            Self::Undo => write!(f, "undo"),
308            Self::Shift => write!(f, "shift"),
309            Self::Ctrl => write!(f, "ctrl"),
310        }
311    }
312}
313
314/// The state machine of control surface.
315#[derive(Default, Debug, Clone, PartialEq, Eq)]
316pub struct MachineState {
317    /// The boolean value of each item.
318    bool_items: Vec<bool>,
319    /// The u16 value of each item.
320    u16_items: Vec<u16>,
321    /// Between 0-3.
322    bank: u16,
323    /// One of Rew, Fwd, Stop, Play, and Record.
324    transport: MachineItem,
325}
326
327/// The event of state machine.
328#[derive(Debug, Copy, Clone, PartialEq, Eq)]
329pub enum ItemValue {
330    Bool(bool),
331    U16(u16),
332}
333
334const BANK_MIN: u16 = 0;
335const BANK_MAX: u16 = 3;
336
337/// The trait for operation of state machine.
338pub trait MachineStateOperation {
339    const BOOL_ITEMS: &'static [MachineItem];
340    const U16_ITEMS: &'static [MachineItem];
341    const HAS_TRANSPORT: bool;
342    const HAS_BANK: bool;
343
344    const BANK_MIN: u16 = BANK_MIN;
345    const BANK_MAX: u16 = BANK_MAX;
346
347    const U16_ITEM_MIN: u16 = 0;
348    const U16_ITEM_MAX: u16 = 0xffffu16;
349
350    const TRANSPORT_ITEMS: [MachineItem; 5] = [
351        MachineItem::Rew,
352        MachineItem::Fwd,
353        MachineItem::Stop,
354        MachineItem::Play,
355        MachineItem::Record,
356    ];
357
358    const EQ_BAND_ITEMS: [MachineItem; 4] = [
359        MachineItem::High,
360        MachineItem::HighMid,
361        MachineItem::LowMid,
362        MachineItem::Low,
363    ];
364
365    fn create_machine_state() -> MachineState {
366        MachineState {
367            bool_items: vec![false; Self::BOOL_ITEMS.len()],
368            u16_items: vec![0; Self::U16_ITEMS.len()],
369            bank: 0,
370            transport: MachineItem::Stop,
371        }
372    }
373
374    fn get_machine_current_values(state: &MachineState) -> Vec<(MachineItem, ItemValue)> {
375        let mut machine_values = Vec::new();
376
377        Self::BOOL_ITEMS
378            .iter()
379            .zip(&state.bool_items)
380            .for_each(|(&item, &value)| machine_values.push((item, ItemValue::Bool(value))));
381
382        Self::U16_ITEMS
383            .iter()
384            .zip(&state.u16_items)
385            .for_each(|(&item, &value)| machine_values.push((item, ItemValue::U16(value))));
386
387        if Self::HAS_BANK {
388            machine_values.push((MachineItem::Bank, ItemValue::U16(state.bank)));
389        }
390
391        if Self::HAS_TRANSPORT {
392            Self::TRANSPORT_ITEMS.iter().for_each(|&item| {
393                machine_values.push((item, ItemValue::Bool(item.eq(&state.transport))));
394            });
395        }
396
397        machine_values
398    }
399
400    fn change_machine_value(
401        state: &mut MachineState,
402        input: &(MachineItem, ItemValue),
403    ) -> Vec<(MachineItem, ItemValue)> {
404        let mut outputs = Vec::new();
405
406        if let ItemValue::Bool(value) = input.1 {
407            // Normal items.
408            let _ = Self::BOOL_ITEMS
409                .iter()
410                .zip(&mut state.bool_items)
411                .find(|(i, v)| input.0.eq(i) && !value.eq(v))
412                .map(|(_, v)| {
413                    *v = value;
414                    outputs.push((input.0, ItemValue::Bool(*v)));
415                });
416
417            // One of transport items should be enabled.
418            if Self::HAS_TRANSPORT
419                && Self::TRANSPORT_ITEMS
420                    .iter()
421                    .find(|i| input.0.eq(i))
422                    .is_some()
423            {
424                if input.0 != state.transport {
425                    outputs.push((state.transport, ItemValue::Bool(false)));
426                    outputs.push((input.0, ItemValue::Bool(true)));
427                    state.transport = input.0;
428                }
429            }
430
431            // None of, or one of equalizer band items should be enabled.
432            if Self::EQ_BAND_ITEMS.iter().find(|i| input.0.eq(i)).is_some() {
433                if value {
434                    Self::BOOL_ITEMS
435                        .iter()
436                        .zip(&mut state.bool_items)
437                        .filter(|(i, v)| {
438                            !input.0.eq(i)
439                                && **v
440                                && Self::EQ_BAND_ITEMS.iter().find(|item| item.eq(i)).is_some()
441                        })
442                        .for_each(|(i, v)| {
443                            *v = false;
444                            outputs.push((*i, ItemValue::Bool(*v)));
445                        });
446                }
447            }
448        } else if let ItemValue::U16(value) = input.1 {
449            let _ = Self::U16_ITEMS
450                .iter()
451                .zip(&mut state.u16_items)
452                .find(|(i, v)| input.0.eq(i) && !value.eq(v))
453                .map(|(_, v)| {
454                    *v = value;
455                    outputs.push((input.0, ItemValue::U16(*v)));
456                });
457
458            if Self::HAS_BANK && input.0 == MachineItem::Bank {
459                if state.bank != value && value <= Self::BANK_MAX {
460                    state.bank = value;
461                    outputs.push((MachineItem::Bank, ItemValue::U16(state.bank)));
462                }
463            }
464        }
465
466        outputs
467    }
468}
469
470/// The trait to operate LED in surface.
471pub trait TascamSurfaceLedOperation<T> {
472    fn operate_leds(
473        state: &mut T,
474        machine_value: &(MachineItem, ItemValue),
475        req: &mut FwReq,
476        node: &mut FwNode,
477        timeout_ms: u32,
478    ) -> Result<(), Error>;
479
480    fn clear_leds(
481        state: &mut T,
482        req: &mut FwReq,
483        node: &mut FwNode,
484        timeout_ms: u32,
485    ) -> Result<(), Error>;
486}
487
488/// The trait to operate local state of surface.
489pub trait TascamSurfaceStateOperation<T> {
490    /// Initialize the state.
491    fn init(state: &mut T);
492
493    /// Peek machine value from image and event.
494    fn peek(
495        state: &T,
496        image: &[u32],
497        index: u32,
498        before: u32,
499        after: u32,
500    ) -> Vec<(MachineItem, ItemValue)>;
501
502    /// Ack the machine value returned from peek method.
503    fn ack(state: &mut T, machine_value: &(MachineItem, ItemValue));
504}
505
506/// Common state of surface.
507#[derive(Default, Debug, Clone, PartialEq, Eq)]
508pub struct TascamSurfaceCommonState {
509    stateful_items: Vec<bool>,
510    enabled_leds: LedState,
511}
512
513/// The trait to express specification of normal LEDs.
514pub trait TascamSurfaceLedNormalSpecification {
515    const NORMAL_LEDS: &'static [(&'static [MachineItem], &'static [u16])];
516}
517
518impl<O> TascamSurfaceLedOperation<TascamSurfaceCommonState> for O
519where
520    O: TascamSurfaceLedNormalSpecification,
521{
522    fn operate_leds(
523        state: &mut TascamSurfaceCommonState,
524        machine_value: &(MachineItem, ItemValue),
525        req: &mut FwReq,
526        node: &mut FwNode,
527        timeout_ms: u32,
528    ) -> Result<(), Error> {
529        if let ItemValue::Bool(value) = machine_value.1 {
530            if let Some((_, positions)) = Self::NORMAL_LEDS.iter().find(|(items, _)| {
531                if items.len() == 1 {
532                    machine_value.0.eq(&items[0])
533                } else {
534                    items.iter().find(|i| machine_value.0.eq(i)).is_some()
535                }
536            }) {
537                operate_led_cached(
538                    &mut state.enabled_leds,
539                    req,
540                    node,
541                    positions[0],
542                    value,
543                    timeout_ms,
544                )?;
545            }
546        }
547
548        Ok(())
549    }
550
551    fn clear_leds(
552        state: &mut TascamSurfaceCommonState,
553        req: &mut FwReq,
554        node: &mut FwNode,
555        timeout_ms: u32,
556    ) -> Result<(), Error> {
557        clear_leds(&mut state.enabled_leds, req, node, timeout_ms)
558    }
559}
560
561/// The trait to express specification for common state of surface.
562pub trait TascamSurfaceStateCommonSpecification {
563    /// The surface items to be stateful.
564    const STATEFUL_ITEMS: &'static [(SurfaceBoolValue, MachineItem)];
565    /// The surface items to be stateless.
566    const STATELESS_ITEMS: &'static [(SurfaceBoolValue, MachineItem)];
567    /// The surface rotaries.
568    const ROTARIES: &'static [(SurfaceU16Value, MachineItem)];
569    /// The surface faders.
570    const FADERS: &'static [(SurfaceBoolValue, SurfaceU16Value, MachineItem)];
571}
572
573impl<O> TascamSurfaceStateOperation<TascamSurfaceCommonState> for O
574where
575    O: TascamSurfaceStateCommonSpecification,
576{
577    fn init(state: &mut TascamSurfaceCommonState) {
578        state.stateful_items = vec![Default::default(); Self::STATEFUL_ITEMS.len()];
579    }
580
581    fn peek(
582        state: &TascamSurfaceCommonState,
583        image: &[u32],
584        index: u32,
585        before: u32,
586        after: u32,
587    ) -> Vec<(MachineItem, ItemValue)> {
588        let mut machine_values = Vec::new();
589
590        Self::STATEFUL_ITEMS
591            .iter()
592            .zip(&state.stateful_items)
593            .filter(|((bool_val, _), _)| {
594                detect_stateful_bool_action(bool_val, index, before, after)
595            })
596            .for_each(|((_, item), &s)| machine_values.push((*item, ItemValue::Bool(!s))));
597
598        Self::STATELESS_ITEMS
599            .iter()
600            .filter(|(bool_val, _)| detect_bool_action(bool_val, index, before, after))
601            .for_each(|(bool_val, item)| {
602                let value = detect_bool_value(bool_val, before);
603                machine_values.push((*item, ItemValue::Bool(value)));
604            });
605
606        Self::ROTARIES
607            .iter()
608            .filter(|(u16_val, _)| detect_u16_action(u16_val, index, before, after))
609            .for_each(|(u16_val, item)| {
610                let value = detect_u16_value(u16_val, after);
611                machine_values.push((*item, ItemValue::U16(value)));
612            });
613
614        Self::FADERS
615            .iter()
616            .filter(|(bool_val, _, _)| detect_bool_action(bool_val, index, before, after))
617            .for_each(|(_, u16_val, item)| {
618                let value = detect_u16_value_in_image(u16_val, image);
619                machine_values.push((*item, ItemValue::U16(value)));
620            });
621
622        machine_values
623    }
624
625    fn ack(state: &mut TascamSurfaceCommonState, machine_value: &(MachineItem, ItemValue)) {
626        if let ItemValue::Bool(val) = machine_value.1 {
627            Self::STATEFUL_ITEMS
628                .iter()
629                .zip(&mut state.stateful_items)
630                .find(|((_, item), _)| machine_value.0.eq(item))
631                .map(|((_, _), s)| *s = val);
632        }
633    }
634}
635
636/// Boolean value in surface image.
637#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
638pub struct SurfaceBoolValue(usize, u32); // index, mask.
639
640/// U16 value in surface image.
641#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
642pub struct SurfaceU16Value(usize, u32, usize); // index, mask, shift
643
644fn detect_stateful_bool_action(
645    bool_val: &SurfaceBoolValue,
646    index: u32,
647    before: u32,
648    after: u32,
649) -> bool {
650    bool_val.0 == index as usize && (before ^ after) & bool_val.1 > 0 && before & bool_val.1 > 0
651}
652
653fn detect_bool_action(bool_val: &SurfaceBoolValue, index: u32, before: u32, after: u32) -> bool {
654    bool_val.0 == index as usize && (before ^ after) & bool_val.1 > 0
655}
656
657fn detect_bool_value(bool_val: &SurfaceBoolValue, before: u32) -> bool {
658    before & bool_val.1 > 0
659}
660
661fn detect_u16_action(u16_val: &SurfaceU16Value, index: u32, before: u32, after: u32) -> bool {
662    u16_val.0 == index as usize && (before ^ after) & u16_val.1 > 0
663}
664
665fn detect_u16_value(u16_val: &SurfaceU16Value, after: u32) -> u16 {
666    ((after & u16_val.1) >> u16_val.2) as u16
667}
668
669fn detect_u16_value_in_image(u16_val: &SurfaceU16Value, image: &[u32]) -> u16 {
670    ((image[u16_val.0] & u16_val.1) >> u16_val.2) as u16
671}
672
673#[derive(Default, Debug, Clone, PartialEq, Eq)]
674struct LedState(Vec<u16>);
675
676fn operate_led(
677    req: &mut FwReq,
678    node: &mut FwNode,
679    pos: u16,
680    enable: bool,
681    timeout_ms: u32,
682) -> Result<(), Error> {
683    let mut frame = [0; 4];
684    frame[0..2].copy_from_slice(&(enable as u16).to_be_bytes());
685    frame[2..4].copy_from_slice(&pos.to_be_bytes());
686    write_quadlet(req, node, LED_OFFSET, &mut frame, timeout_ms)
687}
688
689fn operate_led_cached(
690    state: &mut LedState,
691    req: &mut FwReq,
692    node: &mut FwNode,
693    pos: u16,
694    enable: bool,
695    timeout_ms: u32,
696) -> Result<(), Error> {
697    operate_led(req, node, pos, enable, timeout_ms).map(|_| {
698        if !enable {
699            state.0.retain(|&p| p != pos);
700        } else if state.0.iter().find(|&p| *p == pos).is_none() {
701            state.0.push(pos);
702        }
703    })
704}
705
706fn clear_leds(
707    state: &mut LedState,
708    req: &mut FwReq,
709    node: &mut FwNode,
710    timeout_ms: u32,
711) -> Result<(), Error> {
712    let cache = state.0.to_vec();
713    cache
714        .iter()
715        .try_for_each(|&pos| operate_led_cached(state, req, node, pos, false, timeout_ms))
716}
717
718/// The trait for operation of FireWire LED.
719pub trait FireWireLedOperation {
720    const POSITIONS: &'static [u16];
721
722    fn operate_firewire_led(
723        req: &mut FwReq,
724        node: &mut FwNode,
725        enable: bool,
726        timeout_ms: u32,
727    ) -> Result<(), Error> {
728        operate_led(req, node, Self::POSITIONS[0], enable, timeout_ms)
729    }
730}