libftdi support

This commit is contained in:
Mateusz Faderewski 2024-06-27 21:03:02 +02:00
parent acc3e588d8
commit 346c65fc9c
8 changed files with 1339 additions and 509 deletions

790
sw/deployer/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,21 +7,22 @@ description = "SummerCart64 loader and control software"
documentation = "https://github.com/Polprzewodnikowy/SummerCart64" documentation = "https://github.com/Polprzewodnikowy/SummerCart64"
[dependencies] [dependencies]
chrono = "0.4.23" chrono = "0.4.38"
clap = { version = "4.1.6", features = ["derive"] } clap = { version = "4.5.7", features = ["derive"] }
clap-num = "1.0.2" clap-num = "1.1.1"
colored = "2.0.0" colored = "2.1.0"
crc32fast = "1.3.2" crc32fast = "1.4.2"
ctrlc = "3.2.5" ctrlc = "3.4.4"
encoding_rs = "0.8.32" encoding_rs = "0.8.34"
hex = "0.4.3" hex = "0.4.3"
image = "0.24.5" image = "0.25.1"
include-flate = { version = "0.2.0", features = ["stable"] } include-flate = { version = "0.2.0", features = ["stable"] }
libftdi1-sys = "1.1.3"
md5 = "0.7.0" md5 = "0.7.0"
panic-message = "0.3.0" panic-message = "0.3.0"
rand = "0.8.5" rand = "0.8.5"
rust-ini = "0.18.0" rust-ini = "0.18.0"
serial2 = "0.2.20" serial2 = "0.2.26"
serialport = "4.3.0" serialport = "4.3.0"
[profile.release] [profile.release]

View File

@ -350,7 +350,13 @@ fn handle_list_command() -> Result<(), sc64::Error> {
println!("{}", "Found devices:".bold()); println!("{}", "Found devices:".bold());
for (i, d) in devices.iter().enumerate() { for (i, d) in devices.iter().enumerate() {
println!(" {i}: [{}] at port [{}]", d.serial_number, d.port); let index = i + 1;
println!(
" {index}: [{}] at port [{}] (using \"{}\" backend)",
d.serial.bold(),
d.port.bold(),
d.backend.to_string().bold()
);
} }
Ok(()) Ok(())

View File

@ -1,3 +1,4 @@
use super::ftdi::FtdiError;
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -32,3 +33,9 @@ impl From<serialport::Error> for Error {
Error::new(format!("SerialPort error: {}", value.description).as_str()) Error::new(format!("SerialPort error: {}", value.description).as_str())
} }
} }
impl From<FtdiError> for Error {
fn from(value: FtdiError) -> Self {
Error::new(format!("libftdi error: {}", value.description).as_str())
}
}

View File

