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