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"
[dependencies]
chrono = "0.4.23"
clap = { version = "4.1.6", features = ["derive"] }
clap-num = "1.0.2"
colored = "2.0.0"
crc32fast = "1.3.2"
ctrlc = "3.2.5"
encoding_rs = "0.8.32"
chrono = "0.4.38"
clap = { version = "4.5.7", features = ["derive"] }
clap-num = "1.1.1"
colored = "2.1.0"
crc32fast = "1.4.2"
ctrlc = "3.4.4"
encoding_rs = "0.8.34"
hex = "0.4.3"
image = "0.24.5"
image = "0.25.1"
include-flate = { version = "0.2.0", features = ["stable"] }
libftdi1-sys = "1.1.3"
md5 = "0.7.0"
panic-message = "0.3.0"
rand = "0.8.5"
rust-ini = "0.18.0"
serial2 = "0.2.20"
serial2 = "0.2.26"
serialport = "4.3.0"
[profile.release]

View File

@ -350,7 +350,13 @@ fn handle_list_command() -> Result<(), sc64::Error> {
println!("{}", "Found devices:".bold());
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(())

View File

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

View File

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

View File

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