1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use std::cell::RefCell;
use std::rc::Rc;

use anyhow::{bail, ensure, Context, Result};

use crate::io::gpio::GpioPin;
use crate::io::spi::Target;
use crate::io::uart::Uart;
use crate::transport::{
    Capabilities, Capability, Transport, TransportError, TransportInterfaceType,
};

pub mod gpio;
pub mod mpsse;
pub mod spi;
pub mod uart;

#[derive(Default)]
pub struct Ultradebug {
    pub usb_vid: Option<u16>,
    pub usb_pid: Option<u16>,
    pub usb_serial: Option<String>,
    // A ref-counted pointer to an MPSSE context for FTDI interface B.  This is needed because
    // interface B contains both the SPI and GPIO functions on ultradebug.
    mpsse_b: RefCell<Option<Rc<RefCell<mpsse::Context>>>>,
    inner: RefCell<Inner>,
}

#[derive(Default)]
struct Inner {
    gpio: Option<Rc<gpio::UltradebugGpio>>,
    spi: Option<Rc<dyn Target>>,
    uart: Option<Rc<dyn Uart>>,
}

impl Ultradebug {
    pub const VID_GOOGLE: u16 = 0x18d1;
    pub const PID_ULTRADEBUG: u16 = 0x0304;

    /// Create a new `Ultradebug` struct, optionally specifying the USB vid/pid/serial number.
    pub fn new(usb_vid: Option<u16>, usb_pid: Option<u16>, usb_serial: Option<String>) -> Self {
        Ultradebug {
            usb_vid,
            usb_pid,
            usb_serial,
            ..Default::default()
        }
    }

    /// Construct an `ftdi::Device` for the specified `interface` on the Ultradebug device.
    pub fn from_interface(&self, interface: ftdi::Interface) -> Result<ftdi::Device> {
        let vid = self.usb_vid.unwrap_or(Ultradebug::VID_GOOGLE);
        let pid = self.usb_pid.unwrap_or(Ultradebug::PID_ULTRADEBUG);

        let mut opener = ftdi::find_by_vid_pid(vid, pid).interface(interface);
        if let Some(serial) = &self.usb_serial {
            opener = opener.serial(serial);
        }

        opener.open().context("FTDI error")
    }

    // Create an instance of an MPSSE context bound to Ultradebug interface B.
    // This is both the SPI and GPIO block on ultradebug.
    fn mpsse_interface_b(&self) -> Result<Rc<RefCell<mpsse::Context>>> {
        let mut mpsse_b = self.mpsse_b.borrow_mut();
        if mpsse_b.is_none() {
            let device = self.from_interface(ftdi::Interface::B)?;

            // Create a new MPSSE context and configure it
            let mut mpdev = mpsse::Context::new(device).context("FTDI error")?;
            mpdev.gpio_direction.insert(
                mpsse::GpioDirection::OUT_0 |   // Clock out
                mpsse::GpioDirection::OUT_1 |   // Master out
                                                // Pin 2 is Master In
                mpsse::GpioDirection::OUT_3 |   // Chip select
                mpsse::GpioDirection::OUT_4 |   // SPI_ZB
                mpsse::GpioDirection::OUT_5 |   // RESET_B
                mpsse::GpioDirection::OUT_6 |   // BOOTSTRAP
                mpsse::GpioDirection::OUT_7, // TGT_RESET
            );

            let _ = mpdev.gpio_get().context("FTDI error")?;
            // Clear the low 3 bits as they are mapped to the SPI pins.
            // The SPI chip select is managed like a normal GPIO.
            // We don't need to change the GPIOs immediately; it is sufficient
            // to cache the value before the next GPIO operation.
            mpdev.gpio_value &= 0xF8;
            *mpsse_b = Some(Rc::new(RefCell::new(mpdev)));
        }
        Ok(Rc::clone(mpsse_b.as_ref().unwrap()))
    }

    /// Construct an `mpsse::Context` for the requested interface.
    pub fn mpsse(&self, interface: ftdi::Interface) -> Result<Rc<RefCell<mpsse::Context>>> {
        match interface {
            // Note: we may want to be able to create an MPSSE for interface A in the future.
            // There are enough IOs for a second SPI bus or for JTAG or I2C busses.
            // For now, only interface B is supported.
            ftdi::Interface::B => self.mpsse_interface_b(),
            _ => {
                bail!(TransportError::UsbOpenError(format!(
                    "I don't know how to create an MPSSE context for interface {:?}",
                    interface
                )));
            }
        }
    }
}

impl Transport for Ultradebug {
    fn capabilities(&self) -> Result<Capabilities> {
        Ok(Capabilities::new(
            Capability::UART | Capability::GPIO | Capability::SPI,
        ))
    }

    fn uart(&self, instance: &str) -> Result<Rc<dyn Uart>> {
        ensure!(
            instance == "0",
            TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string())
        );
        let mut inner = self.inner.borrow_mut();
        if inner.uart.is_none() {
            inner.uart = Some(Rc::new(uart::UltradebugUart::open(self)?));
        }
        Ok(Rc::clone(inner.uart.as_ref().unwrap()))
    }

    fn gpio_pin(&self, instance: &str) -> Result<Rc<dyn GpioPin>> {
        let mut inner = self.inner.borrow_mut();
        if inner.gpio.is_none() {
            inner.gpio = Some(Rc::new(gpio::UltradebugGpio::open(self)?));
        }
        Ok(Rc::new(inner.gpio.as_ref().unwrap().pin(instance)?))
    }

    fn spi(&self, instance: &str) -> Result<Rc<dyn Target>> {
        ensure!(
            instance == "0",
            TransportError::InvalidInstance(TransportInterfaceType::Spi, instance.to_string())
        );
        let mut inner = self.inner.borrow_mut();
        if inner.spi.is_none() {
            inner.spi = Some(Rc::new(spi::UltradebugSpi::open(self)?));
        }
        Ok(Rc::clone(inner.spi.as_ref().unwrap()))
    }
}