mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-22 05:59:15 +01:00
deployer remote connection done
This commit is contained in:
parent
441633d655
commit
eadb7f9f92
44
sw/deployer/Cargo.lock
generated
44
sw/deployer/Cargo.lock
generated
@ -281,9 +281,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
|
||||
checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@ -293,9 +293,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
|
||||
checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@ -308,15 +308,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
|
||||
checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.91"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
|
||||
checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -538,9 +538,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
||||
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
@ -867,9 +867,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
@ -877,9 +877,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.2"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
@ -916,9 +916,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.8"
|
||||
version = "0.36.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
||||
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@ -961,9 +961,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.3"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
|
||||
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||
|
||||
[[package]]
|
||||
name = "serialport"
|
||||
@ -1067,9 +1067,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@ -1254,9 +1254,9 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.50"
|
||||
version = "0.2.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589245df6230839c305984dcc0a8385cc72af1fd223f360ffd5d65efa4216d40"
|
||||
checksum = "a01728b79fb9b7e28a8c11f715e1cd8dc2cda7416a007d66cac55cebb3a8ac6b"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
@ -7,9 +7,10 @@ use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||
use clap_num::maybe_hex_range;
|
||||
use colored::Colorize;
|
||||
use panic_message::panic_message;
|
||||
use sc64::ServerEvent;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{stdin, stdout, BufReader, Read, Write},
|
||||
io::{stdin, stdout, Read, Write},
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
@ -307,8 +308,7 @@ fn handle_list_command() -> Result<(), sc64::Error> {
|
||||
fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<(), sc64::Error> {
|
||||
let mut sc64 = init_sc64(connection, true)?;
|
||||
|
||||
let (rom_file_unbuffered, rom_name, rom_length) = open_file(&args.rom)?;
|
||||
let mut rom_file = BufReader::new(rom_file_unbuffered);
|
||||
let (mut rom_file, rom_name, rom_length) = open_file(&args.rom)?;
|
||||
|
||||
sc64.reset_state()?;
|
||||
|
||||
@ -532,7 +532,7 @@ fn handle_firmware_command(
|
||||
|
||||
let metadata = sc64::firmware::verify(&firmware)?;
|
||||
println!("{}", "Firmware metadata:".bold());
|
||||
println!("{}", format!("{}", metadata).blue().to_string());
|
||||
println!("{}", format!("{}", metadata).cyan().to_string());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -551,7 +551,7 @@ fn handle_firmware_command(
|
||||
|
||||
let metadata = sc64::firmware::verify(&firmware)?;
|
||||
println!("{}", "Firmware metadata:".bold());
|
||||
println!("{}", format!("{}", metadata).blue().to_string());
|
||||
println!("{}", format!("{}", metadata).cyan().to_string());
|
||||
|
||||
backup_file.write_all(&firmware)?;
|
||||
|
||||
@ -568,12 +568,17 @@ fn handle_firmware_command(
|
||||
|
||||
let metadata = sc64::firmware::verify(&firmware)?;
|
||||
println!("{}", "Firmware metadata:".bold());
|
||||
println!("{}", format!("{}", metadata).blue().to_string());
|
||||
println!("{}", format!("{}", metadata).cyan().to_string());
|
||||
println!("{}", "Firmware file verification was successful".green());
|
||||
let answer = prompt(format!("{}", "Continue with update process? [y/N] ".bold()));
|
||||
if answer.to_ascii_lowercase() != "y" {
|
||||
panic!("Firmware update process aborted");
|
||||
println!("{}", "Firmware update process aborted".red());
|
||||
return Ok(());
|
||||
}
|
||||
println!(
|
||||
"{}",
|
||||
"Do not unplug SC64 from the computer, doing so might brick your device".yellow()
|
||||
);
|
||||
|
||||
sc64.reset_state()?;
|
||||
|
||||
@ -594,10 +599,26 @@ fn handle_server_command(connection: Connection, args: &ServerArgs) -> Result<()
|
||||
None
|
||||
};
|
||||
|
||||
let _server = sc64::new_server(port, args.address.clone())?;
|
||||
|
||||
let exit = setup_exit_flag();
|
||||
while !exit.load(Ordering::Relaxed) {}
|
||||
sc64::run_server(port, args.address.clone(), |event| match event {
|
||||
ServerEvent::StartedListening(address) => println!(
|
||||
"{}: Started listening on address {}",
|
||||
"[Server]".bold(),
|
||||
address
|
||||
),
|
||||
ServerEvent::NewConnection(peer) => {
|
||||
println!("{}: New connection from {}", "[Server]".bold(), peer);
|
||||
}
|
||||
ServerEvent::Disconnected(peer) => {
|
||||
println!("{}: Client {} disconnected", "[Server]".bold(), peer);
|
||||
}
|
||||
ServerEvent::Err(error) => {
|
||||
println!(
|
||||
"{}: Client disconnected with error: {}",
|
||||
"[Server]".bold(),
|
||||
error
|
||||
);
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,15 +1,43 @@
|
||||
use super::{error::Error, utils};
|
||||
use std::{collections::VecDeque, time::Duration};
|
||||
use super::error::Error;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
io::{BufRead, BufReader, BufWriter, ErrorKind, Read, Write},
|
||||
net::{TcpListener, TcpStream},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub struct LocalDevice {
|
||||
pub port: String,
|
||||
pub serial_number: String,
|
||||
enum DataType {
|
||||
Command,
|
||||
Response,
|
||||
Packet,
|
||||
}
|
||||
|
||||
pub struct Command {
|
||||
impl From<DataType> for u32 {
|
||||
fn from(value: DataType) -> Self {
|
||||
match value {
|
||||
DataType::Command => 1,
|
||||
DataType::Response => 2,
|
||||
DataType::Packet => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for DataType {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
1 => Self::Command,
|
||||
2 => Self::Response,
|
||||
3 => Self::Packet,
|
||||
_ => return Err(Error::new("Unknown data type")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Command<'a> {
|
||||
pub id: u8,
|
||||
pub args: [u32; 2],
|
||||
pub data: Vec<u8>,
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
pub struct Response {
|
||||
@ -23,32 +51,20 @@ pub struct Packet {
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
enum DataType {
|
||||
Response,
|
||||
Packet,
|
||||
}
|
||||
|
||||
pub trait Link {
|
||||
fn execute_command(&mut self, command: &Command) -> Result<Vec<u8>, Error>;
|
||||
fn execute_command_raw(
|
||||
trait Backend {
|
||||
fn send_command(&mut self, command: &Command) -> Result<(), Error>;
|
||||
fn process_incoming_data(
|
||||
&mut self,
|
||||
command: &Command,
|
||||
timeout: Duration,
|
||||
no_response: bool,
|
||||
ignore_error: bool,
|
||||
) -> Result<Vec<u8>, Error>;
|
||||
fn receive_packet(&mut self) -> Result<Option<Packet>, Error>;
|
||||
data_type: DataType,
|
||||
packets: &mut VecDeque<Packet>,
|
||||
) -> Result<Option<Response>, Error>;
|
||||
}
|
||||
|
||||
pub struct SerialLink {
|
||||
struct SerialBackend {
|
||||
serial: Box<dyn serialport::SerialPort>,
|
||||
packets: VecDeque<Packet>,
|
||||
}
|
||||
|
||||
const COMMAND_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const PACKET_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
impl SerialLink {
|
||||
impl SerialBackend {
|
||||
fn reset(&mut self) -> Result<(), Error> {
|
||||
const WAIT_DURATION: Duration = Duration::from_millis(10);
|
||||
const RETRY_COUNT: i32 = 100;
|
||||
@ -78,37 +94,33 @@ impl SerialLink {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn serial_send_command(&mut self, command: &Command, timeout: Duration) -> Result<(), Error> {
|
||||
let mut header: Vec<u8> = Vec::new();
|
||||
header.append(&mut b"CMD".to_vec());
|
||||
header.append(&mut [command.id].to_vec());
|
||||
header.append(&mut command.args[0].to_be_bytes().to_vec());
|
||||
header.append(&mut command.args[1].to_be_bytes().to_vec());
|
||||
impl Backend for SerialBackend {
|
||||
fn send_command(&mut 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.serial.set_timeout(timeout)?;
|
||||
self.serial.write_all(&header)?;
|
||||
self.serial.write_all(&command.data)?;
|
||||
|
||||
self.serial.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serial_process_incoming_data(
|
||||
fn process_incoming_data(
|
||||
&mut self,
|
||||
data_type: DataType,
|
||||
timeout: Duration,
|
||||
packets: &mut VecDeque<Packet>,
|
||||
) -> Result<Option<Response>, Error> {
|
||||
const HEADER_SIZE: u32 = 8;
|
||||
let mut buffer = [0u8; 4];
|
||||
|
||||
let mut buffer = [0u8; HEADER_SIZE as usize];
|
||||
|
||||
self.serial.set_timeout(timeout)?;
|
||||
|
||||
while matches!(data_type, DataType::Response) || self.serial.bytes_to_read()? >= HEADER_SIZE
|
||||
while matches!(data_type, DataType::Response)
|
||||
|| self.serial.bytes_to_read()? as usize >= buffer.len()
|
||||
{
|
||||
self.serial.read_exact(&mut buffer)?;
|
||||
|
||||
let (packet_token, error) = (match &buffer[0..3] {
|
||||
b"CMP" => Ok((false, false)),
|
||||
b"PKT" => Ok((true, false)),
|
||||
@ -116,15 +128,17 @@ impl SerialLink {
|
||||
_ => Err(Error::new("Unknown response token")),
|
||||
})?;
|
||||
let id = buffer[3];
|
||||
let length = utils::u32_from_vec(&buffer[4..8])? as usize;
|
||||
|
||||
self.serial.read_exact(&mut buffer)?;
|
||||
let length = u32::from_be_bytes(buffer) as usize;
|
||||
|
||||
let mut data = vec![0u8; length];
|
||||
self.serial.read_exact(&mut data)?;
|
||||
|
||||
if packet_token {
|
||||
self.packets.push_back(Packet { id, data });
|
||||
packets.push_back(Packet { id, data });
|
||||
if matches!(data_type, DataType::Packet) {
|
||||
return Ok(None);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return Ok(Some(Response { id, error, data }));
|
||||
@ -133,36 +147,158 @@ impl SerialLink {
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn send_command(&mut self, command: &Command) -> Result<(), Error> {
|
||||
self.serial_send_command(command, COMMAND_TIMEOUT)
|
||||
}
|
||||
fn new_serial_backend(port: &str) -> Result<SerialBackend, Error> {
|
||||
let mut backend = SerialBackend {
|
||||
serial: serialport::new(port, 115_200)
|
||||
.timeout(Duration::from_secs(10))
|
||||
.open()?,
|
||||
};
|
||||
backend.reset()?;
|
||||
Ok(backend)
|
||||
}
|
||||
|
||||
fn receive_response(&mut self, timeout: Duration) -> Result<Response, Error> {
|
||||
if let Some(response) = self.serial_process_incoming_data(DataType::Response, timeout)? {
|
||||
return Ok(response);
|
||||
}
|
||||
Err(Error::new("Command response timeout"))
|
||||
struct TcpBackend {
|
||||
stream: TcpStream,
|
||||
reader: BufReader<TcpStream>,
|
||||
writer: BufWriter<TcpStream>,
|
||||
}
|
||||
|
||||
impl TcpBackend {
|
||||
fn bytes_to_read(&mut self) -> Result<usize, Error> {
|
||||
self.stream.set_nonblocking(true)?;
|
||||
let result = self.reader.fill_buf();
|
||||
let length = match result {
|
||||
Ok(buffer) => buffer.len(),
|
||||
Err(error) => {
|
||||
if error.kind() == ErrorKind::WouldBlock {
|
||||
0
|
||||
} else {
|
||||
return Err(error.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
self.stream.set_nonblocking(false)?;
|
||||
return Ok(length);
|
||||
}
|
||||
}
|
||||
|
||||
impl Link for SerialLink {
|
||||
fn execute_command(&mut self, command: &Command) -> Result<Vec<u8>, Error> {
|
||||
self.execute_command_raw(command, COMMAND_TIMEOUT, false, false)
|
||||
impl Backend for TcpBackend {
|
||||
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.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())?;
|
||||
|
||||
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.writer.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_command_raw(
|
||||
fn process_incoming_data(
|
||||
&mut self,
|
||||
data_type: DataType,
|
||||
packets: &mut VecDeque<Packet>,
|
||||
) -> Result<Option<Response>, Error> {
|
||||
let mut buffer = [0u8; 4];
|
||||
|
||||
while matches!(data_type, DataType::Response) || self.bytes_to_read()? >= 4 {
|
||||
self.reader.read_exact(&mut buffer)?;
|
||||
let payload_data_type: DataType = u32::from_be_bytes(buffer).try_into()?;
|
||||
|
||||
match payload_data_type {
|
||||
DataType::Response => {
|
||||
let mut response_info = vec![0u8; 2];
|
||||
self.reader.read_exact(&mut response_info)?;
|
||||
|
||||
self.reader.read_exact(&mut buffer)?;
|
||||
let response_data_length = u32::from_be_bytes(buffer) as usize;
|
||||
|
||||
let mut data = vec![0u8; response_data_length];
|
||||
self.reader.read_exact(&mut data)?;
|
||||
|
||||
return Ok(Some(Response {
|
||||
id: response_info[0],
|
||||
error: response_info[1] != 0,
|
||||
data,
|
||||
}));
|
||||
}
|
||||
DataType::Packet => {
|
||||
let mut packet_info = vec![0u8; 1];
|
||||
self.reader.read_exact(&mut packet_info)?;
|
||||
|
||||
self.reader.read_exact(&mut buffer)?;
|
||||
let packet_data_length = u32::from_be_bytes(buffer) as usize;
|
||||
|
||||
let mut data = vec![0u8; packet_data_length];
|
||||
self.reader.read_exact(&mut data)?;
|
||||
|
||||
packets.push_back(Packet {
|
||||
id: packet_info[0],
|
||||
data,
|
||||
});
|
||||
if matches!(data_type, DataType::Packet) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::new("Unexpected payload data type received")),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
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_secs(10)))?;
|
||||
stream
|
||||
}
|
||||
Err(error) => {
|
||||
return Err(Error::new(
|
||||
format!("Couldn't connect to [{address}]: {error}").as_str(),
|
||||
))
|
||||
}
|
||||
};
|
||||
let reader = BufReader::new(stream.try_clone()?);
|
||||
let writer = BufWriter::new(stream.try_clone()?);
|
||||
Ok(TcpBackend {
|
||||
stream,
|
||||
reader,
|
||||
writer,
|
||||
})
|
||||
}
|
||||
|
||||
pub struct Link {
|
||||
backend: Box<dyn Backend>,
|
||||
packets: VecDeque<Packet>,
|
||||
}
|
||||
|
||||
impl Link {
|
||||
pub fn execute_command(&mut self, command: &Command) -> Result<Vec<u8>, Error> {
|
||||
self.execute_command_raw(command, false, false)
|
||||
}
|
||||
|
||||
pub fn execute_command_raw(
|
||||
&mut self,
|
||||
command: &Command,
|
||||
timeout: Duration,
|
||||
no_response: bool,
|
||||
ignore_error: bool,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
self.send_command(command)?;
|
||||
self.backend.send_command(command)?;
|
||||
if no_response {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let response = self.receive_response(timeout)?;
|
||||
let response = self.receive_response()?;
|
||||
if command.id != response.id {
|
||||
return Err(Error::new("Command response ID didn't match"));
|
||||
}
|
||||
@ -172,14 +308,53 @@ impl Link for SerialLink {
|
||||
Ok(response.data)
|
||||
}
|
||||
|
||||
fn receive_packet(&mut self) -> Result<Option<Packet>, Error> {
|
||||
fn receive_response(&mut self) -> Result<Response, Error> {
|
||||
match self
|
||||
.backend
|
||||
.process_incoming_data(DataType::Response, &mut self.packets)
|
||||
{
|
||||
Ok(response) => match response {
|
||||
Some(response) => Ok(response),
|
||||
None => Err(Error::new("No response was received")),
|
||||
},
|
||||
Err(error) => Err(Error::new(
|
||||
format!("Command response error: {error}").as_str(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_packet(&mut self) -> Result<Option<Packet>, Error> {
|
||||
if self.packets.len() == 0 {
|
||||
self.serial_process_incoming_data(DataType::Packet, PACKET_TIMEOUT)?;
|
||||
let response = self
|
||||
.backend
|
||||
.process_incoming_data(DataType::Packet, &mut self.packets)?;
|
||||
if response.is_some() {
|
||||
return Err(Error::new("Unexpected command response in data stream"));
|
||||
}
|
||||
}
|
||||
Ok(self.packets.pop_front())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_local(port: &str) -> Result<Link, Error> {
|
||||
Ok(Link {
|
||||
backend: Box::new(new_serial_backend(port)?),
|
||||
packets: VecDeque::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_remote(address: &str) -> Result<Link, Error> {
|
||||
Ok(Link {
|
||||
backend: Box::new(new_tcp_backend(address)?),
|
||||
packets: VecDeque::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub struct LocalDevice {
|
||||
pub port: String,
|
||||
pub serial_number: String,
|
||||
}
|
||||
|
||||
pub fn list_local_devices() -> Result<Vec<LocalDevice>, Error> {
|
||||
const SC64_VID: u16 = 0x0403;
|
||||
const SC64_PID: u16 = 0x6014;
|
||||
@ -206,13 +381,109 @@ pub fn list_local_devices() -> Result<Vec<LocalDevice>, Error> {
|
||||
return Ok(serial_devices);
|
||||
}
|
||||
|
||||
pub fn new_local(port: &str) -> Result<Box<dyn Link>, Error> {
|
||||
let mut link = SerialLink {
|
||||
serial: serialport::new(port, 115_200).open()?,
|
||||
packets: VecDeque::new(),
|
||||
};
|
||||
|
||||
link.reset()?;
|
||||
|
||||
Ok(Box::new(link))
|
||||
pub enum ServerEvent {
|
||||
StartedListening(String),
|
||||
NewConnection(String),
|
||||
Disconnected(String),
|
||||
Err(String),
|
||||
}
|
||||
|
||||
pub fn run_server(
|
||||
port: &str,
|
||||
address: String,
|
||||
event_callback: fn(ServerEvent),
|
||||
) -> Result<(), Error> {
|
||||
let listener = TcpListener::bind(address)?;
|
||||
|
||||
event_callback(ServerEvent::StartedListening(
|
||||
listener.local_addr()?.to_string(),
|
||||
));
|
||||
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(mut stream) => match server_accept_connection(port, event_callback, &mut stream) {
|
||||
Ok(()) => {}
|
||||
Err(error) => event_callback(ServerEvent::Err(error.to_string())),
|
||||
},
|
||||
Err(error) => return Err(error.into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn server_accept_connection(
|
||||
port: &str,
|
||||
event_callback: fn(ServerEvent),
|
||||
stream: &mut TcpStream,
|
||||
) -> Result<(), Error> {
|
||||
stream.set_nonblocking(true)?;
|
||||
let peer = stream.peer_addr()?.to_string();
|
||||
|
||||
let mut serial_backend = new_serial_backend(port)?;
|
||||
serial_backend.reset()?;
|
||||
|
||||
let mut packets: VecDeque<Packet> = VecDeque::new();
|
||||
|
||||
let mut buffer = [0u8; 4];
|
||||
|
||||
event_callback(ServerEvent::NewConnection(peer.clone()));
|
||||
|
||||
loop {
|
||||
match stream.read_exact(&mut buffer) {
|
||||
Ok(()) => {
|
||||
let data_type: DataType = u32::from_be_bytes(buffer).try_into()?;
|
||||
|
||||
if !matches!(data_type, DataType::Command) {
|
||||
return Err(Error::new("Received data type wasn't a command data type"));
|
||||
}
|
||||
|
||||
let mut id_buffer = [0u8; 1];
|
||||
let mut args = [0u32; 2];
|
||||
|
||||
stream.read_exact(&mut id_buffer)?;
|
||||
stream.read_exact(&mut buffer)?;
|
||||
args[0] = u32::from_be_bytes(buffer);
|
||||
stream.read_exact(&mut buffer)?;
|
||||
args[1] = u32::from_be_bytes(buffer);
|
||||
|
||||
stream.read_exact(&mut buffer)?;
|
||||
let command_data_length = u32::from_be_bytes(buffer) as usize;
|
||||
let mut data = vec![0u8; command_data_length];
|
||||
stream.read_exact(&mut data)?;
|
||||
|
||||
serial_backend.send_command(&Command {
|
||||
id: id_buffer[0],
|
||||
args,
|
||||
data: &data,
|
||||
})?;
|
||||
|
||||
continue;
|
||||
}
|
||||
Err(error) => {
|
||||
if error.kind() != ErrorKind::WouldBlock {
|
||||
event_callback(ServerEvent::Disconnected(peer.clone()));
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(response) =
|
||||
serial_backend.process_incoming_data(DataType::Packet, &mut packets)?
|
||||
{
|
||||
stream.write_all(&u32::to_be_bytes(DataType::Response.into()))?;
|
||||
stream.write_all(&[response.id])?;
|
||||
stream.write_all(&[response.error as u8])?;
|
||||
stream.write_all(&(response.data.len() as u32).to_be_bytes())?;
|
||||
stream.write_all(&response.data)?;
|
||||
stream.flush()?;
|
||||
} else if let Some(packet) = packets.pop_front() {
|
||||
stream.write_all(&u32::to_be_bytes(DataType::Packet.into()))?;
|
||||
stream.write_all(&[packet.id])?;
|
||||
stream.write_all(&(packet.data.len() as u32).to_be_bytes())?;
|
||||
stream.write_all(&packet.data)?;
|
||||
stream.flush()?;
|
||||
} else {
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ mod utils;
|
||||
|
||||
pub use self::{
|
||||
error::Error,
|
||||
link::list_local_devices,
|
||||
link::{list_local_devices, ServerEvent},
|
||||
types::{
|
||||
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
|
||||
DebugPacket, DiskPacket, FpgaDebugData, McuStackUsage, SaveType, Switch, TvType,
|
||||
@ -20,7 +20,7 @@ use self::{
|
||||
types::{
|
||||
get_config, get_setting, Config, ConfigId, FirmwareStatus, Setting, SettingId, UpdateStatus,
|
||||
},
|
||||
utils::{args_from_vec, datetime_from_vec, u32_from_vec, vec_from_datetime},
|
||||
utils::{convert_from_datetime, convert_to_datetime},
|
||||
};
|
||||
use chrono::{DateTime, Local};
|
||||
use std::{
|
||||
@ -30,7 +30,7 @@ use std::{
|
||||
};
|
||||
|
||||
pub struct SC64 {
|
||||
link: Box<dyn Link>,
|
||||
link: Link,
|
||||
}
|
||||
|
||||
pub struct DeviceState {
|
||||
@ -86,11 +86,8 @@ const FLASHRAM_LENGTH: usize = 128 * 1024;
|
||||
const BOOTLOADER_ADDRESS: u32 = 0x04E0_0000;
|
||||
|
||||
const FIRMWARE_ADDRESS: u32 = 0x0010_0000; // Arbitrary offset in SDRAM memory
|
||||
const FIRMWARE_COMMAND_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const FIRMWARE_UPDATE_TIMEOUT: Duration = Duration::from_secs(90);
|
||||
|
||||
const USB_WRITE_COMMAND_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
const ISV_BUFFER_LENGTH: usize = 64 * 1024;
|
||||
|
||||
pub const MEMORY_LENGTH: usize = 0x0500_2980;
|
||||
@ -98,23 +95,33 @@ pub const MEMORY_LENGTH: usize = 0x0500_2980;
|
||||
const MEMORY_CHUNK_LENGTH: usize = 1 * 1024 * 1024;
|
||||
|
||||
impl SC64 {
|
||||
fn command_identifier_get(&mut self) -> Result<Vec<u8>, Error> {
|
||||
let identifier = self.link.execute_command(&Command {
|
||||
fn command_identifier_get(&mut self) -> Result<[u8; 4], Error> {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'v',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(identifier)
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for identifier get command",
|
||||
));
|
||||
}
|
||||
Ok(data[0..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
fn command_version_get(&mut self) -> Result<(u16, u16), Error> {
|
||||
let version = self.link.execute_command(&Command {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'V',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
let major = utils::u16_from_vec(&version[0..2])?;
|
||||
let minor = utils::u16_from_vec(&version[2..4])?;
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for version get command",
|
||||
));
|
||||
}
|
||||
let major = u16::from_be_bytes(data[0..2].try_into().unwrap());
|
||||
let minor = u16::from_be_bytes(data[2..4].try_into().unwrap());
|
||||
Ok((major, minor))
|
||||
}
|
||||
|
||||
@ -122,7 +129,7 @@ impl SC64 {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'R',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@ -133,13 +140,14 @@ impl SC64 {
|
||||
seed: u8,
|
||||
checksum: &[u8; 6],
|
||||
) -> Result<(), Error> {
|
||||
let mut params: Vec<u8> = vec![];
|
||||
params.append(&mut [(disable as u8) << 0, seed].to_vec());
|
||||
params.append(&mut checksum.to_vec());
|
||||
let args = [
|
||||
u32::from_be_bytes([(disable as u8) << 0, seed, checksum[0], checksum[1]]),
|
||||
u32::from_be_bytes([checksum[2], checksum[3], checksum[4], checksum[5]]),
|
||||
];
|
||||
self.link.execute_command(&Command {
|
||||
id: b'B',
|
||||
args: args_from_vec(¶ms[0..8])?,
|
||||
data: vec![],
|
||||
args,
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@ -148,9 +156,14 @@ impl SC64 {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'c',
|
||||
args: [config_id.into(), 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
let value = u32_from_vec(&data[0..4])?;
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for config get command",
|
||||
));
|
||||
}
|
||||
let value = u32::from_be_bytes(data[0..4].try_into().unwrap());
|
||||
Ok((config_id, value).try_into()?)
|
||||
}
|
||||
|
||||
@ -158,7 +171,7 @@ impl SC64 {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'C',
|
||||
args: config.into(),
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@ -167,9 +180,14 @@ impl SC64 {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'a',
|
||||
args: [setting_id.into(), 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
let value = u32_from_vec(&data[0..4])?;
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for setting get command",
|
||||
));
|
||||
}
|
||||
let value = u32::from_be_bytes(data[0..4].try_into().unwrap());
|
||||
Ok((setting_id, value).try_into()?)
|
||||
}
|
||||
|
||||
@ -177,7 +195,7 @@ impl SC64 {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'A',
|
||||
args: setting.into(),
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@ -186,16 +204,21 @@ impl SC64 {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b't',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(datetime_from_vec(&data[0..8])?)
|
||||
if data.len() != 8 {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for time get command",
|
||||
));
|
||||
}
|
||||
Ok(convert_to_datetime(&data[0..8].try_into().unwrap())?)
|
||||
}
|
||||
|
||||
fn command_time_set(&mut self, datetime: DateTime<Local>) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'T',
|
||||
args: args_from_vec(&vec_from_datetime(datetime)?[0..8])?,
|
||||
data: vec![],
|
||||
args: convert_from_datetime(datetime),
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@ -204,8 +227,13 @@ impl SC64 {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'm',
|
||||
args: [address, length as u32],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
if data.len() != length {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for memory read command",
|
||||
));
|
||||
}
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
@ -213,7 +241,7 @@ impl SC64 {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'M',
|
||||
args: [address, data.len() as u32],
|
||||
data: data.to_vec(),
|
||||
data,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@ -223,9 +251,8 @@ impl SC64 {
|
||||
&Command {
|
||||
id: b'U',
|
||||
args: [datatype as u32, data.len() as u32],
|
||||
data: data.to_vec(),
|
||||
data,
|
||||
},
|
||||
USB_WRITE_COMMAND_TIMEOUT,
|
||||
true,
|
||||
false,
|
||||
)?;
|
||||
@ -236,25 +263,31 @@ impl SC64 {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'D',
|
||||
args: [error as u32, 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn command_flash_wait_busy(&mut self, wait: bool) -> Result<u32, Error> {
|
||||
let erase_block_size = self.link.execute_command(&Command {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'p',
|
||||
args: [wait as u32, 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(utils::u32_from_vec(&erase_block_size[0..4])?)
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for flash wait busy command",
|
||||
));
|
||||
}
|
||||
let erase_block_size = u32::from_be_bytes(data[0..4].try_into().unwrap());
|
||||
Ok(erase_block_size)
|
||||
}
|
||||
|
||||
fn command_flash_erase_block(&mut self, address: u32) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'P',
|
||||
args: [address, 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
@ -264,15 +297,19 @@ impl SC64 {
|
||||
&Command {
|
||||
id: b'f',
|
||||
args: [address, 0],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
},
|
||||
FIRMWARE_COMMAND_TIMEOUT,
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
let status = FirmwareStatus::try_from(utils::u32_from_vec(&data[0..4])?)?;
|
||||
let length = utils::u32_from_vec(&data[4..8])?;
|
||||
Ok((status, length))
|
||||
if data.len() != 8 {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for firmware backup command",
|
||||
));
|
||||
}
|
||||
let status = u32::from_be_bytes(data[0..4].try_into().unwrap());
|
||||
let length = u32::from_be_bytes(data[4..8].try_into().unwrap());
|
||||
Ok((status.try_into()?, length))
|
||||
}
|
||||
|
||||
fn command_firmware_update(
|
||||
@ -284,33 +321,35 @@ impl SC64 {
|
||||
&Command {
|
||||
id: b'F',
|
||||
args: [address, length as u32],
|
||||
data: vec![],
|
||||
data: &[],
|
||||
},
|
||||
FIRMWARE_COMMAND_TIMEOUT,
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
Ok(FirmwareStatus::try_from(utils::u32_from_vec(&data[0..4])?)?)
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
"Invalid data length received for firmware update command",
|
||||
));
|
||||
}
|
||||
Ok(u32::from_be_bytes(data[0..4].try_into().unwrap()).try_into()?)
|
||||
}
|
||||
|
||||
fn command_debug_get(&mut self) -> Result<FpgaDebugData, Error> {
|
||||
self.link
|
||||
.execute_command(&Command {
|
||||
id: b'?',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
})?
|
||||
.try_into()
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'?',
|
||||
args: [0, 0],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(data.try_into()?)
|
||||
}
|
||||
|
||||
fn command_stack_usage_get(&mut self) -> Result<McuStackUsage, Error> {
|
||||
self.link
|
||||
.execute_command(&Command {
|
||||
id: b'%',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
})?
|
||||
.try_into()
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'%',
|
||||
args: [0, 0],
|
||||
data: &[],
|
||||
})?;
|
||||
Ok(data.try_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,6 +603,16 @@ impl SC64 {
|
||||
self.command_usb_write(debug_packet.datatype, &debug_packet.data)
|
||||
}
|
||||
|
||||
pub fn check_device(&mut self) -> Result<(), Error> {
|
||||
let identifier = self.command_identifier_get().map_err(|e| {
|
||||
Error::new(format!("Couldn't get SC64 device identifier: {e}").as_str())
|
||||
})?;
|
||||
if &identifier != SC64_V2_IDENTIFIER {
|
||||
return Err(Error::new("Unknown identifier received, not a SC64 device"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_firmware_version(&mut self) -> Result<(u16, u16), Error> {
|
||||
let (major, minor) = self
|
||||
.command_version_get()
|
||||
@ -626,8 +675,9 @@ impl SC64 {
|
||||
if timeout.elapsed() > FIRMWARE_UPDATE_TIMEOUT {
|
||||
return Err(Error::new(
|
||||
format!(
|
||||
"Firmware update timeout, SC64 did not finish update in {} seconds",
|
||||
FIRMWARE_UPDATE_TIMEOUT.as_secs()
|
||||
"Firmware update timeout, SC64 did not finish update in {} seconds, last step: {}",
|
||||
FIRMWARE_UPDATE_TIMEOUT.as_secs(),
|
||||
last_update_status
|
||||
)
|
||||
.as_str(),
|
||||
));
|
||||
@ -706,28 +756,30 @@ pub fn new_local(port: Option<String>) -> Result<SC64, Error> {
|
||||
} else {
|
||||
list_local_devices()?[0].port.clone()
|
||||
};
|
||||
|
||||
let mut sc64 = SC64 {
|
||||
link: link::new_local(&port)?,
|
||||
};
|
||||
|
||||
let identifier = sc64
|
||||
.command_identifier_get()
|
||||
.map_err(|_| Error::new("Couldn't get SC64 device identifier"))?;
|
||||
|
||||
if identifier != SC64_V2_IDENTIFIER {
|
||||
return Err(Error::new("Unknown identifier received, not a SC64 device"));
|
||||
}
|
||||
|
||||
sc64.check_device()?;
|
||||
Ok(sc64)
|
||||
}
|
||||
|
||||
pub fn new_remote(remote: String) -> Result<SC64, Error> {
|
||||
let _ = remote;
|
||||
Err(Error::new("Remote connection not implemented yet"))
|
||||
pub fn new_remote(address: String) -> Result<SC64, Error> {
|
||||
let mut sc64 = SC64 {
|
||||
link: link::new_remote(&address)?,
|
||||
};
|
||||
sc64.check_device()?;
|
||||
Ok(sc64)
|
||||
}
|
||||
|
||||
pub fn new_server(port: Option<String>, bind: String) -> Result<(), Error> {
|
||||
let _ = (port, bind);
|
||||
Err(Error::new("SC64 server not implemented yet"))
|
||||
pub fn run_server(
|
||||
port: Option<String>,
|
||||
address: String,
|
||||
event_callback: fn(ServerEvent),
|
||||
) -> Result<(), Error> {
|
||||
let port = if let Some(port) = port {
|
||||
port
|
||||
} else {
|
||||
list_local_devices()?[0].port.clone()
|
||||
};
|
||||
link::run_server(&port, address, event_callback)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{link::Packet, utils::u32_from_vec, Error};
|
||||
use super::{link::Packet, Error};
|
||||
use encoding_rs::EUC_JP;
|
||||
use std::fmt::Display;
|
||||
|
||||
@ -591,7 +591,16 @@ impl TryFrom<Packet> for DataPacket {
|
||||
b'U' => Self::Debug(value.data.try_into()?),
|
||||
b'D' => Self::Disk(value.data.try_into()?),
|
||||
b'I' => Self::IsViewer(EUC_JP.decode(&value.data).0.into()),
|
||||
b'F' => Self::UpdateStatus(u32_from_vec(&value.data[0..4])?.try_into()?),
|
||||
b'F' => {
|
||||
if value.data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
"Incorrect data length for update status data packet",
|
||||
));
|
||||
}
|
||||
Self::UpdateStatus(
|
||||
u32::from_be_bytes(value.data[0..4].try_into().unwrap()).try_into()?,
|
||||
)
|
||||
}
|
||||
_ => return Err(Error::new("Unknown data packet code")),
|
||||
})
|
||||
}
|
||||
@ -608,7 +617,7 @@ impl TryFrom<Vec<u8>> for DebugPacket {
|
||||
if value.len() < 4 {
|
||||
return Err(Error::new("Couldn't extract header from debug packet"));
|
||||
}
|
||||
let header = u32_from_vec(&value[0..4])?;
|
||||
let header = u32::from_be_bytes(value[0..4].try_into().unwrap());
|
||||
let datatype = ((header >> 24) & 0xFF) as u8;
|
||||
let length = (header & 0x00FFFFFF) as usize;
|
||||
let data = value[4..].to_vec();
|
||||
@ -630,9 +639,9 @@ impl TryFrom<Vec<u8>> for DiskPacket {
|
||||
if value.len() < 12 {
|
||||
return Err(Error::new("Couldn't extract block info from disk packet"));
|
||||
}
|
||||
let command = u32_from_vec(&value[0..4])?;
|
||||
let address = u32_from_vec(&value[4..8])?;
|
||||
let track_head_block = u32_from_vec(&value[8..12])?;
|
||||
let command = u32::from_be_bytes(value[0..4].try_into().unwrap());
|
||||
let address = u32::from_be_bytes(value[4..8].try_into().unwrap());
|
||||
let track_head_block = u32::from_be_bytes(value[8..12].try_into().unwrap());
|
||||
let disk_block = DiskBlock {
|
||||
address,
|
||||
track: (track_head_block >> 2) & 0xFFF,
|
||||
@ -678,7 +687,7 @@ impl Display for FirmwareStatus {
|
||||
FirmwareStatus::Ok => "OK",
|
||||
FirmwareStatus::ErrToken => "Invalid firmware header",
|
||||
FirmwareStatus::ErrChecksum => "Invalid chunk checksum",
|
||||
FirmwareStatus::ErrSize => "Invalid firmware size",
|
||||
FirmwareStatus::ErrSize => "Invalid chunk size",
|
||||
FirmwareStatus::ErrUnknownChunk => "Unknown chunk in firmware",
|
||||
FirmwareStatus::ErrRead => "Firmware read error",
|
||||
FirmwareStatus::ErrAddress => "Invalid address or length provided",
|
||||
@ -747,11 +756,11 @@ pub struct FpgaDebugData {
|
||||
impl TryFrom<Vec<u8>> for FpgaDebugData {
|
||||
type Error = Error;
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if value.len() < 8 {
|
||||
if value.len() != 8 {
|
||||
return Err(Error::new("Invalid data length for FPGA debug data"));
|
||||
}
|
||||
Ok(FpgaDebugData {
|
||||
last_pi_address: u32_from_vec(&value[0..4])?,
|
||||
last_pi_address: u32::from_be_bytes(value[0..4].try_into().unwrap()),
|
||||
read_fifo_wait: (value[7] & (1 << 0)) != 0,
|
||||
read_fifo_failure: (value[7] & (1 << 1)) != 0,
|
||||
write_fifo_wait: (value[7] & (1 << 2)) != 0,
|
||||
@ -789,14 +798,14 @@ pub struct McuStackUsage {
|
||||
impl TryFrom<Vec<u8>> for McuStackUsage {
|
||||
type Error = Error;
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if value.len() < 16 {
|
||||
if value.len() != 16 {
|
||||
return Err(Error::new("Invalid data length for MCU stack usage"));
|
||||
}
|
||||
Ok(McuStackUsage {
|
||||
cic: u32_from_vec(&value[0..4])?,
|
||||
rtc: u32_from_vec(&value[4..8])?,
|
||||
led: u32_from_vec(&value[8..12])?,
|
||||
gvr: u32_from_vec(&value[12..16])?,
|
||||
cic: u32::from_be_bytes(value[0..4].try_into().unwrap()),
|
||||
rtc: u32::from_be_bytes(value[4..8].try_into().unwrap()),
|
||||
led: u32::from_be_bytes(value[8..12].try_into().unwrap()),
|
||||
gvr: u32::from_be_bytes(value[12..16].try_into().unwrap()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,6 @@
|
||||
use super::Error;
|
||||
use chrono::{DateTime, Datelike, Local, NaiveDateTime, TimeZone, Timelike};
|
||||
|
||||
pub fn u16_from_vec(data: &[u8]) -> Result<u16, Error> {
|
||||
if data.len() != 2 {
|
||||
return Err(Error::new("Invalid slice length provided to u16_from_vec"));
|
||||
}
|
||||
let bytes = data[0..2]
|
||||
.try_into()
|
||||
.map_err(|_| Error::new("Couldn't convert from bytes to u16"))?;
|
||||
Ok(u16::from_be_bytes(bytes))
|
||||
}
|
||||
|
||||
pub fn u32_from_vec(data: &[u8]) -> Result<u32, Error> {
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new("Invalid slice length provided to u32_from_vec"));
|
||||
}
|
||||
let bytes = data[0..4]
|
||||
.try_into()
|
||||
.map_err(|_| Error::new("Couldn't convert from bytes to u32"))?;
|
||||
Ok(u32::from_be_bytes(bytes))
|
||||
}
|
||||
|
||||
pub fn args_from_vec(data: &[u8]) -> Result<[u32; 2], Error> {
|
||||
if data.len() != 8 {
|
||||
return Err(Error::new("Invalid slice length provided to args_from_vec"));
|
||||
}
|
||||
Ok([u32_from_vec(&data[0..4])?, u32_from_vec(&data[4..8])?])
|
||||
}
|
||||
|
||||
pub fn u8_from_bcd(value: u8) -> u8 {
|
||||
(((value & 0xF0) >> 4) * 10) + (value & 0x0F)
|
||||
}
|
||||
@ -36,7 +9,7 @@ pub fn bcd_from_u8(value: u8) -> u8 {
|
||||
(((value / 10) & 0x0F) << 4) | ((value % 10) & 0x0F)
|
||||
}
|
||||
|
||||
pub fn datetime_from_vec(data: &[u8]) -> Result<DateTime<Local>, Error> {
|
||||
pub fn convert_to_datetime(data: &[u8; 8]) -> Result<DateTime<Local>, Error> {
|
||||
let hour = u8_from_bcd(data[1]);
|
||||
let minute = u8_from_bcd(data[2]);
|
||||
let second = u8_from_bcd(data[3]);
|
||||
@ -51,7 +24,7 @@ pub fn datetime_from_vec(data: &[u8]) -> Result<DateTime<Local>, Error> {
|
||||
Ok(Local.from_local_datetime(native).unwrap())
|
||||
}
|
||||
|
||||
pub fn vec_from_datetime(datetime: DateTime<Local>) -> Result<Vec<u8>, Error> {
|
||||
pub fn convert_from_datetime(datetime: DateTime<Local>) -> [u32; 2] {
|
||||
let weekday = bcd_from_u8((datetime.weekday() as u8) + 1);
|
||||
let hour = bcd_from_u8(datetime.hour() as u8);
|
||||
let minute = bcd_from_u8(datetime.minute() as u8);
|
||||
@ -59,5 +32,8 @@ pub fn vec_from_datetime(datetime: DateTime<Local>) -> Result<Vec<u8>, Error> {
|
||||
let year = bcd_from_u8((datetime.year() - 2000) as u8);
|
||||
let month = bcd_from_u8(datetime.month() as u8);
|
||||
let day = bcd_from_u8(datetime.day() as u8);
|
||||
Ok(vec![weekday, hour, minute, second, 0, year, month, day])
|
||||
[
|
||||
u32::from_be_bytes([weekday, hour, minute, second]),
|
||||
u32::from_be_bytes([0, year, month, day]),
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user