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