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