#[cfg(not(any(
feature = "pcie",
feature = "uart",
feature = "spi",
feature = "ethernet",
feature = "usb"
)))]
compile_error!("Must enable at least one bridge type: pcie, uart, spi, ethernet, or usb");
pub(crate) mod bridges;
#[doc(hidden)]
#[cfg(feature = "ethernet")]
pub use bridges::ethernet::EthernetBridgeInner;
#[doc(hidden)]
#[cfg(feature = "pcie")]
pub use bridges::pcie::PCIeBridgeInner;
#[doc(hidden)]
#[cfg(feature = "spi")]
pub use bridges::spi::SpiBridgeInner;
#[doc(hidden)]
#[cfg(feature = "uart")]
pub use bridges::uart::UartBridgeInner;
#[doc(hidden)]
#[cfg(feature = "usb")]
pub use bridges::usb::UsbBridgeInner;
#[cfg(feature = "ethernet")]
pub use bridges::ethernet::{EthernetBridge, EthernetBridgeProtocol};
#[cfg(feature = "pcie")]
pub use bridges::pcie::PCIeBridge;
#[cfg(feature = "spi")]
pub use bridges::spi::SpiBridge;
#[cfg(feature = "uart")]
pub use bridges::uart::UartBridge;
#[cfg(feature = "usb")]
pub use bridges::usb::UsbBridge;
use log::debug;
use std::io;
use std::sync::{Arc, Mutex};
#[doc(hidden)]
#[derive(Clone)]
pub enum BridgeConfig {
None,
#[cfg(feature = "ethernet")]
EthernetBridge(EthernetBridge),
#[cfg(feature = "pcie")]
PCIeBridge(PCIeBridge),
#[cfg(feature = "spi")]
SpiBridge(SpiBridge),
#[cfg(feature = "uart")]
UartBridge(UartBridge),
#[cfg(feature = "usb")]
UsbBridge(UsbBridge),
}
#[doc(hidden)]
#[derive(Clone)]
pub enum BridgeCore {
#[cfg(feature = "ethernet")]
EthernetBridge(EthernetBridgeInner),
#[cfg(feature = "pcie")]
PCIeBridge(PCIeBridgeInner),
#[cfg(feature = "spi")]
SpiBridge(SpiBridgeInner),
#[cfg(feature = "uart")]
UartBridge(UartBridgeInner),
#[cfg(feature = "usb")]
UsbBridge(UsbBridgeInner),
}
#[derive(Clone)]
pub struct Bridge {
core: BridgeCore,
offset: usize,
mutex: Arc<Mutex<()>>,
}
#[derive(Debug)]
pub enum BridgeError {
NoBridgeSpecified,
LengthError(usize, usize),
#[cfg(feature = "usb")]
USBError(libusb_wishbone_tool::Error),
IoError(io::Error),
NotConnected,
InvalidAddress,
WrongResponse,
#[allow(dead_code)]
ProtocolNotSupported,
#[allow(dead_code)]
Timeout,
}
impl ::std::fmt::Display for BridgeError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
use BridgeError::*;
match self {
LengthError(expected, actual) => {
write!(f, "expected {} bytes, but got {} instead", expected, actual)
}
#[cfg(feature = "usb")]
USBError(e) => write!(f, "libusb error {}", e.strerror()),
IoError(e) => write!(f, "io error {}", e),
NoBridgeSpecified => write!(f, "no bridge was specified"),
NotConnected => write!(f, "bridge not connected"),
WrongResponse => write!(f, "wrong response received"),
InvalidAddress => write!(f, "bad address or path"),
ProtocolNotSupported => write!(f, "protocol not supported on this platform"),
Timeout => write!(f, "connection timed out"),
}
}
}
#[cfg(feature = "usb")]
impl std::convert::From<libusb_wishbone_tool::Error> for BridgeError {
fn from(e: libusb_wishbone_tool::Error) -> BridgeError {
BridgeError::USBError(e)
}
}
impl std::convert::From<io::Error> for BridgeError {
fn from(e: io::Error) -> BridgeError {
BridgeError::IoError(e)
}
}
impl Bridge {
pub(crate) fn new(bridge_cfg: BridgeConfig) -> Result<Bridge, BridgeError> {
let mutex = Arc::new(Mutex::new(()));
match &bridge_cfg {
BridgeConfig::None => Err(BridgeError::NoBridgeSpecified),
#[cfg(feature = "ethernet")]
BridgeConfig::EthernetBridge(bridge_cfg) => Ok(Bridge {
mutex,
core: BridgeCore::EthernetBridge(EthernetBridgeInner::new(bridge_cfg)?),
offset: 0,
}),
#[cfg(feature = "pcie")]
BridgeConfig::PCIeBridge(bridge_cfg) => Ok(Bridge {
mutex,
core: BridgeCore::PCIeBridge(PCIeBridgeInner::new(bridge_cfg)?),
offset: 0,
}),
#[cfg(feature = "spi")]
BridgeConfig::SpiBridge(bridge_cfg) => Ok(Bridge {
mutex,
core: BridgeCore::SpiBridge(SpiBridgeInner::new(bridge_cfg)?),
offset: 0,
}),
#[cfg(feature = "uart")]
BridgeConfig::UartBridge(bridge_cfg) => Ok(Bridge {
mutex,
core: BridgeCore::UartBridge(UartBridgeInner::new(bridge_cfg)?),
offset: 0,
}),
#[cfg(feature = "usb")]
BridgeConfig::UsbBridge(bridge_cfg) => Ok(Bridge {
mutex,
core: BridgeCore::UsbBridge(UsbBridgeInner::new(bridge_cfg)?),
offset: 0,
}),
}
}
pub fn connect(&self) -> Result<(), BridgeError> {
let _mtx = self.mutex.lock().unwrap();
match &self.core {
#[cfg(feature = "ethernet")]
BridgeCore::EthernetBridge(b) => b.connect(),
#[cfg(feature = "pcie")]
BridgeCore::PCIeBridge(b) => b.connect(),
#[cfg(feature = "spi")]
BridgeCore::SpiBridge(b) => b.connect(),
#[cfg(feature = "uart")]
BridgeCore::UartBridge(b) => b.connect(),
#[cfg(feature = "usb")]
BridgeCore::UsbBridge(b) => b.connect(),
}
}
pub fn peek(&self, addr: u32) -> Result<u32, BridgeError> {
let _mtx = self.mutex.lock().unwrap();
loop {
let result = match &self.core {
#[cfg(feature = "ethernet")]
BridgeCore::EthernetBridge(b) => b.peek(addr),
#[cfg(feature = "pcie")]
BridgeCore::PCIeBridge(b) => b.peek(addr),
#[cfg(feature = "spi")]
BridgeCore::SpiBridge(b) => b.peek(addr),
#[cfg(feature = "uart")]
BridgeCore::UartBridge(b) => b.peek(addr),
#[cfg(feature = "usb")]
BridgeCore::UsbBridge(b) => b.peek(addr),
};
#[allow(unreachable_code)]
if let Err(e) = result {
#[cfg(feature = "usb")]
if let BridgeError::USBError(libusb_wishbone_tool::Error::Pipe) = e {
debug!("USB device disconnected, forcing early return");
return Err(e);
}
debug!("Peek failed, trying again: {:?}", e);
} else {
return result;
}
}
}
pub fn poke(&self, addr: u32, value: u32) -> Result<(), BridgeError> {
let _mtx = self.mutex.lock().unwrap();
loop {
let result = match &self.core {
#[cfg(feature = "ethernet")]
BridgeCore::EthernetBridge(b) => b.poke(addr, value),
#[cfg(feature = "pcie")]
BridgeCore::PCIeBridge(b) => b.poke(addr, value),
#[cfg(feature = "spi")]
BridgeCore::SpiBridge(b) => b.poke(addr, value),
#[cfg(feature = "uart")]
BridgeCore::UartBridge(b) => b.poke(addr, value),
#[cfg(feature = "usb")]
BridgeCore::UsbBridge(b) => b.poke(addr, value),
};
#[allow(unreachable_code)]
if let Err(e) = result {
match e {
#[cfg(feature = "usb")]
BridgeError::USBError(libusb_wishbone_tool::Error::Pipe) => {
debug!("USB device disconnected (Windows), forcing early return");
return Err(e);
}
#[cfg(feature = "usb")]
BridgeError::USBError(libusb_wishbone_tool::Error::Io) => {
debug!("USB device disconnected (Posix), forcing early return");
return Err(e);
}
_ => {}
}
debug!("Poke failed, trying again: {:?}", e);
} else {
return result;
}
}
}
pub fn burst_read(&self, addr: u32, length: u32) -> Result<Vec<u8>, BridgeError> {
let _mtx = self.mutex.lock().unwrap();
loop {
let result = match &self.core {
#[cfg(feature = "ethernet")]
BridgeCore::EthernetBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
#[cfg(feature = "pcie")]
BridgeCore::PCIeBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
#[cfg(feature = "spi")]
BridgeCore::SpiBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
#[cfg(feature = "uart")]
BridgeCore::UartBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
#[cfg(feature = "usb")]
BridgeCore::UsbBridge(b) => b.burst_read(addr, length),
};
#[allow(unreachable_code)]
if let Err(e) = result {
#[cfg(feature = "usb")]
if let BridgeError::USBError(libusb_wishbone_tool::Error::Pipe) = e {
debug!("USB device disconnected, forcing early return");
return Err(e);
}
debug!("Peek failed, trying again: {:?}", e);
} else {
return result;
}
}
}
pub fn burst_write(&self, addr: u32, data: &Vec<u8>) -> Result<(), BridgeError> {
let _mtx = self.mutex.lock().unwrap();
loop {
let result = match &self.core {
#[cfg(feature = "ethernet")]
BridgeCore::EthernetBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
#[cfg(feature = "pcie")]
BridgeCore::PCIeBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
#[cfg(feature = "spi")]
BridgeCore::SpiBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
#[cfg(feature = "uart")]
BridgeCore::UartBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
#[cfg(feature = "usb")]
BridgeCore::UsbBridge(b) => b.burst_write(addr, data),
};
#[allow(unreachable_code)]
if let Err(e) = result {
#[cfg(feature = "usb")]
if let BridgeError::USBError(libusb_wishbone_tool::Error::Pipe) = e {
debug!("USB device disconnected, forcing early return");
return Err(e);
}
debug!("Peek failed, trying again: {:?}", e);
} else {
return result;
}
}
}
}
impl std::io::Read for Bridge {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let _mtx = self.mutex.lock().unwrap();
let addr = self.offset as _;
use std::convert::TryInto;
use std::io::{Error, ErrorKind};
fn fill_array(src: &[u8], dest: &mut [u8]) -> usize {
let mut fill_bytes = 0;
for (s, d) in src.iter().zip(dest) {
*d = *s;
fill_bytes += 1;
}
fill_bytes
}
let copied = match &self.core {
#[cfg(feature = "ethernet")]
BridgeCore::EthernetBridge(b) => {
b.peek(addr).map(|v| fill_array(&v.to_le_bytes(), buf))
}
#[cfg(feature = "pcie")]
BridgeCore::PCIeBridge(b) => b.peek(addr).map(|v| fill_array(&v.to_le_bytes(), buf)),
#[cfg(feature = "spi")]
BridgeCore::SpiBridge(b) => b.peek(addr).map(|v| fill_array(&v.to_le_bytes(), buf)),
#[cfg(feature = "uart")]
BridgeCore::UartBridge(b) => b.peek(addr).map(|v| fill_array(&v.to_le_bytes(), buf)),
#[cfg(feature = "usb")]
BridgeCore::UsbBridge(b) => b
.burst_read(addr, buf.len().try_into().unwrap())
.map(|v| fill_array(&v, buf)),
}
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
self.offset += copied;
Ok(copied)
}
}
impl std::io::Seek for Bridge {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
use std::convert::TryInto;
use std::io::{Error, ErrorKind};
let new_offset = match pos {
std::io::SeekFrom::End(_) => Err(Error::new(
ErrorKind::AddrNotAvailable,
"cannot seek from end",
))?,
std::io::SeekFrom::Current(add) => {
if add > 0 {
self.offset + (add as usize)
} else {
self.offset - (-add as usize)
}
}
std::io::SeekFrom::Start(offset) => offset as usize,
};
self.offset += new_offset;
Ok(self.offset.try_into().unwrap())
}
}
impl std::io::Write for Bridge {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
use std::convert::TryInto;
use std::io::{Error, ErrorKind};
let _mtx = self.mutex.lock().unwrap();
fn slice_to_u32(buf: &[u8]) -> std::io::Result<u32> {
if buf.len() < 3 {
Err(Error::new(
ErrorKind::InvalidData,
"data not a multiple of 4 bytes",
))?;
}
Ok(u32::from_le_bytes(buf[0..3].try_into().unwrap()))
}
let addr = self.offset as _;
let bytes_written = match &self.core {
#[cfg(feature = "ethernet")]
BridgeCore::EthernetBridge(_) => self.poke(addr, slice_to_u32(buf)?).map(|_| 4),
#[cfg(feature = "pcie")]
BridgeCore::PCIeBridge(_) => self.poke(addr, slice_to_u32(buf)?).map(|_| 4),
#[cfg(feature = "spi")]
BridgeCore::SpiBridge(_) => self.poke(addr, slice_to_u32(buf)?).map(|_| 4),
#[cfg(feature = "uart")]
BridgeCore::UartBridge(_) => self.poke(addr, slice_to_u32(buf)?).map(|_| 4),
#[cfg(feature = "usb")]
BridgeCore::UsbBridge(b) => b.burst_write(addr, buf).map(|_| buf.len()),
}
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
self.offset += bytes_written;
Ok(bytes_written)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}