opentitanlib/transport/ftdi/
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
5use anyhow::Result;
6use serde_annotate::Annotate;
7use std::any::Any;
8use std::cell::RefCell;
9use std::collections::HashMap;
10use std::collections::hash_map::Entry;
11use std::rc::Rc;
12
13use crate::io::gpio::{GpioPin, PinMode};
14use crate::io::spi::Target;
15use crate::io::uart::Uart;
16use crate::io::uart::UartError;
17use crate::transport::common::uart::{SerialPortUart, SoftwareFlowControl};
18use crate::transport::{
19    Capabilities, Capability, Transport, TransportError, TransportInterfaceType,
20};
21use crate::util::parse_int::ParseInt;
22use serialport::SerialPortType;
23
24use chip::Chip;
25use ftdi_embedded_hal as ftdi_hal;
26
27pub mod chip;
28pub mod gpio;
29pub mod spi;
30
31#[derive(Default)]
32struct Inner {
33    spi: Option<Rc<dyn Target>>,
34    gpio: HashMap<String, Rc<dyn GpioPin>>,
35    uart: HashMap<u32, Rc<dyn Uart>>,
36}
37
38pub struct Ftdi<C: Chip> {
39    pub(crate) ftdi_interfaces: Rc<HashMap<ftdi::Interface, ftdi_hal::FtHal<ftdi::Device>>>,
40    inner: RefCell<Inner>,
41    phantom: std::marker::PhantomData<C>,
42}
43
44impl<C: Chip> Ftdi<C> {
45    pub fn new() -> anyhow::Result<Self> {
46        let mut ftdi_interfaces = HashMap::new();
47        for interface in C::INTERFACES {
48            let device = ftdi::find_by_vid_pid(C::VENDOR_ID, C::PRODUCT_ID)
49                .interface(*interface)
50                .open()?;
51            ftdi_interfaces.insert(*interface, ftdi_hal::FtHal::init_freq(device, 8_000_000)?);
52        }
53
54        let ftdi_dev = Ftdi {
55            ftdi_interfaces: Rc::new(ftdi_interfaces),
56            inner: RefCell::default(),
57            phantom: std::marker::PhantomData,
58        };
59        Ok(ftdi_dev)
60    }
61
62    fn open_uart(&self, instance: u32) -> Result<SoftwareFlowControl<SerialPortUart>> {
63        let mut ports = serialport::available_ports()
64            .map_err(|e| UartError::EnumerationError(e.to_string()))?;
65
66        ports.retain(|port| {
67            if let SerialPortType::UsbPort(info) = &port.port_type
68                && info.vid == C::VENDOR_ID
69                && info.pid == C::PRODUCT_ID
70            {
71                return true;
72            }
73            false
74        });
75
76        let port = ports.get(instance as usize).ok_or_else(|| {
77            TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string())
78        })?;
79
80        Ok(SoftwareFlowControl::new(SerialPortUart::open(
81            &port.port_name,
82            C::UART_BAUD,
83        )?))
84    }
85}
86
87impl<C: Chip> Transport for Ftdi<C> {
88    fn capabilities(&self) -> Result<Capabilities> {
89        Ok(Capabilities::new(
90            Capability::SPI | Capability::GPIO | Capability::UART | Capability::UART_NONBLOCKING,
91        ))
92    }
93
94    fn uart(&self, instance: &str) -> Result<Rc<dyn Uart>> {
95        let mut inner = self.inner.borrow_mut();
96        let instance = u32::from_str(instance).ok().ok_or_else(|| {
97            TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string())
98        })?;
99        let uart = match inner.uart.entry(instance) {
100            Entry::Vacant(v) => {
101                let u = v.insert(Rc::new(self.open_uart(instance)?));
102                Rc::clone(u)
103            }
104            Entry::Occupied(o) => Rc::clone(o.get()),
105        };
106        Ok(uart)
107    }
108
109    fn gpio_pin(&self, pinname: &str) -> Result<Rc<dyn GpioPin>> {
110        let mut inner = self.inner.borrow_mut();
111        Ok(match inner.gpio.entry(pinname.to_string()) {
112            Entry::Vacant(v) => {
113                let u = v.insert(Rc::new(gpio::Pin::open::<C>(
114                    &self.ftdi_interfaces,
115                    pinname.to_string(),
116                )?));
117                Rc::clone(u)
118            }
119            Entry::Occupied(o) => Rc::clone(o.get()),
120        })
121    }
122
123    fn spi(&self, _instance: &str) -> Result<Rc<dyn Target>> {
124        let spi_cs = gpio::Pin::open::<C>(&self.ftdi_interfaces, "bdbus3".to_string())?;
125        spi_cs.set_mode(PinMode::PushPull)?;
126        let mut inner = self.inner.borrow_mut();
127        if inner.spi.is_none() {
128            inner.spi = Some(Rc::new(spi::Spi::open(&self.ftdi_interfaces, spi_cs)?));
129        }
130        Ok(Rc::clone(inner.spi.as_ref().unwrap()))
131    }
132
133    fn dispatch(&self, _action: &dyn Any) -> Result<Option<Box<dyn Annotate>>> {
134        Err(TransportError::UnsupportedOperation.into())
135    }
136}
137
138/// Command for Transport::dispatch().
139pub struct SetPll {}
140
141/// Command for Transport::dispatch(). Resets the Chip whisperer board's SAM3X chip.
142pub struct ResetSam3x {}