opentitanlib/app/
mod.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5//! Useful modules for OpenTitanTool application development.
6pub mod command;
7pub mod config;
8
9mod gpio;
10mod i2c;
11mod spi;
12
13use crate::debug::openocd::OpenOcdJtagChain;
14use crate::io::emu::Emulator;
15use crate::io::gpio::{GpioBitbanging, GpioMonitoring, GpioPin, PinMode, PullMode};
16use crate::io::i2c::Bus;
17use crate::io::ioexpander::IoExpander;
18use crate::io::jtag::{JtagChain, JtagParams};
19use crate::io::spi::{Target, TransferMode};
20use crate::io::uart::Uart;
21use crate::transport::{
22    Capability, MaintainConnection, ProgressIndicator, ProxyOps, Transport, TransportError,
23    TransportInterfaceType, ioexpander,
24};
25
26use anyhow::{Result, bail, ensure};
27use indicatif::{ProgressBar, ProgressStyle};
28use serialport::Parity;
29use std::any::Any;
30use std::cell::{Cell, RefCell};
31use std::collections::hash_map::Entry;
32use std::collections::{HashMap, HashSet};
33use std::path::PathBuf;
34use std::rc::Rc;
35use std::time::Duration;
36use std::vec::Vec;
37
38pub struct NoProgressBar;
39
40impl ProgressIndicator for NoProgressBar {
41    fn new_stage(&self, _name: &str, _total: usize) {}
42    fn progress(&self, _absolute: usize) {}
43}
44
45/// Helper struct for displaying progress bars for operations which may have multiple stages
46/// (e.g. erasing then writing), or whose byte size may not be known until the operation is
47/// underway.
48pub struct StagedProgressBar {
49    pub current_progress_bar: Rc<RefCell<Option<indicatif::ProgressBar>>>,
50}
51
52impl Default for StagedProgressBar {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58impl StagedProgressBar {
59    const DEFAULT_TEMPLATE: &str = "[{elapsed_precise}] [{wide_bar}] {bytes}/{total_bytes} ({eta})";
60    const STAGE_TEMPLATE: &str =
61        "{msg}: [{elapsed_precise}] [{wide_bar}] {bytes}/{total_bytes} ({eta})";
62
63    pub fn new() -> Self {
64        Self {
65            current_progress_bar: Rc::new(RefCell::new(None)),
66        }
67    }
68
69    pub fn enable_steady_tick(&self, duration: Duration) {
70        let bar = self.current_progress_bar.borrow();
71        let bar = bar.as_ref().unwrap();
72        bar.enable_steady_tick(duration);
73    }
74
75    /// Returns the overall bytes per second for the most recent stage (either completed or in
76    /// progress).
77    pub fn bytes_per_second(&self) -> f64 {
78        let bar = self.current_progress_bar.borrow();
79        let bar = bar.as_ref().unwrap();
80        bar.length().unwrap() as f64 / bar.elapsed().as_secs_f64()
81    }
82}
83
84impl ProgressIndicator for StagedProgressBar {
85    fn new_stage(&self, name: &str, total: usize) {
86        let progress = ProgressBar::new(total as u64);
87        if name.is_empty() {
88            progress.set_style(
89                ProgressStyle::default_bar()
90                    .template(Self::DEFAULT_TEMPLATE)
91                    .unwrap(),
92            );
93        } else {
94            progress.set_style(
95                ProgressStyle::default_bar()
96                    .template(Self::STAGE_TEMPLATE)
97                    .unwrap(),
98            );
99        }
100        self.current_progress_bar
101            .borrow_mut()
102            .replace(progress.with_message(name.to_string()));
103    }
104
105    fn progress(&self, pos: usize) {
106        let bar = self.current_progress_bar.borrow();
107        let bar = bar.as_ref().unwrap();
108        if pos as u64 == bar.length().unwrap() {
109            bar.finish();
110            return;
111        }
112        bar.set_position(pos as u64);
113    }
114}
115
116#[derive(Clone, Copy, Default, Debug)]
117pub struct PinConfiguration {
118    /// The input/output mode of the GPIO pin.
119    pub mode: Option<PinMode>,
120    /// The default/initial level of the pin (true means high), has effect only in `PushPull` or
121    /// `OpenDrain` modes.
122    pub level: Option<bool>,
123    /// Whether the pin has pullup/down resistor enabled.
124    pub pull_mode: Option<PullMode>,
125    /// The default/initial analog level of the pin in Volts, has effect only in `AnalogOutput`
126    /// mode.
127    pub volts: Option<f32>,
128    pub invert: Option<bool>,
129}
130
131fn merge_field<T>(f1: &mut Option<T>, f2: &Option<T>) -> Result<(), ()>
132where
133    T: PartialEq<T> + Clone,
134{
135    match (&*f1, f2) {
136        (Some(v1), Some(v2)) if *v1 != *v2 => return Err(()),
137        (None, _) => *f1 = f2.clone(),
138        _ => (),
139    }
140    Ok(())
141}
142
143impl PinConfiguration {
144    /// Sometimes one configuration file specifies OpenDrain while leaving out the level, and
145    /// another file specifies high level, while leaving out the mode.  This method will merge
146    /// declarations from multiple files, as long as they are not conflicting (e.g. both PushPull
147    /// and OpenDrain, or both high and low level.)
148    fn merge(&mut self, other: &PinConfiguration) -> Result<(), ()> {
149        merge_field(&mut self.mode, &other.mode)?;
150        merge_field(&mut self.level, &other.level)?;
151        merge_field(&mut self.pull_mode, &other.pull_mode)?;
152        merge_field(&mut self.volts, &other.volts)?;
153        merge_field(&mut self.invert, &other.invert)?;
154        Ok(())
155    }
156
157    /// Merges the specified configuration as an override to this configuration. Only the non-empty
158    /// configuration fields from the override are applied.
159    #[must_use]
160    fn override_with(&self, overrides: Option<&Self>) -> Self {
161        let Some(overrides) = overrides else {
162            return *self;
163        };
164        let mut result = *self;
165        overrides.mode.map(|v| result.mode.replace(v));
166        overrides.level.map(|v| result.level.replace(v));
167        overrides.pull_mode.map(|v| result.pull_mode.replace(v));
168        overrides.volts.map(|v| result.volts.replace(v));
169        overrides.invert.map(|v| result.invert.replace(v));
170        result
171    }
172}
173
174#[derive(Default, Debug)]
175pub struct UartConfiguration {
176    pub underlying_instance: String,
177    pub baud_rate: Option<u32>,
178    pub parity: Option<config::UartParity>,
179    pub stop_bits: Option<config::UartStopBits>,
180    pub alias_of: Option<String>,
181}
182
183#[derive(Default, Debug)]
184pub struct SpiConfiguration {
185    pub underlying_instance: String,
186    pub mode: Option<TransferMode>,
187    pub serial_clock: Option<String>,
188    pub host_out_device_in: Option<String>,
189    pub host_in_device_out: Option<String>,
190    pub chip_select: Option<String>,
191    pub gsc_ready: Option<String>,
192    pub bits_per_word: Option<u32>,
193    pub bits_per_sec: Option<u32>,
194}
195
196#[derive(Default, Debug)]
197pub struct I2cConfiguration {
198    pub underlying_instance: String,
199    pub default_addr: Option<u8>,
200    pub bits_per_sec: Option<u32>,
201}
202
203pub struct TransportWrapperBuilder {
204    interface: String,
205    disable_dft_on_reset: bool,
206    openocd_adapter_config: Option<PathBuf>,
207    provides_list: Vec<(String, String)>,
208    requires_list: Vec<(String, String)>,
209    pin_alias_map: HashMap<String, String>,
210    pin_on_io_expander_map: HashMap<String, config::IoExpanderPin>,
211    pin_conf_list: Vec<(String, PinConfiguration)>,
212    uart_conf_map: HashMap<String, config::UartConfiguration>,
213    spi_conf_map: HashMap<String, config::SpiConfiguration>,
214    i2c_conf_map: HashMap<String, config::I2cConfiguration>,
215    strapping_conf_map: HashMap<String, Vec<(String, PinConfiguration)>>,
216    io_expander_conf_map: HashMap<String, config::IoExpander>,
217    gpio_conf: HashSet<String>,
218}
219
220// This is the structure to be passed to each Command implementation,
221// replacing the "bare" Transport argument.  The fields other than
222// transport will have been computed from a number ConfigurationFiles.
223pub struct TransportWrapper {
224    transport: Rc<dyn Transport>,
225    disable_dft_on_reset: Cell<bool>,
226    openocd_adapter_config: Option<PathBuf>,
227    provides_map: HashMap<String, String>,
228    pin_map: HashMap<String, String>,
229    artificial_pin_map: HashMap<String, Rc<dyn GpioPin>>,
230    pin_conf_map: HashMap<String, PinConfiguration>,
231    uart_conf_map: HashMap<String, UartConfiguration>,
232    spi_conf_map: HashMap<String, SpiConfiguration>,
233    i2c_conf_map: HashMap<String, I2cConfiguration>,
234    strapping_conf_map: HashMap<String, HashMap<String, PinConfiguration>>,
235    gpio_conf: HashSet<String>,
236    //
237    // Below fields are lazily populated, as instances are requested.
238    //
239    // Caching SPI and I2C wrapper instances is necessary to avoid repeatedly re-applying settings
240    // such as speed and chip select pin declared along with an alias of the physical port of the
241    // debugger.  This is both an optimization, as well as necessary to not "lose" the asserted
242    // state of the chip select.
243    //
244    pin_instance_map: RefCell<HashMap<String, Rc<gpio::GpioPinWrapper>>>,
245    spi_physical_map: RefCell<HashMap<String, Rc<spi::PhysicalSpiWrapper>>>,
246    spi_logical_map: RefCell<HashMap<String, Rc<spi::LogicalSpiWrapper>>>,
247    i2c_physical_map: RefCell<HashMap<String, Rc<i2c::PhysicalI2cWrapper>>>,
248    i2c_logical_map: RefCell<HashMap<String, Rc<i2c::LogicalI2cWrapper>>>,
249}
250
251impl TransportWrapperBuilder {
252    pub fn new(interface: String, disable_dft_on_reset: bool) -> Self {
253        Self {
254            interface,
255            disable_dft_on_reset,
256            openocd_adapter_config: None,
257            provides_list: Vec::new(),
258            requires_list: Vec::new(),
259            pin_alias_map: HashMap::new(),
260            pin_on_io_expander_map: HashMap::new(),
261            pin_conf_list: Vec::new(),
262            uart_conf_map: HashMap::new(),
263            spi_conf_map: HashMap::new(),
264            i2c_conf_map: HashMap::new(),
265            strapping_conf_map: HashMap::new(),
266            io_expander_conf_map: HashMap::new(),
267            gpio_conf: HashSet::new(),
268        }
269    }
270
271    fn record_pin_conf(
272        pin_conf_list: &mut Vec<(String, PinConfiguration)>,
273        pin_conf: &config::PinConfiguration,
274    ) {
275        if (None, None, None, None, None)
276            == (
277                pin_conf.mode,
278                pin_conf.pull_mode,
279                pin_conf.level,
280                pin_conf.volts,
281                pin_conf.invert,
282            )
283        {
284            return;
285        }
286        let mut conf_entry: PinConfiguration = PinConfiguration::default();
287        if let Some(pin_mode) = pin_conf.mode {
288            conf_entry.mode = Some(pin_mode);
289        }
290        if let Some(pull_mode) = pin_conf.pull_mode {
291            conf_entry.pull_mode = Some(pull_mode);
292        }
293        if let Some(level) = pin_conf.level {
294            conf_entry.level = Some(level);
295        }
296        if let Some(volts) = pin_conf.volts {
297            conf_entry.volts = Some(volts);
298        }
299        if let Some(invert) = pin_conf.invert {
300            conf_entry.invert = Some(invert);
301        }
302        pin_conf_list.push((pin_conf.name.to_string(), conf_entry))
303    }
304
305    fn record_uart_conf(
306        uart_conf_map: &mut HashMap<String, config::UartConfiguration>,
307        uart_conf: &config::UartConfiguration,
308    ) -> Result<(), ()> {
309        let entry = uart_conf_map
310            .entry(uart_conf.name.to_uppercase().to_string())
311            .or_insert_with(|| config::UartConfiguration {
312                name: uart_conf.name.clone(),
313                ..Default::default()
314            });
315        merge_field(&mut entry.baudrate, &uart_conf.baudrate)?;
316        merge_field(&mut entry.parity, &uart_conf.parity)?;
317        merge_field(&mut entry.stopbits, &uart_conf.stopbits)?;
318        merge_field(&mut entry.alias_of, &uart_conf.alias_of)?;
319        Ok(())
320    }
321
322    fn record_spi_conf(
323        spi_conf_map: &mut HashMap<String, config::SpiConfiguration>,
324        spi_conf: &config::SpiConfiguration,
325    ) -> Result<(), ()> {
326        let entry = spi_conf_map
327            .entry(spi_conf.name.to_string())
328            .or_insert_with(|| config::SpiConfiguration {
329                name: spi_conf.name.clone(),
330                ..Default::default()
331            });
332        merge_field(&mut entry.mode, &spi_conf.mode)?;
333        merge_field(&mut entry.bits_per_word, &spi_conf.bits_per_word)?;
334        merge_field(&mut entry.bits_per_sec, &spi_conf.bits_per_sec)?;
335        merge_field(&mut entry.serial_clock, &spi_conf.serial_clock)?;
336        merge_field(&mut entry.host_out_device_in, &spi_conf.host_out_device_in)?;
337        merge_field(&mut entry.host_in_device_out, &spi_conf.host_in_device_out)?;
338        merge_field(&mut entry.chip_select, &spi_conf.chip_select)?;
339        merge_field(&mut entry.alias_of, &spi_conf.alias_of)?;
340        Ok(())
341    }
342
343    fn record_i2c_conf(
344        i2c_conf_map: &mut HashMap<String, config::I2cConfiguration>,
345        i2c_conf: &config::I2cConfiguration,
346    ) -> Result<(), ()> {
347        let entry = i2c_conf_map
348            .entry(i2c_conf.name.to_string())
349            .or_insert_with(|| config::I2cConfiguration {
350                name: i2c_conf.name.clone(),
351                ..Default::default()
352            });
353        merge_field(&mut entry.address, &i2c_conf.address)?;
354        merge_field(&mut entry.bits_per_sec, &i2c_conf.bits_per_sec)?;
355        merge_field(&mut entry.alias_of, &i2c_conf.alias_of)?;
356        Ok(())
357    }
358
359    pub fn add_configuration_file(&mut self, file: config::ConfigurationFile) -> Result<()> {
360        if let Some(interface) = file.interface {
361            if self.interface.is_empty() {
362                self.interface = interface;
363            } else if self.interface == interface {
364                // Same value for interface between in command line and configuration file (or
365                // between multiple configuration files), nothing to update.
366            } else {
367                bail!(TransportError::InconsistentInterfaceConf(
368                    self.interface.to_string(),
369                    interface,
370                ))
371            }
372        }
373        for (key, value) in file.provides {
374            self.provides_list.push((key, value));
375        }
376        for (key, value) in file.requires {
377            self.requires_list.push((key, value));
378        }
379        // Merge content of configuration file into pin_map and other members.
380        for pin_conf in file.pins {
381            if let Some(alias_of) = &pin_conf.alias_of {
382                self.pin_alias_map
383                    .insert(pin_conf.name.to_uppercase(), alias_of.clone());
384            } else if let Some(on_io_expander) = &pin_conf.on_io_expander {
385                ensure!(
386                    &pin_conf.alias_of.is_none(),
387                    TransportError::InconsistentConf(
388                        TransportInterfaceType::Gpio,
389                        pin_conf.name.to_string()
390                    )
391                );
392                let uppercase_name = pin_conf.name.to_uppercase();
393                ensure!(
394                    !self.pin_on_io_expander_map.contains_key(&uppercase_name),
395                    TransportError::InconsistentConf(
396                        TransportInterfaceType::Gpio,
397                        pin_conf.name.to_string()
398                    )
399                );
400                self.pin_on_io_expander_map
401                    .insert(uppercase_name, on_io_expander.clone());
402            }
403            // Record default input / open drain / push pull configuration to the pin.
404            Self::record_pin_conf(&mut self.pin_conf_list, &pin_conf);
405        }
406        for strapping_conf in file.strappings {
407            let strapping_pin_map = self
408                .strapping_conf_map
409                .entry(strapping_conf.name.to_uppercase())
410                .or_default();
411            for pin_conf in strapping_conf.pins {
412                ensure!(
413                    pin_conf.invert.is_none(),
414                    TransportError::InvalidConfStrapInvert(
415                        strapping_conf.name.to_string(),
416                        pin_conf.name.to_string()
417                    )
418                );
419                ensure!(
420                    pin_conf.alias_of.is_none(),
421                    TransportError::InvalidConfStrapAlias(
422                        strapping_conf.name.to_string(),
423                        pin_conf.name.to_string()
424                    )
425                );
426                Self::record_pin_conf(strapping_pin_map, &pin_conf);
427            }
428        }
429        for spi_conf in file.spi {
430            Self::record_spi_conf(&mut self.spi_conf_map, &spi_conf).map_err(|_| {
431                TransportError::InconsistentConf(
432                    TransportInterfaceType::Spi,
433                    spi_conf.name.to_string(),
434                )
435            })?;
436        }
437        for i2c_conf in file.i2c {
438            Self::record_i2c_conf(&mut self.i2c_conf_map, &i2c_conf).map_err(|_| {
439                TransportError::InconsistentConf(
440                    TransportInterfaceType::I2c,
441                    i2c_conf.name.to_string(),
442                )
443            })?;
444        }
445        for uart_conf in file.uarts {
446            Self::record_uart_conf(&mut self.uart_conf_map, &uart_conf).map_err(|_| {
447                TransportError::InconsistentConf(
448                    TransportInterfaceType::Uart,
449                    uart_conf.name.to_string(),
450                )
451            })?;
452        }
453        for io_expander_conf in file.io_expanders {
454            match self
455                .io_expander_conf_map
456                .entry(io_expander_conf.name.to_string())
457            {
458                Entry::Vacant(v) => {
459                    v.insert(io_expander_conf);
460                }
461                Entry::Occupied(_) => bail!(TransportError::InconsistentConf(
462                    TransportInterfaceType::IoExpander,
463                    io_expander_conf.name
464                )),
465            }
466        }
467        for pin in file.gpios {
468            self.gpio_conf.insert(pin);
469        }
470        Ok(())
471    }
472
473    fn consolidate_provides_map(
474        result_provides_map: &mut HashMap<String, String>,
475        provides_list: Vec<(String, String)>,
476    ) -> Result<()> {
477        for (key, value) in provides_list {
478            match result_provides_map.entry(key.clone()) {
479                Entry::Vacant(v) => {
480                    v.insert(value);
481                }
482                Entry::Occupied(v) => {
483                    if v.get() != &value {
484                        bail!(TransportError::InconsistentConf(
485                            TransportInterfaceType::Provides,
486                            key
487                        ))
488                    }
489                }
490            }
491        }
492        Ok(())
493    }
494
495    fn verify_requires_list(
496        provides_map: &HashMap<String, String>,
497        requires_list: &Vec<(String, String)>,
498    ) -> Result<()> {
499        for (key, required_value) in requires_list {
500            match provides_map.get(key) {
501                Some(actual_value) if actual_value == required_value => (),
502                Some(actual_value) => bail!(TransportError::RequiresUnequal(
503                    key.to_string(),
504                    required_value.to_string(),
505                    actual_value.to_string()
506                )),
507                None => bail!(TransportError::RequiresMissing(
508                    key.to_string(),
509                    required_value.to_string()
510                )),
511            }
512        }
513        Ok(())
514    }
515
516    fn consolidate_pin_conf_map(
517        pin_alias_map: &HashMap<String, String>,
518        pin_conf_list: &Vec<(String, PinConfiguration)>,
519    ) -> Result<HashMap<String, PinConfiguration>> {
520        let mut result_pin_conf_map: HashMap<String, PinConfiguration> = HashMap::new();
521        for (name, conf) in pin_conf_list {
522            result_pin_conf_map
523                .entry(map_name(pin_alias_map, name))
524                .or_default()
525                .merge(conf)
526                .map_err(|_| {
527                    TransportError::InconsistentConf(TransportInterfaceType::Gpio, name.to_string())
528                })?;
529        }
530        Ok(result_pin_conf_map)
531    }
532
533    fn resolve_uart_conf(
534        name: &str,
535        uart_conf_map: &HashMap<String, config::UartConfiguration>,
536    ) -> Result<UartConfiguration> {
537        if let Some(entry) = uart_conf_map.get(name.to_uppercase().as_str()) {
538            let mut conf = if let Some(ref alias_of) = entry.alias_of {
539                Self::resolve_uart_conf(alias_of.as_str(), uart_conf_map)?
540            } else {
541                UartConfiguration {
542                    underlying_instance: name.to_uppercase().to_string(),
543                    ..Default::default()
544                }
545            };
546            // Apply configuration from this level
547            if let Some(baud_rate) = entry.baudrate {
548                conf.baud_rate = Some(baud_rate);
549            }
550            if let Some(parity) = entry.parity {
551                conf.parity = Some(parity);
552            }
553            if let Some(stop_bits) = entry.stopbits {
554                conf.stop_bits = Some(stop_bits);
555            }
556            Ok(conf)
557        } else {
558            Ok(UartConfiguration {
559                underlying_instance: name.to_string(),
560                ..Default::default()
561            })
562        }
563    }
564
565    fn consolidate_uart_conf_map(
566        uart_conf_map: &HashMap<String, config::UartConfiguration>,
567    ) -> Result<HashMap<String, UartConfiguration>> {
568        let mut resolved_uart_conf_map = HashMap::new();
569        for name in uart_conf_map.keys() {
570            resolved_uart_conf_map
571                .insert(name.clone(), Self::resolve_uart_conf(name, uart_conf_map)?);
572        }
573        Ok(resolved_uart_conf_map)
574    }
575
576    fn resolve_spi_conf(
577        name: &str,
578        spi_conf_map: &HashMap<String, config::SpiConfiguration>,
579        pin_alias_map: &HashMap<String, String>,
580    ) -> SpiConfiguration {
581        if let Some(entry) = spi_conf_map.get(name) {
582            let mut conf = if let Some(ref alias_of) = entry.alias_of {
583                Self::resolve_spi_conf(alias_of.as_str(), spi_conf_map, pin_alias_map)
584            } else {
585                SpiConfiguration {
586                    underlying_instance: name.to_string(),
587                    ..Default::default()
588                }
589            };
590            // Apply configuration from this level
591            if let Some(serial_clock) = entry.serial_clock.as_ref() {
592                conf.serial_clock = Some(map_name(pin_alias_map, serial_clock));
593            }
594            if let Some(host_out_device_in) = entry.host_out_device_in.as_ref() {
595                conf.host_out_device_in = Some(map_name(pin_alias_map, host_out_device_in));
596            }
597            if let Some(host_in_device_out) = entry.host_in_device_out.as_ref() {
598                conf.host_in_device_out = Some(map_name(pin_alias_map, host_in_device_out));
599            }
600            if let Some(chip_select) = entry.chip_select.as_ref() {
601                conf.chip_select = Some(map_name(pin_alias_map, chip_select));
602            }
603            if let Some(mode) = entry.mode {
604                conf.mode = Some(mode);
605            }
606            if let Some(bits_per_sec) = entry.bits_per_sec {
607                conf.bits_per_sec = Some(bits_per_sec);
608            }
609            if let Some(bits_per_word) = entry.bits_per_word {
610                conf.bits_per_word = Some(bits_per_word);
611            }
612            conf
613        } else {
614            SpiConfiguration {
615                underlying_instance: name.to_string(),
616                ..Default::default()
617            }
618        }
619    }
620
621    fn consolidate_spi_conf_map(
622        spi_conf_map: &HashMap<String, config::SpiConfiguration>,
623        pin_alias_map: &HashMap<String, String>,
624    ) -> Result<HashMap<String, SpiConfiguration>> {
625        let mut resolved_spi_conf_map = HashMap::new();
626        for name in spi_conf_map.keys() {
627            resolved_spi_conf_map.insert(
628                name.clone(),
629                Self::resolve_spi_conf(name, spi_conf_map, pin_alias_map),
630            );
631        }
632        Ok(resolved_spi_conf_map)
633    }
634
635    fn resolve_i2c_conf(
636        name: &str,
637        i2c_conf_map: &HashMap<String, config::I2cConfiguration>,
638    ) -> I2cConfiguration {
639        if let Some(entry) = i2c_conf_map.get(name) {
640            let mut conf = if let Some(ref alias_of) = entry.alias_of {
641                Self::resolve_i2c_conf(alias_of.as_str(), i2c_conf_map)
642            } else {
643                I2cConfiguration {
644                    underlying_instance: name.to_string(),
645                    ..Default::default()
646                }
647            };
648            // Apply configuration from this level
649            if let Some(addr) = entry.address {
650                conf.default_addr = Some(addr);
651            }
652            if let Some(bits_per_sec) = entry.bits_per_sec {
653                conf.bits_per_sec = Some(bits_per_sec);
654            }
655            conf
656        } else {
657            I2cConfiguration {
658                underlying_instance: name.to_string(),
659                ..Default::default()
660            }
661        }
662    }
663
664    fn consolidate_i2c_conf_map(
665        i2c_conf_map: &HashMap<String, config::I2cConfiguration>,
666    ) -> Result<HashMap<String, I2cConfiguration>> {
667        let mut resolved_i2c_conf_map = HashMap::new();
668        for name in i2c_conf_map.keys() {
669            resolved_i2c_conf_map.insert(name.clone(), Self::resolve_i2c_conf(name, i2c_conf_map));
670        }
671        Ok(resolved_i2c_conf_map)
672    }
673
674    pub fn get_interface(&self) -> &str {
675        &self.interface
676    }
677
678    pub fn set_openocd_adapter_config(&mut self, openocd_adapter_config: &Option<PathBuf>) {
679        self.openocd_adapter_config = openocd_adapter_config.clone();
680    }
681
682    pub fn build(
683        self,
684        transport: Box<dyn crate::transport::Transport>,
685    ) -> Result<TransportWrapper> {
686        let mut provides_map = if transport
687            .capabilities()?
688            .request(Capability::PROXY)
689            .ok()
690            .is_ok()
691        {
692            transport.proxy_ops()?.provides_map()?
693        } else {
694            HashMap::new()
695        };
696        Self::consolidate_provides_map(&mut provides_map, self.provides_list)?;
697        Self::verify_requires_list(&provides_map, &self.requires_list)?;
698
699        let pin_conf_map =
700            Self::consolidate_pin_conf_map(&self.pin_alias_map, &self.pin_conf_list)?;
701        let mut strapping_conf_map: HashMap<String, HashMap<String, PinConfiguration>> =
702            HashMap::new();
703        for (strapping_name, pin_conf_map) in self.strapping_conf_map {
704            strapping_conf_map.insert(
705                strapping_name,
706                Self::consolidate_pin_conf_map(&self.pin_alias_map, &pin_conf_map)?,
707            );
708        }
709        let uart_conf_map = Self::consolidate_uart_conf_map(&self.uart_conf_map)?;
710        let spi_conf_map = Self::consolidate_spi_conf_map(&self.spi_conf_map, &self.pin_alias_map)?;
711        let i2c_conf_map = Self::consolidate_i2c_conf_map(&self.i2c_conf_map)?;
712        let mut transport_wrapper = TransportWrapper {
713            transport: Rc::from(transport),
714            disable_dft_on_reset: Cell::new(self.disable_dft_on_reset),
715            openocd_adapter_config: self.openocd_adapter_config,
716            provides_map,
717            pin_map: self.pin_alias_map,
718            artificial_pin_map: HashMap::new(),
719            uart_conf_map,
720            pin_conf_map,
721            spi_conf_map,
722            i2c_conf_map,
723            strapping_conf_map,
724            gpio_conf: self.gpio_conf,
725            pin_instance_map: RefCell::new(HashMap::new()),
726            spi_physical_map: RefCell::new(HashMap::new()),
727            spi_logical_map: RefCell::new(HashMap::new()),
728            i2c_physical_map: RefCell::new(HashMap::new()),
729            i2c_logical_map: RefCell::new(HashMap::new()),
730        };
731        let mut io_expanders: HashMap<String, IoExpander> = HashMap::new();
732        for (name, conf) in self.io_expander_conf_map {
733            io_expanders.insert(
734                name.to_string(),
735                ioexpander::create(&conf, &transport_wrapper)?,
736            );
737        }
738        transport_wrapper
739            .artificial_pin_map
740            .insert("NULL".to_string(), Rc::new(NullPin::new()));
741        for (pinname, v) in self.pin_on_io_expander_map {
742            if let Some(io) = io_expanders.get(&v.io_expander) {
743                ensure!(
744                    (v.pin_no as usize) < io.pins.len(),
745                    TransportError::InvalidIoExpanderPinNo(v.io_expander.to_string(), v.pin_no)
746                );
747                transport_wrapper
748                    .artificial_pin_map
749                    .insert(pinname, Rc::clone(&io.pins[v.pin_no as usize]));
750            } else {
751                bail!(TransportError::InvalidIoExpanderName(
752                    v.io_expander.to_string()
753                ));
754            }
755        }
756        Ok(transport_wrapper)
757    }
758}
759
760impl TransportWrapper {
761    pub fn ignore_dft_straps_on_reset(&self) -> Result<()> {
762        self.disable_dft_on_reset.set(false);
763        Ok(())
764    }
765
766    /// Returns a `Capabilities` object to check the capabilities of this
767    /// transport object.
768    pub fn capabilities(&self) -> Result<crate::transport::Capabilities> {
769        let capabilities = self.transport.capabilities()?;
770        if self.openocd_adapter_config.is_some() {
771            Ok(capabilities.add(Capability::JTAG))
772        } else {
773            Ok(capabilities)
774        }
775    }
776
777    /// Returns a string->string map containing user-defined aspects "provided" by the testbed
778    /// setup.  For instance, whether a SPI flash chip is fitted in the socket, or whether pullup
779    /// resistors are suitable for high-speed I2C.
780    pub fn provides_map(&self) -> Result<&HashMap<String, String>> {
781        Ok(&self.provides_map)
782    }
783
784    pub fn query_provides(&self, key: &str) -> Result<&str> {
785        self.provides_map
786            .get(key)
787            .map(String::as_str)
788            .ok_or_else(|| {
789                TransportError::InvalidInstance(TransportInterfaceType::Provides, key.to_string())
790                    .into()
791            })
792    }
793
794    /// Returns a [`JtagChain`] implementation.
795    pub fn jtag(&self, opts: &JtagParams) -> Result<Box<dyn JtagChain + '_>> {
796        if let Some(ref path) = self.openocd_adapter_config {
797            // Use specified external JTAG dongle, instead of the transport driver itself.
798            return Ok(Box::new(OpenOcdJtagChain::new(
799                &std::fs::read_to_string(path)?,
800                opts,
801            )?));
802        }
803        // Use JTAG functionality of the transport driver itself.  (Currently, HyperDebug is the
804        // only transport which has such support.)
805        self.transport.jtag(opts)
806    }
807
808    /// Returns a SPI [`Target`] implementation.
809    pub fn spi(&self, name: &str) -> Result<Rc<dyn Target>> {
810        let name = name.to_uppercase();
811        let mut spi_logical_map = self.spi_logical_map.borrow_mut();
812        if let Some(instance) = spi_logical_map.get(&name) {
813            return Ok(Rc::clone(instance) as Rc<dyn Target>);
814        }
815        if let Some(spi_conf) = self.spi_conf_map.get(&name) {
816            let mut spi_physical_map = self.spi_physical_map.borrow_mut();
817            // Find if we already have a PhysicalSpiWrapper around the requested instance.  If
818            // not, create one.
819            let physical_wrapper = if let Some(instance) =
820                spi_physical_map.get(&spi_conf.underlying_instance)
821            {
822                Rc::clone(instance)
823            } else {
824                let instance = Rc::new(spi::PhysicalSpiWrapper::new(
825                    self.transport.spi(spi_conf.underlying_instance.as_str())?,
826                ));
827                spi_physical_map.insert(spi_conf.underlying_instance.clone(), Rc::clone(&instance));
828                instance
829            };
830
831            // Create a LogicalSpiWrapper referring to the physical port, and carrying the
832            // particular speed and other settings.
833            let new_wrapper = Rc::new(spi::LogicalSpiWrapper::new(
834                &*self.transport,
835                spi_conf,
836                physical_wrapper,
837            )?);
838            spi_logical_map.insert(name, Rc::clone(&new_wrapper));
839            Ok(new_wrapper)
840        } else {
841            self.transport.spi(name.as_str())
842        }
843    }
844
845    /// Returns a I2C [`Bus`] implementation.
846    pub fn i2c(&self, name: &str) -> Result<Rc<dyn Bus>> {
847        let name = name.to_uppercase();
848        let mut i2c_logical_map = self.i2c_logical_map.borrow_mut();
849        if let Some(instance) = i2c_logical_map.get(&name) {
850            return Ok(Rc::clone(instance) as Rc<dyn Bus>);
851        }
852        if let Some(i2c_conf) = self.i2c_conf_map.get(&name) {
853            let mut i2c_physical_map = self.i2c_physical_map.borrow_mut();
854            // Find if we already have a PhysicalI2cWrapper around the requested instance.  If
855            // not, create one.
856            let physical_wrapper = if let Some(instance) =
857                i2c_physical_map.get(&i2c_conf.underlying_instance)
858            {
859                Rc::clone(instance)
860            } else {
861                let instance = Rc::new(i2c::PhysicalI2cWrapper::new(
862                    self.transport.i2c(i2c_conf.underlying_instance.as_str())?,
863                ));
864                i2c_physical_map.insert(i2c_conf.underlying_instance.clone(), Rc::clone(&instance));
865                instance
866            };
867
868            // Create a LogicalI2cWrapper referring to the physical port, and carrying the
869            // particular speed and other settings.
870            let new_wrapper = Rc::new(i2c::LogicalI2cWrapper::new(
871                &*self.transport,
872                i2c_conf,
873                physical_wrapper,
874            )?);
875            i2c_logical_map.insert(name, Rc::clone(&new_wrapper));
876            Ok(new_wrapper)
877        } else {
878            self.transport.i2c(name.as_str())
879        }
880    }
881
882    /// Returns a [`Uart`] implementation.
883    pub fn uart(&self, name: &str) -> Result<Rc<dyn Uart>> {
884        let uart_conf = self.uart_conf_map.get(name.to_uppercase().as_str());
885        let uart_name = uart_conf
886            .map(|uart_conf| uart_conf.underlying_instance.as_str())
887            .unwrap_or(name);
888        let uart = self.transport.uart(uart_name)?;
889        if let Some(conf) = uart_conf {
890            // Since current OT tests harnesses quite freely re-create the Uart
891            // when they expect it to continue receiving in the background, we
892            // try and read the values first and only set if there is a mismatch.
893            if let Some(baud_rate) = conf.baud_rate {
894                if let Ok(current_baud_rate) = uart.get_baudrate() {
895                    if current_baud_rate != baud_rate {
896                        uart.set_baudrate(baud_rate)?;
897                    }
898                } else {
899                    log::warn!("Could not read UART baud rate to check it is {}", baud_rate);
900                }
901            }
902            if let Some(parity) = conf.parity {
903                let new_parity = match parity {
904                    config::UartParity::None => Parity::None,
905                    config::UartParity::Even => Parity::Even,
906                    config::UartParity::Odd => Parity::Odd,
907                    config::UartParity::Mark | config::UartParity::Space => {
908                        log::warn!("Mark & Space parities not yet supported");
909                        Parity::None
910                    }
911                };
912                if let Ok(current_parity) = uart.get_parity() {
913                    if current_parity != new_parity {
914                        uart.set_parity(new_parity)?;
915                    }
916                } else {
917                    log::warn!("Could not read UART parity to check it is {}", new_parity);
918                }
919            }
920            // TODO: stop bits are not yet supported in the UART interface
921        }
922        Ok(uart)
923    }
924
925    /// Returns a [`GpioPin`] implementation.
926    pub fn gpio_pin(&self, name: &str) -> Result<Rc<dyn GpioPin>> {
927        let resolved_pin_name = map_name(&self.pin_map, name);
928        let mut pin_instance_map = self.pin_instance_map.borrow_mut();
929        // Find if we already have a GpioPinWrapper around the requested instance.  If
930        // not, create one.
931        if let Some(instance) = pin_instance_map.get(&resolved_pin_name) {
932            Ok(Rc::clone(instance) as Rc<dyn GpioPin>)
933        } else {
934            let instance = if let Some(pin) = self.artificial_pin_map.get(&resolved_pin_name) {
935                pin.clone()
936            } else {
937                self.transport.gpio_pin(resolved_pin_name.as_str())?
938            };
939            let invert = self
940                .pin_conf_map
941                .get(&resolved_pin_name)
942                .and_then(|conf| conf.invert)
943                .unwrap_or(false);
944            let wrapper = Rc::new(gpio::GpioPinWrapper::new(instance, invert));
945            pin_instance_map.insert(resolved_pin_name, Rc::clone(&wrapper));
946            Ok(wrapper as Rc<dyn GpioPin>)
947        }
948    }
949
950    /// Convenience method, returns a number of [`GpioPin`] implementations.
951    pub fn gpio_pins(&self, names: &[String]) -> Result<Vec<Rc<dyn GpioPin>>> {
952        let mut result = Vec::new();
953        for name in names {
954            result.push(self.gpio_pin(name)?);
955        }
956        Ok(result)
957    }
958
959    pub fn gpios(&self) -> &HashSet<String> {
960        &self.gpio_conf
961    }
962
963    /// Returns a [`GpioMonitoring`] implementation.
964    pub fn gpio_monitoring(&self) -> Result<Rc<dyn GpioMonitoring>> {
965        self.transport.gpio_monitoring()
966    }
967
968    /// Returns a [`GpioBitbanging`] implementation.
969    pub fn gpio_bitbanging(&self) -> Result<Rc<dyn GpioBitbanging>> {
970        self.transport.gpio_bitbanging()
971    }
972
973    pub fn pin_strapping(&self, name: &str) -> Result<PinStrapping> {
974        let proxy = if self.capabilities()?.request(Capability::PROXY).ok().is_ok() {
975            Some(self.proxy_ops()?)
976        } else {
977            None
978        };
979        let mut pins = Vec::new();
980        if let Some(strapping_conf_map) = self.strapping_conf_map.get(name) {
981            for (pin_name, conf) in strapping_conf_map {
982                pins.push(StrappedPin {
983                    pin: self.gpio_pin(pin_name)?,
984                    strapped: *conf,
985                    original: self.pin_conf_map.get(pin_name).copied(),
986                });
987            }
988        } else if proxy.is_none() {
989            bail!(TransportError::InvalidStrappingName(name.to_string()));
990        }
991        Ok(PinStrapping {
992            proxy,
993            name: name.to_string(),
994            pins,
995        })
996    }
997
998    /// Returns a [`Emulator`] implementation.
999    pub fn emulator(&self) -> Result<Rc<dyn Emulator>> {
1000        self.transport.emulator()
1001    }
1002
1003    /// Methods available only on Proxy implementation.
1004    pub fn proxy_ops(&self) -> Result<Rc<dyn ProxyOps>> {
1005        self.transport.proxy_ops()
1006    }
1007
1008    /// Invoke non-standard functionality of some Transport implementations.
1009    pub fn dispatch(&self, action: &dyn Any) -> Result<Option<Box<dyn erased_serde::Serialize>>> {
1010        self.transport.dispatch(action)
1011    }
1012
1013    /// Apply given configuration to a single pins.
1014    fn apply_pin_configuration(&self, name: &str, conf: &PinConfiguration) -> Result<()> {
1015        let pin = self.gpio_pin(name)?;
1016        pin.set(conf.mode, conf.level, conf.pull_mode, conf.volts)
1017    }
1018
1019    /// Apply given configuration to a all the given pins.
1020    fn apply_pin_configurations(
1021        &self,
1022        conf_map: &HashMap<String, PinConfiguration>,
1023        overrides: Option<&HashMap<String, PinConfiguration>>,
1024    ) -> Result<()> {
1025        // If the overrides map specify a pin, then override the default configuration with it
1026        let update_pin = |name, conf: &PinConfiguration| {
1027            self.apply_pin_configuration(
1028                name,
1029                &conf.override_with(overrides.and_then(|o| o.get(name))),
1030            )
1031        };
1032        // Pins on IO expanders will rely on some "direct" pins being configured for I2C and
1033        // possibly MUX strappings.  To account for that, first apply the configuration to all
1034        // "direct" (non-artificial) pins, and then to the rest.  (In theory, an IO expander could
1035        // be cascaded behind other IO expanders, requiring more complicated management of a
1036        // dependency graph, if that ever becomes an issue, a topological sort in
1037        // `TransportWrapperBuilder.build()` would probably be appropriate.)
1038        for (name, conf) in conf_map {
1039            if !self.artificial_pin_map.contains_key(name) {
1040                update_pin(name, conf)?;
1041            }
1042        }
1043        for (name, conf) in conf_map {
1044            if self.artificial_pin_map.contains_key(name) {
1045                update_pin(name, conf)?;
1046            }
1047        }
1048        Ok(())
1049    }
1050
1051    /// Configure all pins as input/output, pullup, etc. as declared in configuration files.
1052    /// Also configure SPI port mode/speed, and other similar settings.
1053    pub fn apply_default_configuration(&self, strapping_name: Option<&str>) -> Result<()> {
1054        // Telling the transport that this function is the exclusive user of the debugger device
1055        // for the duration of this function, will allow the transport to keep USB handles open,
1056        // for optimization.  (For transports such as the proxy, which does not have any
1057        // such optimization, this is a no-op.)
1058        let _maintain_connection = self.transport.maintain_connection()?;
1059        if let Some(strapping_name) = strapping_name {
1060            if self.capabilities()?.request(Capability::PROXY).ok().is_ok() {
1061                self.proxy_ops()?
1062                    .apply_default_configuration_with_strap(strapping_name)?;
1063            } else if let Some(strapping_conf_map) = self.strapping_conf_map.get(strapping_name) {
1064                // Apply the debugger's default pin configuration (e.g. hyperdebug pin set to HighZ)
1065                self.transport.apply_default_configuration()?;
1066                // Then apply all of the configuration specify pin configuration, these defaults are
1067                // typically specific to a certain logical chip (not debugger/interface)
1068                // configuration. Apply the named gpio strap as an override to the normal default
1069                // configuration.
1070                self.apply_pin_configurations(&self.pin_conf_map, Some(strapping_conf_map))?;
1071            } else {
1072                bail!(TransportError::InvalidStrappingName(
1073                    strapping_name.to_string(),
1074                ));
1075            }
1076        } else {
1077            self.transport.apply_default_configuration()?;
1078            self.apply_pin_configurations(&self.pin_conf_map, None)?;
1079        }
1080        // Clear cache, which could contain settings manually overriden to deviate from the
1081        // defaults in configuration files.
1082        self.pin_instance_map.borrow_mut().clear();
1083        self.spi_physical_map.borrow_mut().clear();
1084        self.spi_logical_map.borrow_mut().clear();
1085        self.i2c_physical_map.borrow_mut().clear();
1086        self.i2c_logical_map.borrow_mut().clear();
1087        Ok(())
1088    }
1089
1090    pub fn reset_target(&self, reset_delay: Duration, clear_uart_rx: bool) -> Result<()> {
1091        // Telling the transport that this function is the exclusive user of the debugger device
1092        // for the duration of this function, will allow the transport to keep USB handles open,
1093        // for optimization.  (For transports such as the proxy, which does not have any
1094        // such optimization, this is a no-op.)
1095        let _maintain_connection = self.transport.maintain_connection()?;
1096        log::info!("Asserting the reset signal");
1097        if self.disable_dft_on_reset.get() {
1098            self.pin_strapping("PRERESET_DFT_DISABLE")?.apply()?;
1099        }
1100        self.pin_strapping("RESET")?.apply()?;
1101        std::thread::sleep(reset_delay);
1102        if clear_uart_rx {
1103            log::info!("Clearing the UART RX buffer");
1104            self.uart("console")?.clear_rx_buffer()?;
1105        }
1106        log::info!("Deasserting the reset signal");
1107        self.pin_strapping("RESET")?.remove()?;
1108        if self.disable_dft_on_reset.get() {
1109            std::thread::sleep(Duration::from_millis(10));
1110            // We remove the DFT strapping after waiting some time, as the DFT straps should have been
1111            // sampled by then and we can resume our desired pin configuration.
1112            self.pin_strapping("PRERESET_DFT_DISABLE")?.remove()?;
1113        }
1114        std::thread::sleep(reset_delay);
1115        Ok(())
1116    }
1117
1118    /// As long as the returned `MaintainConnection` object is kept by the caller, this driver may
1119    /// assume that no other `opentitantool` processes attempt to access the same debugger device.
1120    /// This allows for optimizations such as keeping USB handles open across function invocations.
1121    pub fn maintain_connection(&self) -> Result<Rc<dyn MaintainConnection>> {
1122        self.transport.maintain_connection()
1123    }
1124}
1125
1126/// Given an pin/uart/spi/i2c port name, if the name is a known alias, return the underlying
1127/// name/number, otherwise return the string as is.
1128fn map_name(map: &HashMap<String, String>, name: &str) -> String {
1129    let name = name.to_uppercase();
1130    match map.get(&name) {
1131        Some(v) => {
1132            if v.eq(&name) {
1133                name
1134            } else {
1135                map_name(map, v)
1136            }
1137        }
1138        None => name,
1139    }
1140}
1141
1142/// Certain transports may want to declare that they do not support a particular pin and that it
1143/// should be ignored, even though its name is mentioned in e.g. generic strapping configurations.
1144/// (Absent any such declaration, it would result in an error to mention strappings of a pin that
1145/// is not in fact supported.)
1146struct NullPin {
1147    has_warned: Cell<bool>,
1148}
1149
1150impl NullPin {
1151    fn new() -> Self {
1152        Self {
1153            has_warned: Cell::new(false),
1154        }
1155    }
1156
1157    /// Emit a warning the first this pin is accessed.
1158    fn warn(&self) {
1159        if !self.has_warned.get() {
1160            log::warn!("Accessed NULL pin");
1161            self.has_warned.set(true);
1162        }
1163    }
1164}
1165
1166impl GpioPin for NullPin {
1167    fn read(&self) -> Result<bool> {
1168        self.warn();
1169        Ok(false)
1170    }
1171
1172    fn write(&self, _value: bool) -> Result<()> {
1173        self.warn();
1174        Ok(())
1175    }
1176
1177    fn set_mode(&self, _mode: PinMode) -> Result<()> {
1178        self.warn();
1179        Ok(())
1180    }
1181
1182    fn set_pull_mode(&self, _mode: PullMode) -> Result<()> {
1183        self.warn();
1184        Ok(())
1185    }
1186}
1187
1188/// Represents configuration of a set of pins as strong/weak pullup/pulldown as declared in
1189/// configuration files under a given strapping name.
1190pub struct PinStrapping {
1191    name: String,
1192    proxy: Option<Rc<dyn ProxyOps>>,
1193    pins: Vec<StrappedPin>,
1194}
1195
1196struct StrappedPin {
1197    pin: Rc<dyn GpioPin>,
1198    strapped: PinConfiguration,
1199    original: Option<PinConfiguration>,
1200}
1201
1202impl PinStrapping {
1203    /// Configure the set of pins as strong/weak pullup/pulldown as declared in configuration
1204    /// files under a given strapping name.
1205    pub fn apply(&self) -> Result<()> {
1206        if let Some(ref proxy_ops) = self.proxy {
1207            // The transport happens to be connected to a remote opentitan session.  First, pass
1208            // the request to the remote server.
1209            if let Err(e) = proxy_ops.apply_pin_strapping(&self.name) {
1210                match e.downcast_ref::<TransportError>() {
1211                    Some(TransportError::InvalidStrappingName(_)) => {
1212                        if self.pins.is_empty() {
1213                            return Err(e);
1214                        }
1215                    }
1216                    _ => return Err(e),
1217                }
1218            }
1219        }
1220        for StrappedPin {
1221            pin,
1222            strapped: conf,
1223            original: _,
1224        } in &self.pins
1225        {
1226            pin.set(conf.mode, conf.level, conf.pull_mode, conf.volts)?
1227        }
1228        Ok(())
1229    }
1230
1231    /// Return the set of pins affected by the given strapping to their "default" (un-strapped)
1232    /// configuration, that is, to the level declared in the "pins" section of configuration
1233    /// files, outside of any "strappings" section.
1234    pub fn remove(&self) -> Result<()> {
1235        if let Some(ref proxy_ops) = self.proxy {
1236            // The transport happens to be connection to a remote opentitan session.  Pass
1237            // request to the remote server.
1238            if let Err(e) = proxy_ops.remove_pin_strapping(&self.name) {
1239                match e.downcast_ref::<TransportError>() {
1240                    Some(TransportError::InvalidStrappingName(_)) => {
1241                        if self.pins.is_empty() {
1242                            return Err(e);
1243                        }
1244                    }
1245                    _ => return Err(e),
1246                }
1247            }
1248        }
1249        for StrappedPin {
1250            pin,
1251            strapped: _,
1252            original,
1253        } in &self.pins
1254        {
1255            if let Some(conf) = original {
1256                pin.set(conf.mode, conf.level, conf.pull_mode, conf.volts)?
1257            }
1258        }
1259        Ok(())
1260    }
1261}