@ -0,0 +1,227 @@
pub struct FtdiError {
pub description: String,
}
impl FtdiError {
fn malloc() -> FtdiError {
FtdiError {
description: format!("Couldn't allocate memory for the context"),
}
}
fn libftdi(context: *mut libftdi1_sys::ftdi_context) -> FtdiError {
let raw = unsafe { std::ffi::CStr::from_ptr(libftdi1_sys::ftdi_get_error_string(context)) };
FtdiError {
description: format!("{}", raw.to_str().unwrap_or("Unknown error")),
}
}
}
impl From<FtdiError> for std::io::Error {
fn from(value: FtdiError) -> Self {
return Self::new(std::io::ErrorKind::Other, value.description);
}
}
struct Context {
context: *mut libftdi1_sys::ftdi_context,
}
impl Context {
fn new() -> Result<Context, FtdiError> {
let ctx = unsafe { libftdi1_sys::ftdi_new() };
if ctx.is_null() {
return Err(FtdiError::malloc());
}
let context = Context { context: ctx };
let result = unsafe {
libftdi1_sys::ftdi_set_interface(
context.get(),
libftdi1_sys::ftdi_interface::INTERFACE_A,
)
};
context.check_result(result)?;
Ok(context)
}
fn get(&self) -> *mut libftdi1_sys::ftdi_context {
return self.context;
}
fn check_result(&self, result: i32) -> Result<(), FtdiError> {
if result < 0 {
return Err(FtdiError::libftdi(self.get()));
}
Ok(())
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { libftdi1_sys::ftdi_free(self.get()) }
}
}
pub struct FtdiDevice {
context: Context,
read_timeout: std::time::Duration,
write_timeout: std::time::Duration,
}
impl FtdiDevice {
pub fn open(
port: &str,
read_timeout: std::time::Duration,
write_timeout: std::time::Duration,
) -> Result<FtdiDevice, FtdiError> {
let context = Context::new()?;
unsafe {
let mode = libftdi1_sys::ftdi_module_detach_mode::AUTO_DETACH_REATACH_SIO_MODULE;
(*context.get()).module_detach_mode = mode;
}
let description = std::ffi::CString::new(port).unwrap_or_default().into_raw();
let result = unsafe { libftdi1_sys::ftdi_usb_open_string(context.get(), description) };
context.check_result(result)?;
let result = unsafe { libftdi1_sys::ftdi_set_latency_timer(context.get(), 1) };
context.check_result(result)?;
Ok(FtdiDevice {
context,
read_timeout,
write_timeout,
})
}
pub fn set_dtr(&self, value: bool) -> Result<(), FtdiError> {
let state = if value { 1 } else { 0 };
let result = unsafe { libftdi1_sys::ftdi_setdtr(self.context.get(), state) };
self.context.check_result(result)?;
Ok(())
}
pub fn read_dsr(&self) -> Result<bool, FtdiError> {
const DSR_BIT: u16 = 1 << 5;
let mut status: u16 = 0;
let result = unsafe {
libftdi1_sys::ftdi_poll_modem_status(
self.context.get(),
std::slice::from_mut(&mut status).as_mut_ptr(),
)
};
self.context.check_result(result)?;
Ok((status & DSR_BIT) != 0)
}
pub fn read(&self, data: &mut [u8]) -> std::io::Result<usize> {
let timeout = std::time::Instant::now();
loop {
let result = unsafe {
libftdi1_sys::ftdi_read_data(
self.context.get(),
data.as_mut_ptr(),
data.len() as i32,
)
};
self.context.check_result(result)?;
if result > 0 {
return Ok(result as usize);
}
if timeout.elapsed() > self.read_timeout {
return Err(std::io::ErrorKind::TimedOut.into());
}
}
}
pub fn write(&self, data: &[u8]) -> std::io::Result<usize> {
let timeout = std::time::Instant::now();
loop {
let result = unsafe {
libftdi1_sys::ftdi_write_data(self.context.get(), data.as_ptr(), data.len() as i32)
};
self.context.check_result(result)?;
if result > 0 {
return Ok(result as usize);
}
if timeout.elapsed() > self.write_timeout {
return Err(std::io::ErrorKind::TimedOut.into());
}
}
}
pub fn write_all(&self, data: &[u8]) -> std::io::Result<()> {
let mut data = data;
while !data.is_empty() {
let written = self.write(data)?;
data = &data[written..];
}
Ok(())
}
pub fn discard_buffers(&self) -> std::io::Result<()> {
let result = unsafe { libftdi1_sys::ftdi_tcioflush(self.context.get()) };
self.context.check_result(result)?;
Ok(())
}
}
impl Drop for FtdiDevice {
fn drop(&mut self) {
unsafe { libftdi1_sys::ftdi_usb_close(self.context.get()) };
}
}
pub struct FtdiDeviceInfo {
pub port: String,
pub serial: String,
}
pub fn list_ftdi_devices(vendor: u16, product: u16) -> Result<Vec<FtdiDeviceInfo>, FtdiError> {
let context = Context::new()?;
let mut device_list: *mut libftdi1_sys::ftdi_device_list = std::ptr::null_mut();
let result = unsafe {
libftdi1_sys::ftdi_usb_find_all(
context.get(),
&mut device_list,
vendor as i32,
product as i32,
)
};
context.check_result(result)?;
let mut list: Vec<FtdiDeviceInfo> = vec![];
let mut serial = [0i8; 128];
let mut device = device_list;
let mut index = 0;
while !device.is_null() {
let result = unsafe {
libftdi1_sys::ftdi_usb_get_strings(
context.get(),
(*device).dev,
std::ptr::null_mut(),
0,
std::ptr::null_mut(),
0,
serial.as_mut_ptr(),
serial.len() as i32,
)
};
if let Ok(()) = context.check_result(result) {
list.push(FtdiDeviceInfo {
port: format!("i:0x{vendor:04X}:0x{product:04X}:{index}"),
serial: unsafe { std::ffi::CStr::from_ptr(serial.as_ptr()) }
.to_string_lossy()
.into_owned(),
});
}
device = unsafe { (*device).next };
index += 1;
}
unsafe { libftdi1_sys::ftdi_list_free(&mut device_list) }
Ok(list)
}

View File

