Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion sw/host/opentitanlib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ rust_library(
"src/io/nonblocking_help.rs",
"src/io/spi.rs",
"src/io/uart.rs",
"src/io/usb.rs",
"src/lib.rs",
"src/otp/alert_handler.rs",
"src/otp/alert_handler_regs.rs",
Expand Down Expand Up @@ -189,6 +190,7 @@ rust_library(
"src/transport/common/fpga.rs",
"src/transport/common/mod.rs",
"src/transport/common/uart.rs",
"src/transport/common/usb.rs",
"src/transport/dediprog/gpio.rs",
"src/transport/dediprog/mod.rs",
"src/transport/dediprog/spi.rs",
Expand Down Expand Up @@ -255,7 +257,6 @@ rust_library(
"src/util/status.rs",
"src/util/testing.rs",
"src/util/unknown.rs",
"src/util/usb.rs",
"src/util/usr_access.rs",
"src/util/vcd.rs",
"src/util/vmem/mod.rs",
Expand Down
6 changes: 6 additions & 0 deletions sw/host/opentitanlib/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::io::jtag::{JtagChain, JtagParams};
use crate::io::nonblocking_help::NonblockingHelp;
use crate::io::spi::{Target, TransferMode};
use crate::io::uart::Uart;
use crate::io::usb::UsbContext;
use crate::transport::{
Capability, ProgressIndicator, ProxyOps, Transport, TransportError, TransportInterfaceType,
ioexpander,
Expand Down Expand Up @@ -823,6 +824,11 @@ impl TransportWrapper {
self.transport.uart(map_name(&self.uart_map, name).as_str())
}

/// Returns a [`UsbContext`] implementation.
pub fn usb(&self) -> Result<Rc<dyn UsbContext>> {
self.transport.usb()
}

/// Returns a [`GpioPin`] implementation.
pub fn gpio_pin(&self, name: &str) -> Result<Rc<dyn GpioPin>> {
let resolved_pin_name = map_name(&self.pin_map, name);
Expand Down
1 change: 1 addition & 0 deletions sw/host/opentitanlib/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ pub mod jtag;
pub mod nonblocking_help;
pub mod spi;
pub mod uart;
pub mod usb;
192 changes: 192 additions & 0 deletions sw/host/opentitanlib/src/io/usb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::rc::Rc;
use std::time::Duration;
use thiserror::Error;

use crate::impl_serializable_error;

/// Errors related to the GPIO interface.
#[derive(Debug, Error, Serialize, Deserialize)]
pub enum UsbError {
#[error("Generic error: {0}")]
Generic(String),
}
impl_serializable_error!(UsbError);

/// A trait which represents a USB device.
///
/// Note: some of the methods use `rusb`'s datatypes to avoid redefining
/// all USB structure but otherwise does not require rusb to be implemented.
pub trait UsbDevice {
/// Return the VID of the device.
fn get_vendor_id(&self) -> u16;

/// Return the PID of the device.
fn get_product_id(&self) -> u16;

/// Gets the serial number of the device.
fn get_serial_number(&self) -> &str;

/// Set the active configuration.
fn set_active_configuration(&self, config: u8) -> Result<()>;

/// Claim an interface for use with the kernel.
fn claim_interface(&self, iface: u8) -> Result<()>;

/// Release a previously claimed interface to the kernel.
fn release_interface(&self, iface: u8) -> Result<()>;

/// Set an interface alternate setting.
fn set_alternate_setting(&self, iface: u8, setting: u8) -> Result<()>;

/// Check whether a kernel driver currentl controls the device.
fn kernel_driver_active(&self, iface: u8) -> Result<bool>;

/// Detach the kernel driver from the device.
fn detach_kernel_driver(&self, iface: u8) -> Result<()>;

/// Attach the kernel driver to the device.
fn attach_kernel_driver(&self, iface: u8) -> Result<()>;

/// Return the currently active configuration's descriptor.
fn active_config_descriptor(&self) -> Result<rusb::ConfigDescriptor>;

/// Return the device's bus number.
fn bus_number(&self) -> u8;

/// Return the sequence of port numbers from the root down to the device.
fn port_numbers(&self) -> Result<Vec<u8>>;

/// Return a string descriptor in ASCII.
fn read_string_descriptor_ascii(&self, idx: u8) -> Result<String>;

/// Reset the device.
///
/// Note that this UsbDevice handle will most likely become invalid
/// after resetting the device and a new one has to be obtained.
fn reset(&self) -> Result<()>;

/// Get the default timeout for operations.
fn get_timeout(&self) -> Duration;

/// Issue a USB control request with optional host-to-device data.
fn write_control_timeout(
&self,
request_type: u8,
request: u8,
value: u16,
index: u16,
buf: &[u8],
timeout: Duration,
) -> Result<usize>;

/// Issue a USB control request with optional host-to-device data.
///
/// This function uses the default timeout set up by the context.
fn write_control(
&self,
request_type: u8,
request: u8,
value: u16,
index: u16,
buf: &[u8],
) -> Result<usize> {
self.write_control_timeout(request_type, request, value, index, buf, self.get_timeout())
}

/// Issue a USB control request with optional device-to-host data.
fn read_control_timeout(
&self,
request_type: u8,
request: u8,
value: u16,
index: u16,
buf: &mut [u8],
timeout: Duration,
) -> Result<usize>;

/// Issue a USB control request with optional device-to-host data.
///
/// This function uses the default timeout set up by the context.
fn read_control(
&self,
request_type: u8,
request: u8,
value: u16,
index: u16,
buf: &mut [u8],
) -> Result<usize> {
self.read_control_timeout(request_type, request, value, index, buf, self.get_timeout())
}

/// Read bulk data bytes to given USB endpoint.
fn read_bulk_timeout(&self, endpoint: u8, data: &mut [u8], timeout: Duration) -> Result<usize>;

/// Read bulk data bytes to given USB endpoint.
///
/// This function uses the default timeout set up by the context.
fn read_bulk(&self, endpoint: u8, data: &mut [u8]) -> Result<usize> {
self.read_bulk_timeout(endpoint, data, self.get_timeout())
}

/// Write bulk data bytes to given USB endpoint.
fn write_bulk_timeout(&self, endpoint: u8, data: &[u8], timeout: Duration) -> Result<usize>;

/// Write bulk data bytes to given USB endpoint.
///
/// This function uses the default timeout set up by the context.
fn write_bulk(&self, endpoint: u8, data: &[u8]) -> Result<usize> {
self.write_bulk_timeout(endpoint, data, self.get_timeout())
}
}

/// A trait which represents a USB context.
pub trait UsbContext {
/// Find a device by VID:PID, and optionally disambiguate by serial number.
///
/// If no device matches, this function returns immediately and does not wait.
fn device_by_id(
&self,
usb_vid: u16,
usb_pid: u16,
usb_serial: Option<&str>,
) -> Result<Rc<dyn UsbDevice>> {
self.device_by_id_with_timeout(usb_vid, usb_pid, usb_serial, Duration::ZERO)
}

/// Find a device by VID:PID, and optionally disambiguate by serial number.
fn device_by_id_with_timeout(
&self,
usb_vid: u16,
usb_pid: u16,
usb_serial: Option<&str>,
timeout: Duration,
) -> Result<Rc<dyn UsbDevice>>;

/// Find a device with a specific interface, and optionally disambiguate by serial number.
///
/// If no device matches, this function returns immediately and does not wait.
fn device_by_interface(
&self,
class: u8,
subclass: u8,
protocol: u8,
usb_serial: Option<&str>,
) -> Result<Rc<dyn UsbDevice>> {
self.device_by_interface_with_timeout(class, subclass, protocol, usb_serial, Duration::ZERO)
}

fn device_by_interface_with_timeout(
&self,
class: u8,
subclass: u8,
protocol: u8,
usb_serial: Option<&str>,
timeout: Duration,
) -> Result<Rc<dyn UsbDevice>>;
}
13 changes: 7 additions & 6 deletions sw/host/opentitanlib/src/rescue/usbdfu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@

use anyhow::{Result, bail};
use std::cell::{Cell, Ref, RefCell};
use std::rc::Rc;
use std::time::Duration;

use crate::app::{TransportWrapper, UartRx};
use crate::io::usb::UsbDevice;
use crate::rescue::dfu::*;
use crate::rescue::{EntryMode, Rescue, RescueError, RescueMode, RescueParams};
use crate::util::usb::UsbBackend;

pub struct UsbDfu {
usb: RefCell<Option<UsbBackend>>,
usb: RefCell<Option<Rc<dyn UsbDevice>>>,
interface: Cell<u8>,
params: RescueParams,
reset_delay: Duration,
Expand All @@ -33,9 +34,9 @@ impl UsbDfu {
}
}

fn device(&self) -> Ref<'_, UsbBackend> {
let device = self.usb.borrow();
Ref::map(device, |d| d.as_ref().expect("device handle"))
fn device(&self) -> Ref<'_, dyn UsbDevice> {
let usb = self.usb.borrow();
Ref::map(usb, |d| &**d.as_ref().expect("device handle"))
}
}

Expand All @@ -54,7 +55,7 @@ impl Rescue for UsbDfu {
EntryMode::None => {}
}

let device = UsbBackend::from_interface_with_timeout(
let device = transport.usb()?.device_by_interface_with_timeout(
Self::CLASS,
Self::SUBCLASS,
Self::PROTOCOL,
Expand Down
12 changes: 11 additions & 1 deletion sw/host/opentitanlib/src/transport/chip_whisperer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ use std::rc::Rc;
use crate::io::gpio::GpioPin;
use crate::io::spi::Target;
use crate::io::uart::{Uart, UartError};
use crate::io::usb::UsbContext;
use crate::transport::common::fpga::{ClearBitstream, FpgaProgram};
use crate::transport::common::uart::SerialPortUart;
use crate::transport::common::usb::RusbContext;
use crate::transport::{
Capabilities, Capability, Transport, TransportError, TransportInterfaceType,
};
Expand Down Expand Up @@ -93,7 +95,11 @@ impl<B: Board> ChipWhisperer<B> {
impl<B: Board + 'static> Transport for ChipWhisperer<B> {
fn capabilities(&self) -> Result<Capabilities> {
Ok(Capabilities::new(
Capability::SPI | Capability::GPIO | Capability::UART | Capability::UART_NONBLOCKING,
Capability::SPI
| Capability::GPIO
| Capability::UART
| Capability::UART_NONBLOCKING
| Capability::USB,
))
}

Expand All @@ -112,6 +118,10 @@ impl<B: Board + 'static> Transport for ChipWhisperer<B> {
Ok(uart)
}

fn usb(&self) -> Result<Rc<dyn UsbContext>> {
Ok(Rc::new(RusbContext::new()))
}

fn gpio_pin(&self, pinname: &str) -> Result<Rc<dyn GpioPin>> {
let mut inner = self.inner.borrow_mut();
Ok(match inner.gpio.entry(pinname.to_string()) {
Expand Down
9 changes: 6 additions & 3 deletions sw/host/opentitanlib/src/transport/chip_whisperer/usb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ use std::collections::HashMap;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::marker::PhantomData;
use std::rc::Rc;
use std::time::Duration;

use super::board::Board;
use crate::collection;
use crate::io::gpio::GpioError;
use crate::io::spi::SpiError;
use crate::io::usb::{UsbContext, UsbDevice};
use crate::transport::common::usb::RusbContext;
use crate::transport::{ProgressIndicator, TransportError, TransportInterfaceType};
use crate::util::parse_int::ParseInt;
use crate::util::usb::UsbBackend;

/// The `Backend` struct provides high-level access to the Chip Whisperer board.
pub struct Backend<B: Board> {
usb: UsbBackend,
usb: Rc<dyn UsbDevice>,
_marker: PhantomData<B>,
}

Expand Down Expand Up @@ -108,8 +110,9 @@ impl<B: Board> Backend<B> {
usb_pid: Option<u16>,
usb_serial: Option<&str>,
) -> Result<Self> {
let usb_context = RusbContext::new();
Ok(Backend {
usb: UsbBackend::new(
usb: usb_context.device_by_id(
usb_vid.unwrap_or(B::VENDOR_ID),
usb_pid.unwrap_or(B::PRODUCT_ID),
usb_serial,
Expand Down
1 change: 1 addition & 0 deletions sw/host/opentitanlib/src/transport/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

pub mod fpga;
pub mod uart;
pub mod usb;
Loading
Loading