use std::{cell::RefCell, collections::BTreeMap, io};
use crate::probe::{DebugProbeError, DebugProbeSelector, ProbeError};
use super::{net::GlasgowNetDevice, proto::Target, usb::GlasgowUsbDevice};
#[derive(Debug, thiserror::Error)]
pub enum DiscoveryError {
#[error(
"Serial number format is \"<device serial>:<IN interface>:<OUT interface>\" or \"tcp:<host>:<port>\"."
)]
InvalidFormat,
#[error("Specified USB interfaces are invalid.")]
InvalidInterfaces,
#[error("Could not connect to remote host: {0}.")]
ConnectionFailed(#[from] io::Error),
}
impl ProbeError for DiscoveryError {}
#[derive(Debug, thiserror::Error)]
pub enum PacketDecodeError {
#[error("COBS decode error: {0}.")]
CobsDecodeError(#[from] cobs::DecodeError),
#[error("Packet too short.")]
PacketTooShort,
#[error("Packet destination does not exist.")]
NoDestination,
}
impl ProbeError for PacketDecodeError {}
fn packet_encode(target: Target, mut data: Vec<u8>) -> Vec<u8> {
assert!(!data.is_empty());
data.insert(0, target as u8);
let mut result = vec![0; cobs::max_encoding_length(data.len())];
let result_len = cobs::encode(&data, &mut result);
result.truncate(result_len);
result
}
fn packet_decode(packet: &mut [u8]) -> Result<(Target, &[u8]), PacketDecodeError> {
let result_len = cobs::decode_in_place(packet).map_err(PacketDecodeError::CobsDecodeError)?;
let packet = &packet[..result_len];
let target = *packet.first().ok_or(PacketDecodeError::PacketTooShort)?;
let target = Target::try_from(target).map_err(|_| PacketDecodeError::NoDestination)?;
Ok((target, &packet[1..]))
}
enum GlasgowDeviceInner {
Usb(GlasgowUsbDevice),
Net(GlasgowNetDevice),
}
impl GlasgowDeviceInner {
fn transfer(
&mut self,
output: Vec<u8>,
input: impl FnMut(Vec<u8>) -> Result<bool, DebugProbeError>,
) -> Result<(), DebugProbeError> {
match self {
GlasgowDeviceInner::Usb(usb_device) => usb_device.transfer(output, input),
GlasgowDeviceInner::Net(tcp_device) => tcp_device.transfer(output, input),
}
}
}
pub struct GlasgowDevice {
inner: RefCell<GlasgowDeviceInner>,
in_queue: RefCell<Vec<u8>>,
in_buffers: RefCell<BTreeMap<Target, Vec<u8>>>,
out_buffers: RefCell<BTreeMap<Target, Vec<u8>>>,
}
impl GlasgowDevice {
fn new(inner: GlasgowDeviceInner) -> Self {
Self {
inner: RefCell::new(inner),
in_queue: RefCell::new(Vec::new()),
in_buffers: RefCell::new(BTreeMap::from_iter([
(Target::Root, Vec::new()),
(Target::Swd, Vec::new()),
])),
out_buffers: RefCell::new(BTreeMap::from_iter([
(Target::Root, Vec::new()),
(Target::Swd, Vec::new()),
])),
}
}
pub fn new_from_selector(selector: &DebugProbeSelector) -> Result<Self, DebugProbeError> {
let Some(ref serial) = selector.serial_number else {
unreachable!()
};
if serial.starts_with("tcp:") || serial.starts_with("unix:") {
Ok(Self::new(GlasgowDeviceInner::Net(
GlasgowNetDevice::new_from_selector(selector)?,
)))
} else {
Ok(Self::new(GlasgowDeviceInner::Usb(
GlasgowUsbDevice::new_from_selector(selector)?,
)))
}
}
fn collect_out_data(&self) -> Vec<u8> {
let mut out_buffers = self.out_buffers.borrow_mut();
let mut output = Vec::new();
for (&target, buffer) in out_buffers.iter_mut() {
if !buffer.is_empty() {
output.extend(packet_encode(target, std::mem::take(buffer)));
output.push(0x00);
}
}
output
}
fn dispatch_in_data(&self, input: Vec<u8>) -> Result<(), PacketDecodeError> {
let mut in_buffers = self.in_buffers.borrow_mut();
let mut in_queue = self.in_queue.borrow_mut();
in_queue.extend(input);
let mut chunks = in_queue.split_mut(|b| *b == 0x00).collect::<Vec<_>>();
let chunks_len = chunks.len();
for chunk in &mut chunks[..chunks_len - 1] {
let (target, packet) = packet_decode(chunk)?;
in_buffers
.get_mut(&target)
.expect("IN buffer not found")
.extend(packet);
}
*in_queue = chunks[chunks_len - 1].to_vec();
Ok(())
}
pub fn send(&mut self, target: Target, data: &[u8]) {
tracing::trace!("send({target:?}, {})", hexdump(data));
self.out_buffers
.borrow_mut()
.get_mut(&target)
.expect("OUT buffer not found")
.extend(data);
}
pub fn recv(&mut self, target: Target, size: usize) -> Result<Vec<u8>, DebugProbeError> {
tracing::trace!("recv({target:?}, {size}) -> ...");
let out_transfer = self.collect_out_data();
self.inner
.borrow_mut()
.transfer(out_transfer, |in_transfer| {
self.dispatch_in_data(in_transfer)?;
let in_buffers = self.in_buffers.borrow_mut();
Ok(in_buffers.get(&target).expect("IN buffer not found").len() >= size)
})?;
let mut in_buffers = self.in_buffers.borrow_mut();
let in_buffer = in_buffers.get_mut(&target).expect("IN buffer not found");
let mut data = in_buffer.split_off(size);
std::mem::swap(&mut data, in_buffer);
tracing::trace!("recv({target:?}, {size}) -> {}", hexdump(&data));
Ok(data)
}
}
pub(super) fn hexdump(bytes: &[u8]) -> String {
use std::fmt::Write;
let mut result = String::new();
result.push('<');
for byte in bytes {
write!(&mut result, "{byte:02x}").unwrap();
}
result.push('>');
result
}