@ -1,10 +1,13 @@
use super::error::Error; use super::{
error::Error,
ftdi::{list_ftdi_devices, FtdiDevice, FtdiError},
};
use serial2::SerialPort; use serial2::SerialPort;
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
fmt::Display,
io::{BufReader, BufWriter, ErrorKind, Read, Write}, io::{BufReader, BufWriter, ErrorKind, Read, Write},
net::TcpStream, net::TcpStream,
thread,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@ -56,74 +59,54 @@ pub struct Packet {
pub data: Vec<u8>, pub data: Vec<u8>,
} }
pub struct Serial { const SERIAL_PREFIX: &str = "serial://";
serial: SerialPort, const FTDI_PREFIX: &str = "ftdi://";
}
impl Serial { const RESET_TIMEOUT: Duration = Duration::from_secs(1);
fn reset(&self) -> Result<(), Error> { const POLL_TIMEOUT: Duration = Duration::from_millis(1);
const RESET_WAIT_DURATION: Duration = Duration::from_millis(10); const READ_TIMEOUT: Duration = Duration::from_secs(5);
const RESET_RETRY_COUNT: i32 = 100; const WRITE_TIMEOUT: Duration = Duration::from_secs(5);
const FLUSH_TIMEOUT: Duration = Duration::from_secs(1);
self.serial.set_dtr(true)?; pub trait Backend {
for n in 0..=RESET_RETRY_COUNT { fn reset(&mut self) -> Result<(), Error>;
self.serial.discard_buffers()?;
thread::sleep(RESET_WAIT_DURATION);
if self.serial.read_dsr()? {
break;
}
if n == RESET_RETRY_COUNT {
return Err(Error::new("Couldn't reset SC64 device (on)"));
}
}
let flush_timeout = Instant::now(); fn close(&self);
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize>;
fn write(&mut self, buffer: &[u8]) -> std::io::Result<()>;
fn flush(&mut self) -> std::io::Result<()>;
fn purge_incoming_data(&mut self) -> std::io::Result<()> {
let timeout = Instant::now();
loop { loop {
match self.serial.read(&mut vec![0; 1]) { match self.read(&mut vec![0; 1]) {
Ok(length) => match length { Ok(length) => match length {
0 => break, 0 => return Ok(()),
_ => {} _ => {}
}, },
Err(error) => match error.kind() { Err(error) => match error.kind() {
ErrorKind::TimedOut => break, ErrorKind::TimedOut => return Ok(()),
_ => { _ => return Err(error),
return Err(Error::new(
format!("Couldn't flush SC64 serial buffer: {error}").as_str(),
))
}
}, },
} }
if flush_timeout.elapsed() >= FLUSH_TIMEOUT { if timeout.elapsed() >= RESET_TIMEOUT {
return Err(Error::new("SC64 serial buffer flush took too long")); return Err(std::io::Error::new(
ErrorKind::TimedOut,
"SC64 read buffer flush took too long",
));
} }
} }
self.serial.set_dtr(false)?;
for n in 0..=RESET_RETRY_COUNT {
thread::sleep(RESET_WAIT_DURATION);
if !self.serial.read_dsr()? {
break;
}
if n == RESET_RETRY_COUNT {
return Err(Error::new("Couldn't reset SC64 device (off)"));
}
}
Ok(())
} }
fn read_data(&self, buffer: &mut [u8], block: bool) -> Result<Option<()>, Error> { fn try_read(&mut self, buffer: &mut [u8], block: bool) -> Result<Option<()>, Error> {
let timeout = Instant::now();
let mut position = 0; let mut position = 0;
let length = buffer.len(); let length = buffer.len();
let timeout = Instant::now();
while position < length { while position < length {
if timeout.elapsed() > Duration::from_secs(10) { match self.read(&mut buffer[position..length]) {
return Err(Error::new("Serial read timeout")); Ok(0) => return Err(Error::new("Unexpected end of stream data")),
}
match self.serial.read(&mut buffer[position..length]) {
Ok(0) => return Err(Error::new("Unexpected end of serial data")),
Ok(bytes) => position += bytes, Ok(bytes) => position += bytes,
Err(error) => match error.kind() { Err(error) => match error.kind() {
ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => { ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {
@ -134,42 +117,47 @@ impl Serial {
_ => return Err(error.into()), _ => return Err(error.into()),
}, },
} }
if timeout.elapsed() > READ_TIMEOUT {
return Err(Error::new("Read timeout"));
}
} }
Ok(Some(())) Ok(Some(()))
} }
fn read_exact(&self, buffer: &mut [u8]) -> Result<(), Error> { fn try_read_header(&mut self, block: bool) -> Result<Option<[u8; 4]>, Error> {
match self.read_data(buffer, true)? { let mut header = [0u8; 4];
Ok(self.try_read(&mut header, block)?.map(|_| header))
}
fn read_exact(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
match self.try_read(buffer, true)? {
Some(()) => Ok(()), Some(()) => Ok(()),
None => Err(Error::new("Unexpected end of serial data")), None => Err(Error::new("Unexpected end of data")),
} }
} }
fn read_header(&self, block: bool) -> Result<Option<[u8; 4]>, Error> { fn send_command(&mut self, command: &Command) -> Result<(), Error> {
let mut header = [0u8; 4]; self.write(b"CMD")?;
Ok(self.read_data(&mut header, block)?.map(|_| header)) self.write(&command.id.to_be_bytes())?;
}
pub fn send_command(&self, command: &Command) -> Result<(), Error> { self.write(&command.args[0].to_be_bytes())?;
self.serial.write_all(b"CMD")?; self.write(&command.args[1].to_be_bytes())?;
self.serial.write_all(&command.id.to_be_bytes())?;
self.serial.write_all(&command.args[0].to_be_bytes())?;
self.serial.write_all(&command.args[1].to_be_bytes())?;
self.serial.write_all(&command.data)?; self.write(&command.data)?;
self.serial.flush()?; self.flush()?;
Ok(()) Ok(())
} }
pub fn process_incoming_data( fn process_incoming_data(
&self, &mut self,
data_type: DataType, data_type: DataType,
packets: &mut VecDeque<Packet>, packets: &mut VecDeque<Packet>,
) -> Result<Option<Response>, Error> { ) -> Result<Option<Response>, Error> {
let block = matches!(data_type, DataType::Response); let block = matches!(data_type, DataType::Response);
while let Some(header) = self.read_header(block)? {
while let Some(header) = self.try_read_header(block)? {
let (packet_token, error) = (match &header[0..3] { let (packet_token, error) = (match &header[0..3] {
b"CMP" => Ok((false, false)), b"CMP" => Ok((false, false)),
b"PKT" => Ok((true, false)), b"PKT" => Ok((true, false)),
@ -200,48 +188,115 @@ impl Serial {
} }
} }
pub fn new_serial(port: &str) -> Result<Serial, Error> { pub struct SerialBackend {
let mut serial = SerialPort::open(port, 115_200)?; device: SerialPort,
serial.set_write_timeout(Duration::from_secs(10))?;
serial.set_read_timeout(Duration::from_millis(10))?;
let backend = Serial { serial };
backend.reset()?;
Ok(backend)
}
trait Backend {
fn send_command(&mut self, command: &Command) -> Result<(), Error>;
fn process_incoming_data(
&mut self,
data_type: DataType,
packets: &mut VecDeque<Packet>,
) -> Result<Option<Response>, Error>;
fn close(&self) {}
}
struct SerialBackend {
inner: Serial,
} }
impl Backend for SerialBackend { impl Backend for SerialBackend {
fn send_command(&mut self, command: &Command) -> Result<(), Error> { fn reset(&mut self) -> Result<(), Error> {
self.inner.send_command(command) self.device.set_dtr(true)?;
let timeout = Instant::now();
loop {
self.device.discard_buffers()?;
if self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(Error::new("Couldn't reset SC64 device (on)"));
}
}
self.purge_incoming_data()?;
self.device.set_dtr(false)?;
let timeout = Instant::now();
loop {
if !self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(Error::new("Couldn't reset SC64 device (off)"));
}
}
Ok(())
} }
fn process_incoming_data( fn close(&self) {}
&mut self,
data_type: DataType, fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
packets: &mut VecDeque<Packet>, self.device.read(buffer)
) -> Result<Option<Response>, Error> { }
self.inner.process_incoming_data(data_type, packets)
fn write(&mut self, buffer: &[u8]) -> std::io::Result<()> {
self.device.write_all(buffer)
}
fn flush(&mut self) -> std::io::Result<()> {
self.device.flush()
} }
} }
fn new_serial_backend(port: &str) -> Result<SerialBackend, Error> { fn new_serial_backend(port: &str) -> std::io::Result<SerialBackend> {
let backend = SerialBackend { let mut serial = SerialPort::open(port, 115_200)?;
inner: new_serial(port)?, serial.set_read_timeout(POLL_TIMEOUT)?;
}; serial.set_write_timeout(WRITE_TIMEOUT)?;
Ok(backend) Ok(SerialBackend { device: serial })
}
struct FtdiBackend {
device: FtdiDevice,
}
impl Backend for FtdiBackend {
fn reset(&mut self) -> Result<(), Error> {
self.device.set_dtr(true)?;
let timeout = Instant::now();
loop {
self.device.discard_buffers()?;
if self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(Error::new("Couldn't reset SC64 device (on)"));
}
}
self.purge_incoming_data()?;
self.device.set_dtr(false)?;
let timeout = Instant::now();
loop {
if !self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(Error::new("Couldn't reset SC64 device (off)"));
}
}
Ok(())
}
fn close(&self) {}
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
self.device.read(buffer)
}
fn write(&mut self, buffer: &[u8]) -> std::io::Result<()> {
self.device.write_all(buffer)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
fn new_ftdi_backend(port: &str) -> Result<FtdiBackend, FtdiError> {
Ok(FtdiBackend {
device: FtdiDevice::open(port, POLL_TIMEOUT, WRITE_TIMEOUT)?,
})
} }
struct TcpBackend { struct TcpBackend {
@ -250,58 +305,40 @@ struct TcpBackend {
writer: BufWriter<TcpStream>, writer: BufWriter<TcpStream>,
} }
impl TcpBackend {
fn read_data(&mut self, buffer: &mut [u8], block: bool) -> Result<Option<()>, Error> {
let timeout = Instant::now();
let mut position = 0;
let length = buffer.len();
while position < length {
if timeout.elapsed() > Duration::from_secs(10) {
return Err(Error::new("Stream read timeout"));
}
match self.reader.read(&mut buffer[position..length]) {
Ok(0) => return Err(Error::new("Unexpected end of stream data")),
Ok(bytes) => position += bytes,
Err(error) => match error.kind() {
ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {
if !block && position == 0 {
return Ok(None);
}
}
_ => return Err(error.into()),
},
}
}
Ok(Some(()))
}
fn read_exact(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
match self.read_data(buffer, true)? {
Some(()) => Ok(()),
None => Err(Error::new("Unexpected end of stream data")),
}
}
fn read_header(&mut self, block: bool) -> Result<Option<[u8; 4]>, Error> {
let mut header = [0u8; 4];
Ok(self.read_data(&mut header, block)?.map(|_| header))
}
}
impl Backend for TcpBackend { impl Backend for TcpBackend {
fn reset(&mut self) -> Result<(), Error> {
Ok(())
}
fn close(&self) {
self.stream.shutdown(std::net::Shutdown::Both).ok();
}
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
self.reader.read(buffer)
}
fn write(&mut self, buffer: &[u8]) -> std::io::Result<()> {
self.writer.write_all(buffer)
}
fn flush(&mut self) -> std::io::Result<()> {
self.writer.flush()
}
fn send_command(&mut self, command: &Command) -> Result<(), Error> { fn send_command(&mut self, command: &Command) -> Result<(), Error> {
let payload_data_type: u32 = DataType::Command.into(); let payload_data_type: u32 = DataType::Command.into();
self.writer.write_all(&payload_data_type.to_be_bytes())?; self.write(&payload_data_type.to_be_bytes())?;
self.writer.write_all(&command.id.to_be_bytes())?; self.write(&command.id.to_be_bytes())?;
self.writer.write_all(&command.args[0].to_be_bytes())?; self.write(&command.args[0].to_be_bytes())?;
self.writer.write_all(&command.args[1].to_be_bytes())?; self.write(&command.args[1].to_be_bytes())?;
let command_data_length = command.data.len() as u32; let command_data_length = command.data.len() as u32;
self.writer.write_all(&command_data_length.to_be_bytes())?; self.write(&command_data_length.to_be_bytes())?;
self.writer.write_all(&command.data)?; self.write(&command.data)?;
self.writer.flush()?; self.flush()?;
Ok(()) Ok(())
} }
@ -312,7 +349,7 @@ impl Backend for TcpBackend {
packets: &mut VecDeque<Packet>, packets: &mut VecDeque<Packet>,
) -> Result<Option<Response>, Error> { ) -> Result<Option<Response>, Error> {
let block = matches!(data_type, DataType::Response); let block = matches!(data_type, DataType::Response);
while let Some(header) = self.read_header(block)? { while let Some(header) = self.try_read_header(block)? {
let payload_data_type: DataType = u32::from_be_bytes(header).try_into()?; let payload_data_type: DataType = u32::from_be_bytes(header).try_into()?;
let mut buffer = [0u8; 4]; let mut buffer = [0u8; 4];
match payload_data_type { match payload_data_type {
@ -357,17 +394,13 @@ impl Backend for TcpBackend {
Ok(None) Ok(None)
} }
fn close(&self) {
self.stream.shutdown(std::net::Shutdown::Both).ok();
}
} }
fn new_tcp_backend(address: &str) -> Result<TcpBackend, Error> { fn new_tcp_backend(address: &str) -> Result<TcpBackend, Error> {
let stream = match TcpStream::connect(address) { let stream = match TcpStream::connect(address) {
Ok(stream) => { Ok(stream) => {
stream.set_write_timeout(Some(Duration::from_secs(10)))?; stream.set_write_timeout(Some(WRITE_TIMEOUT))?;
stream.set_read_timeout(Some(Duration::from_millis(10)))?; stream.set_read_timeout(Some(POLL_TIMEOUT))?;
stream stream
} }
Err(error) => { Err(error) => {
@ -385,8 +418,28 @@ fn new_tcp_backend(address: &str) -> Result<TcpBackend, Error> {
}) })
} }
fn new_local_backend(port: &str) -> Result<Box<dyn Backend>, Error> {
let mut backend: Box<dyn Backend> = if port.starts_with(SERIAL_PREFIX) {
Box::new(new_serial_backend(
port.strip_prefix(SERIAL_PREFIX).unwrap_or_default(),
)?)
} else if port.starts_with(FTDI_PREFIX) {
Box::new(new_ftdi_backend(
port.strip_prefix(FTDI_PREFIX).unwrap_or_default(),
)?)
} else {
return Err(Error::new("Invalid port prefix provided"));
};
backend.reset()?;
Ok(backend)
}
fn new_remote_backend(address: &str) -> Result<Box<dyn Backend>, Error> {
Ok(Box::new(new_tcp_backend(address)?))
}
pub struct Link { pub struct Link {
backend: Box<dyn Backend>, pub backend: Box<dyn Backend>,
packets: VecDeque<Packet>, packets: VecDeque<Packet>,
} }
@ -451,45 +504,76 @@ impl Drop for Link {
pub fn new_local(port: &str) -> Result<Link, Error> { pub fn new_local(port: &str) -> Result<Link, Error> {
Ok(Link { Ok(Link {
backend: Box::new(new_serial_backend(port)?), backend: new_local_backend(port)?,
packets: VecDeque::new(), packets: VecDeque::new(),
}) })
} }
pub fn new_remote(address: &str) -> Result<Link, Error> { pub fn new_remote(address: &str) -> Result<Link, Error> {
Ok(Link { Ok(Link {
backend: Box::new(new_tcp_backend(address)?), backend: new_remote_backend(address)?,
packets: VecDeque::new(), packets: VecDeque::new(),
}) })
} }
pub struct LocalDevice { pub enum BackendType {
pub port: String, Serial,
pub serial_number: String, Ftdi,
} }
pub fn list_local_devices() -> Result<Vec<LocalDevice>, Error> { impl Display for BackendType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Serial => "serial",
Self::Ftdi => "libftdi",
})
}
}
pub struct DeviceInfo {
pub backend: BackendType,
pub port: String,
pub serial: String,
}
pub fn list_local_devices() -> Result<Vec<DeviceInfo>, Error> {
const SC64_VID: u16 = 0x0403; const SC64_VID: u16 = 0x0403;
const SC64_PID: u16 = 0x6014; const SC64_PID: u16 = 0x6014;
const SC64_SID: &str = "SC64"; const SC64_SID: &str = "SC64";
let mut serial_devices: Vec<LocalDevice> = Vec::new(); let mut devices: Vec<DeviceInfo> = Vec::new();
for device in serialport::available_ports()?.into_iter() { if let Ok(list) = list_ftdi_devices(SC64_VID, SC64_PID) {
if let serialport::SerialPortType::UsbPort(info) = device.port_type { for device in list.into_iter() {
let serial_number = info.serial_number.unwrap_or("".to_string()); if device.serial.starts_with(SC64_SID) {
if info.vid == SC64_VID && info.pid == SC64_PID && serial_number.starts_with(SC64_SID) { devices.push(DeviceInfo {
serial_devices.push(LocalDevice { backend: BackendType::Ftdi,
port: device.port_name, port: format!("{FTDI_PREFIX}{}", device.port),
serial_number, serial: device.serial,
}); })
} }
} }
} }
if serial_devices.len() == 0 { if let Ok(list) = serialport::available_ports() {
for device in list.into_iter() {
if let serialport::SerialPortType::UsbPort(i) = device.port_type {
if let Some(serial) = i.serial_number {
if i.vid == SC64_VID && i.pid == SC64_PID && serial.starts_with(SC64_SID) {
devices.push(DeviceInfo {
backend: BackendType::Serial,
port: format!("{SERIAL_PREFIX}{}", device.port_name),
serial,
});
}
}
}
}
}
if devices.len() == 0 {
return Err(Error::new("No SC64 devices found")); return Err(Error::new("No SC64 devices found"));
} }
return Ok(serial_devices); return Ok(devices);
} }

View File

@ -1,6 +1,7 @@
mod cic; mod cic;
mod error; mod error;
pub mod firmware; pub mod firmware;
mod ftdi;
mod link; mod link;
pub mod server; pub mod server;
mod time; mod time;

View File

@ -1,19 +1,5 @@
use super::{ use super::{error::Error, link::list_local_devices};
error::Error, use std::net::{TcpListener, TcpStream};
link::{list_local_devices, new_serial, Command, DataType, Packet, Response, Serial},
};
use std::{
collections::VecDeque,
io::{BufReader, BufWriter, ErrorKind, Read, Write},
net::{TcpListener, TcpStream},
sync::{
atomic::{AtomicBool, Ordering},
mpsc::{channel, Receiver, Sender},
Arc,
},
thread,
time::{Duration, Instant},
};
pub enum ServerEvent { pub enum ServerEvent {
Listening(String), Listening(String),
@ -55,204 +41,208 @@ pub fn run(
Ok(()) Ok(())
} }
enum Event { // enum Event {
Command(Command), // Command(Command),
Response(Response), // Response(Response),
Packet(Packet), // Packet(Packet),
KeepAlive, // KeepAlive,
Closed(Option<Error>), // Closed(Option<Error>),
} // }
fn server_accept_connection(port: String, stream: &mut TcpStream) -> Result<(), Error> { fn server_accept_connection(_port: String, _stream: &mut TcpStream) -> Result<(), Error> {
let (event_sender, event_receiver) = channel::<Event>(); // let (event_sender, event_receiver) = channel::<Event>();
let exit_flag = Arc::new(AtomicBool::new(false)); // let exit_flag = Arc::new(AtomicBool::new(false));
let mut stream_writer = BufWriter::new(stream.try_clone()?); // let mut stream_writer = BufWriter::new(stream.try_clone()?);
let mut stream_reader = stream.try_clone()?; // let mut stream_reader = stream.try_clone()?;
let serial = Arc::new(new_serial(&port)?); // let serial = Arc::new(new_local(&port)?);
let serial_writer = serial.clone(); // let serial_writer = serial.clone();
let serial_reader = serial.clone(); // let serial_reader = serial.clone();
let stream_event_sender = event_sender.clone(); // let stream_event_sender = event_sender.clone();
let stream_exit_flag = exit_flag.clone(); // let stream_exit_flag = exit_flag.clone();
let stream_thread = thread::spawn(move || { // let stream_thread = thread::spawn(move || {
let closed_sender = stream_event_sender.clone(); // let closed_sender = stream_event_sender.clone();
match server_stream_thread(&mut stream_reader, stream_event_sender, stream_exit_flag) { // match server_stream_thread(&mut stream_reader, stream_event_sender, stream_exit_flag) {
Ok(()) => closed_sender.send(Event::Closed(None)), // Ok(()) => closed_sender.send(Event::Closed(None)),
Err(error) => closed_sender.send(Event::Closed(Some(error))), // Err(error) => closed_sender.send(Event::Closed(Some(error))),
} // }
.ok(); // .ok();
}); // });
let serial_event_sender = event_sender.clone(); // let serial_event_sender = event_sender.clone();
let serial_exit_flag = exit_flag.clone(); // let serial_exit_flag = exit_flag.clone();
let serial_thread = thread::spawn(move || { // let serial_thread = thread::spawn(move || {
let closed_sender = serial_event_sender.clone(); // let closed_sender = serial_event_sender.clone();
match server_serial_thread(serial_reader, serial_event_sender, serial_exit_flag) { // match server_serial_thread(serial_reader, serial_event_sender, serial_exit_flag) {
Ok(()) => closed_sender.send(Event::Closed(None)), // Ok(()) => closed_sender.send(Event::Closed(None)),
Err(error) => closed_sender.send(Event::Closed(Some(error))), // Err(error) => closed_sender.send(Event::Closed(Some(error))),
} // }
.ok(); // .ok();
}); // });
let keepalive_event_sender = event_sender.clone(); // let keepalive_event_sender = event_sender.clone();
let keepalive_exit_flag = exit_flag.clone(); // let keepalive_exit_flag = exit_flag.clone();
let keepalive_thread = thread::spawn(move || { // let keepalive_thread = thread::spawn(move || {
server_keepalive_thread(keepalive_event_sender, keepalive_exit_flag); // server_keepalive_thread(keepalive_event_sender, keepalive_exit_flag);
}); // });
let result = server_process_events(&mut stream_writer, serial_writer, event_receiver); // let result = server_process_events(&mut stream_writer, serial_writer, event_receiver);
exit_flag.store(true, Ordering::Relaxed); // exit_flag.store(true, Ordering::Relaxed);
stream_thread.join().ok(); // stream_thread.join().ok();
serial_thread.join().ok(); // serial_thread.join().ok();
keepalive_thread.join().ok(); // keepalive_thread.join().ok();
result
}
fn server_process_events(
stream_writer: &mut BufWriter<TcpStream>,
serial_writer: Arc<Serial>,
event_receiver: Receiver<Event>,
) -> Result<(), Error> {
for event in event_receiver.into_iter() {
match event {
Event::Command(command) => {
serial_writer.send_command(&command)?;
}
Event::Response(response) => {
stream_writer.write_all(&u32::to_be_bytes(DataType::Response.into()))?;
stream_writer.write_all(&[response.id])?;
stream_writer.write_all(&[response.error as u8])?;
stream_writer.write_all(&(response.data.len() as u32).to_be_bytes())?;
stream_writer.write_all(&response.data)?;
stream_writer.flush()?;
}
Event::Packet(packet) => {
stream_writer.write_all(&u32::to_be_bytes(DataType::Packet.into()))?;
stream_writer.write_all(&[packet.id])?;
stream_writer.write_all(&(packet.data.len() as u32).to_be_bytes())?;
stream_writer.write_all(&packet.data)?;
stream_writer.flush()?;
}
Event::KeepAlive => {
stream_writer.write_all(&u32::to_be_bytes(DataType::KeepAlive.into()))?;
stream_writer.flush()?;
}
Event::Closed(result) => match result {
Some(error) => return Err(error),
None => {
break;
}
},
}
}
// result
Ok(()) Ok(())
} }
fn server_stream_thread( // fn server_process_events(
stream: &mut TcpStream, // stream_writer: &mut BufWriter<TcpStream>,
event_sender: Sender<Event>, // link: &mut Link,
exit_flag: Arc<AtomicBool>, // event_receiver: Receiver<Event>,
) -> Result<(), Error> { // ) -> Result<(), Error> {
let mut stream_reader = BufReader::new(stream.try_clone()?); // for event in event_receiver.into_iter() {
// match event {
// Event::Command(command) => {
// // serial_writer.send_command(&command)?;
// // serial_writer.
// // link.execute_command(&command)?;
// link.execute_command(&command)?;
// }
// Event::Response(response) => {
// stream_writer.write_all(&u32::to_be_bytes(DataType::Response.into()))?;
// stream_writer.write_all(&[response.id])?;
// stream_writer.write_all(&[response.error as u8])?;
// stream_writer.write_all(&(response.data.len() as u32).to_be_bytes())?;
// stream_writer.write_all(&response.data)?;
// stream_writer.flush()?;
// }
// Event::Packet(packet) => {
// stream_writer.write_all(&u32::to_be_bytes(DataType::Packet.into()))?;
// stream_writer.write_all(&[packet.id])?;
// stream_writer.write_all(&(packet.data.len() as u32).to_be_bytes())?;
// stream_writer.write_all(&packet.data)?;
// stream_writer.flush()?;
// }
// Event::KeepAlive => {
// stream_writer.write_all(&u32::to_be_bytes(DataType::KeepAlive.into()))?;
// stream_writer.flush()?;
// }
// Event::Closed(result) => match result {
// Some(error) => return Err(error),
// None => {
// break;
// }
// },
// }
// }
let mut header = [0u8; 4]; // Ok(())
let header_length = header.len(); // }
loop { // fn server_stream_thread(
let mut header_position = 0; // stream: &mut TcpStream,
// event_sender: Sender<Event>,
// exit_flag: Arc<AtomicBool>,
// ) -> Result<(), Error> {
// let mut stream_reader = BufReader::new(stream.try_clone()?);
let timeout = stream.read_timeout()?; // let mut header = [0u8; 4];
stream.set_read_timeout(Some(Duration::from_millis(10)))?; // let header_length = header.len();
while header_position < header_length {
if exit_flag.load(Ordering::Relaxed) {
return Ok(());
}
match stream_reader.read(&mut header[header_position..header_length]) {
Ok(0) => return Ok(()),
Ok(bytes) => header_position += bytes,
Err(error) => match error.kind() {
ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {}
_ => return Err(error.into()),
},
}
}
stream.set_read_timeout(timeout)?;
let data_type: DataType = u32::from_be_bytes(header).try_into()?; // loop {
if !matches!(data_type, DataType::Command) { // let mut header_position = 0;
return Err(Error::new("Received data type was not a command data type"));
}
let mut buffer = [0u8; 4]; // let timeout = stream.read_timeout()?;
let mut id_buffer = [0u8; 1]; // stream.set_read_timeout(Some(Duration::from_millis(10)))?;
let mut args = [0u32; 2]; // while header_position < header_length {
// if exit_flag.load(Ordering::Relaxed) {
// return Ok(());
// }
// match stream_reader.read(&mut header[header_position..header_length]) {
// Ok(0) => return Ok(()),
// Ok(bytes) => header_position += bytes,
// Err(error) => match error.kind() {
// ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {}
// _ => return Err(error.into()),
// },
// }
// }
// stream.set_read_timeout(timeout)?;
stream_reader.read_exact(&mut id_buffer)?; // let data_type: DataType = u32::from_be_bytes(header).try_into()?;
let id = id_buffer[0]; // if !matches!(data_type, DataType::Command) {
// return Err(Error::new("Received data type was not a command data type"));
// }
stream_reader.read_exact(&mut buffer)?; // let mut buffer = [0u8; 4];
args[0] = u32::from_be_bytes(buffer); // let mut id_buffer = [0u8; 1];
stream_reader.read_exact(&mut buffer)?; // let mut args = [0u32; 2];
args[1] = u32::from_be_bytes(buffer);
stream_reader.read_exact(&mut buffer)?; // stream_reader.read_exact(&mut id_buffer)?;
let command_data_length = u32::from_be_bytes(buffer) as usize; // let id = id_buffer[0];
let mut data = vec![0u8; command_data_length];
stream_reader.read_exact(&mut data)?;
if event_sender // stream_reader.read_exact(&mut buffer)?;
.send(Event::Command(Command { id, args, data })) // args[0] = u32::from_be_bytes(buffer);
.is_err() // stream_reader.read_exact(&mut buffer)?;
{ // args[1] = u32::from_be_bytes(buffer);
break;
}
}
Ok(()) // stream_reader.read_exact(&mut buffer)?;
} // let command_data_length = u32::from_be_bytes(buffer) as usize;
// let mut data = vec![0u8; command_data_length];
// stream_reader.read_exact(&mut data)?;
fn server_serial_thread( // if event_sender
serial_reader: Arc<Serial>, // .send(Event::Command(Command { id, args, data }))
event_sender: Sender<Event>, // .is_err()
exit_flag: Arc<AtomicBool>, // {
) -> Result<(), Error> { // break;
let mut packets: VecDeque<Packet> = VecDeque::new(); // }
// }
while !exit_flag.load(Ordering::Relaxed) { // Ok(())
let response = serial_reader.process_incoming_data(DataType::Packet, &mut packets)?; // }
if let Some(response) = response { // fn server_serial_thread(
if event_sender.send(Event::Response(response)).is_err() { // link: &mut Link,
break; // event_sender: Sender<Event>,
} // exit_flag: Arc<AtomicBool>,
} // ) -> Result<(), Error> {
// let mut packets: VecDeque<Packet> = VecDeque::new();
if let Some(packet) = packets.pop_front() { // while !exit_flag.load(Ordering::Relaxed) {
if event_sender.send(Event::Packet(packet)).is_err() { // let response = link.backend.process_incoming_data(DataType::Packet, &mut packets)?;
break;
}
}
}
Ok(()) // if let Some(response) = response {
} // if event_sender.send(Event::Response(response)).is_err() {
// break;
// }
// }
fn server_keepalive_thread(event_sender: Sender<Event>, exit_flag: Arc<AtomicBool>) { // if let Some(packet) = packets.pop_front() {
let mut keepalive = Instant::now(); // if event_sender.send(Event::Packet(packet)).is_err() {
// break;
// }
// }
// }
while !exit_flag.load(Ordering::Relaxed) { // Ok(())
if keepalive.elapsed() >= Duration::from_secs(5) { // }
keepalive = Instant::now();
if event_sender.send(Event::KeepAlive).is_err() { // fn server_keepalive_thread(event_sender: Sender<Event>, exit_flag: Arc<AtomicBool>) {
break; // let mut keepalive = Instant::now();
}
} else { // while !exit_flag.load(Ordering::Relaxed) {
thread::sleep(Duration::from_millis(10)); // if keepalive.elapsed() >= Duration::from_secs(5) {
} // keepalive = Instant::now();
} // if event_sender.send(Event::KeepAlive).is_err() {
} // break;
// }
// } else {
// thread::sleep(Duration::from_millis(100));
// }
// }
// }