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