Initial KWP flashing
This commit is contained in:
parent
0b0280d553
commit
bca8965924
|
@ -1,5 +1,7 @@
|
|||
use std::iter;
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
|
||||
use std::iter;
|
||||
use egui::FontDefinitions;
|
||||
use epi::App;
|
||||
use ui::launcher::Launcher;
|
||||
|
|
|
@ -12,44 +12,16 @@ use super::{status_bar::MainStatusBar, StatusText};
|
|||
pub mod cfg_structs;
|
||||
|
||||
pub struct ConfigPage {
|
||||
nag: Arc<Mutex<Nag52USB>>,
|
||||
server: Arc<Mutex<Kwp2000DiagnosticServer>>,
|
||||
bar: MainStatusBar,
|
||||
server: Kwp2000DiagnosticServer,
|
||||
status: StatusText,
|
||||
scn: Option<TcmCoreConfig>
|
||||
}
|
||||
|
||||
impl ConfigPage {
|
||||
pub fn new(nag: Arc<Mutex<Nag52USB>>, bar: MainStatusBar) -> Self {
|
||||
let mut channel = Hardware::create_iso_tp_channel(nag.clone()).unwrap();
|
||||
let channel_cfg = ecu_diagnostics::channel::IsoTPSettings {
|
||||
block_size: 8,
|
||||
st_min: 20,
|
||||
extended_addressing: false,
|
||||
pad_frame: true,
|
||||
can_speed: 500_000,
|
||||
can_use_ext_addr: false,
|
||||
};
|
||||
let server_settings = Kwp2000ServerOptions {
|
||||
send_id: 0x07E1,
|
||||
recv_id: 0x07E9,
|
||||
read_timeout_ms: 2000,
|
||||
write_timeout_ms: 2000,
|
||||
global_tp_id: 0,
|
||||
tester_present_interval_ms: 2000,
|
||||
tester_present_require_response: true,
|
||||
global_session_control: false
|
||||
};
|
||||
let mut kwp = Kwp2000DiagnosticServer::new_over_iso_tp(
|
||||
server_settings,
|
||||
channel,
|
||||
channel_cfg,
|
||||
Kwp2000VoidHandler {},
|
||||
).unwrap();
|
||||
|
||||
pub fn new(server: Arc<Mutex<Kwp2000DiagnosticServer>>, bar: MainStatusBar) -> Self {
|
||||
Self {
|
||||
server: kwp,
|
||||
nag,
|
||||
server,
|
||||
bar,
|
||||
status: StatusText::Ok("".into()),
|
||||
scn: None
|
||||
|
@ -64,7 +36,7 @@ impl crate::window::InterfacePage for ConfigPage {
|
|||
|
||||
|
||||
if ui.button("Read Configuration").clicked() {
|
||||
match self.server.read_custom_local_identifier(0xFE) {
|
||||
match self.server.lock().unwrap().read_custom_local_identifier(0xFE) {
|
||||
Ok(res) => {
|
||||
self.scn = Some(TcmCoreConfig::from_bytes(res.try_into().unwrap()));
|
||||
self.status = StatusText::Ok(format!("Read OK!"));
|
||||
|
@ -191,9 +163,9 @@ impl crate::window::InterfacePage for ConfigPage {
|
|||
let res = {
|
||||
let mut x: Vec<u8> = vec![0x3B, 0xFE];
|
||||
x.extend_from_slice(&scn.clone().into_bytes());
|
||||
self.server.set_diagnostic_session_mode(SessionType::ExtendedDiagnostics);
|
||||
self.server.send_byte_array_with_response(&x);
|
||||
self.server.reset_ecu(ResetMode::PowerOnReset);
|
||||
self.server.lock().unwrap().set_diagnostic_session_mode(SessionType::ExtendedDiagnostics);
|
||||
self.server.lock().unwrap().send_byte_array_with_response(&x);
|
||||
self.server.lock().unwrap().reset_ecu(ResetMode::PowerOnReset);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,8 @@ pub enum CommandStatus {
|
|||
}
|
||||
|
||||
pub struct DiagnosticsPage{
|
||||
nag: Arc<Mutex<Nag52USB>>,
|
||||
bar: MainStatusBar,
|
||||
server: Kwp2000DiagnosticServer,
|
||||
server: Arc<Mutex<Kwp2000DiagnosticServer>>,
|
||||
last_req_time: Instant,
|
||||
text: CommandStatus,
|
||||
record_data: Option<LocalRecordData>,
|
||||
|
@ -40,37 +39,9 @@ pub struct DiagnosticsPage{
|
|||
}
|
||||
|
||||
impl DiagnosticsPage {
|
||||
pub fn new(nag: Arc<Mutex<Nag52USB>>, bar: MainStatusBar) -> Self {
|
||||
|
||||
let mut channel = Hardware::create_iso_tp_channel(nag.clone()).unwrap();
|
||||
let channel_cfg = ecu_diagnostics::channel::IsoTPSettings {
|
||||
block_size: 8,
|
||||
st_min: 20,
|
||||
extended_addressing: false,
|
||||
pad_frame: true,
|
||||
can_speed: 500_000,
|
||||
can_use_ext_addr: false,
|
||||
};
|
||||
let server_settings = Kwp2000ServerOptions {
|
||||
send_id: 0x07E1,
|
||||
recv_id: 0x07E9,
|
||||
read_timeout_ms: 2000,
|
||||
write_timeout_ms: 2000,
|
||||
global_tp_id: 0,
|
||||
tester_present_interval_ms: 2000,
|
||||
tester_present_require_response: true,
|
||||
global_session_control: false
|
||||
};
|
||||
let mut kwp = Kwp2000DiagnosticServer::new_over_iso_tp(
|
||||
server_settings,
|
||||
channel,
|
||||
channel_cfg,
|
||||
Kwp2000VoidHandler {},
|
||||
).unwrap();
|
||||
|
||||
pub fn new(server: Arc<Mutex<Kwp2000DiagnosticServer>>, bar: MainStatusBar) -> Self {
|
||||
Self {
|
||||
server: kwp,
|
||||
nag,
|
||||
server,
|
||||
bar,
|
||||
last_req_time: Instant::now(),
|
||||
text: CommandStatus::Ok("".into()),
|
||||
|
@ -90,7 +61,7 @@ impl crate::window::InterfacePage for DiagnosticsPage {
|
|||
ui.heading("This is experimental, use with MOST up-to-date firmware");
|
||||
|
||||
if ui.button("Query ECU Serial number").clicked() {
|
||||
match self.server.read_ecu_serial_number() {
|
||||
match self.server.lock().unwrap().read_ecu_serial_number() {
|
||||
Ok(b) => {
|
||||
self.text = CommandStatus::Ok(format!("ECU Serial: {}", String::from_utf8_lossy(&b).to_string()))
|
||||
},
|
||||
|
@ -99,7 +70,7 @@ impl crate::window::InterfacePage for DiagnosticsPage {
|
|||
}
|
||||
|
||||
if ui.button("Query ECU data").clicked() {
|
||||
match self.server.read_daimler_identification() {
|
||||
match self.server.lock().unwrap().read_daimler_identification() {
|
||||
Ok(b) => {
|
||||
self.text = CommandStatus::Ok(format!(
|
||||
r#"
|
||||
|
@ -156,7 +127,7 @@ impl crate::window::InterfacePage for DiagnosticsPage {
|
|||
self.last_query_time = Instant::now();
|
||||
self.chart_idx += 100;
|
||||
if let Some(rid) = self.record_to_query {
|
||||
if let Ok(r) = rid.query_ecu(&mut self.server) {
|
||||
if let Ok(r) = rid.query_ecu(&mut self.server.lock().unwrap()) {
|
||||
self.record_data = Some(r)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,16 @@ use std::{
|
|||
sync::{
|
||||
atomic::{AtomicBool, AtomicU32, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
}, num::Wrapping, ops::DerefMut,
|
||||
};
|
||||
|
||||
use ecu_diagnostics::{kwp2000::{Kwp2000DiagnosticServer, SessionType, ResetMode}, DiagServerResult, DiagnosticServer, DiagError};
|
||||
use egui::*;
|
||||
use epi::*;
|
||||
use nfd::Response;
|
||||
|
||||
use crate::{
|
||||
usb_hw::{diag_usb::Nag52USB, flasher},
|
||||
usb_hw::{diag_usb::Nag52USB, flasher::{self, bin::{Firmware, load_binary}}},
|
||||
window::{InterfacePage, PageAction},
|
||||
};
|
||||
|
||||
|
@ -37,10 +38,10 @@ impl Default for FlashDataFormatted {
|
|||
}
|
||||
|
||||
pub struct FwUpdateUI {
|
||||
usb: Arc<Mutex<Nag52USB>>,
|
||||
server: Arc<Mutex<Kwp2000DiagnosticServer>>,
|
||||
elf_path: Option<String>,
|
||||
flashing: Arc<AtomicBool>,
|
||||
info_data: FlashDataFormatted,
|
||||
firmware: Option<Firmware>,
|
||||
progress: Arc<AtomicU32>,
|
||||
//flash_err: Arc<Mutex<Option<std::result::Result<(), espflash_un52::Error>>>>,
|
||||
reconnect: Arc<AtomicBool>,
|
||||
|
@ -49,17 +50,40 @@ pub struct FwUpdateUI {
|
|||
pub struct FlasherMutate {}
|
||||
|
||||
impl FwUpdateUI {
|
||||
pub fn new(arc: Arc<Mutex<Nag52USB>>) -> Self {
|
||||
pub fn new(server:Arc<Mutex<Kwp2000DiagnosticServer>>) -> Self {
|
||||
Self {
|
||||
usb: arc,
|
||||
server,
|
||||
elf_path: None,
|
||||
flashing: Arc::new(AtomicBool::new(false)),
|
||||
info_data: FlashDataFormatted::default(),
|
||||
firmware: None,
|
||||
progress: Arc::new(AtomicU32::new(0)),
|
||||
//flash_err: Arc::new(Mutex::new(None)),
|
||||
reconnect: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
fn init_flash_mode(&self, server: &mut Kwp2000DiagnosticServer, flash_size: u32) -> DiagServerResult<u32> {
|
||||
server.set_diagnostic_session_mode(SessionType::Reprogramming)?;
|
||||
let mut req: Vec<u8> = vec![0x34, 0x00, 0x00, 0x00, 0x00];
|
||||
req.push((flash_size >> 16) as u8);
|
||||
req.push((flash_size >> 8 ) as u8);
|
||||
req.push((flash_size) as u8);
|
||||
let resp = server.send_byte_array_with_response(&req)?;
|
||||
let bs = (resp[1] as u16) << 8 | resp[2] as u16;
|
||||
Ok(bs as u32)
|
||||
}
|
||||
|
||||
fn on_flash_end(&self, server: &mut Kwp2000DiagnosticServer) -> DiagServerResult<()> {
|
||||
server.send_byte_array_with_response(&[0x37])?;
|
||||
let status = server.send_byte_array_with_response(&[0x31, 0xE1])?;
|
||||
if status[1] == 0x00 {
|
||||
eprintln!("ECU Flash check OK! Rebooting");
|
||||
return server.reset_ecu(ResetMode::PowerOnReset)
|
||||
} else {
|
||||
eprintln!("ECU Flash check failed :(");
|
||||
return Err(DiagError::NotSupported)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InterfacePage for FwUpdateUI {
|
||||
|
@ -72,79 +96,62 @@ impl InterfacePage for FwUpdateUI {
|
|||
ui.label(
|
||||
RichText::new("Caution! Only use when car is off").color(Color32::from_rgb(255, 0, 0)),
|
||||
);
|
||||
if ui.button("Select ELF file").clicked() {
|
||||
match nfd::open_file_dialog(Some("elf"), None) {
|
||||
if ui.button("Select BIN firmware file").clicked() {
|
||||
match nfd::open_file_dialog(Some("bin"), None) {
|
||||
Ok(f) => {
|
||||
if let Response::Okay(path) = f {
|
||||
self.elf_path = Some(path);
|
||||
self.elf_path = Some(path.clone());
|
||||
match load_binary(path) {
|
||||
Ok(f) => self.firmware = Some(f),
|
||||
Err(e) => {
|
||||
eprintln!("E loading binary! {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
if let Some(path) = &self.elf_path {
|
||||
ui.label(RichText::new(format!("Firmware path: {}", path)));
|
||||
if let Some(firmware) = &self.firmware {
|
||||
ui.label(RichText::new(format!(
|
||||
"Firmware size: {} bytes
|
||||
|
||||
Version: {}
|
||||
IDF Version: {}
|
||||
Compile time: {} on {}
|
||||
",
|
||||
firmware.raw.len(),
|
||||
firmware.header.get_version(),
|
||||
firmware.header.get_idf_version(),
|
||||
firmware.header.get_time(),
|
||||
firmware.header.get_date()
|
||||
)));
|
||||
if !self.flashing.load(Ordering::Relaxed) {
|
||||
if ui.button("Flash firmware").clicked() {
|
||||
let elf = flasher::elf_file::EspElfFile::new(&self.elf_path.clone().unwrap());
|
||||
if let Err(e) = elf {
|
||||
println!("ELF ERROR {}", e);
|
||||
}
|
||||
let port = self.usb.lock().unwrap().on_flash_begin();
|
||||
let mut loader = flasher::esp_loader::EspLoader::new(port);
|
||||
loader.connect(10);
|
||||
/*
|
||||
match espflash_un52::Flasher::connect(port, info, Some(115200)) {
|
||||
Ok(mut flasher) => {
|
||||
let mut tmp = Vec::new();
|
||||
let mut f = File::open(self.elf_path.clone().unwrap()).unwrap();
|
||||
f.read_to_end(&mut tmp).unwrap();
|
||||
// Flasher thread
|
||||
|
||||
let flashing_t = self.flashing.clone();
|
||||
let flashing_err_t = self.flash_err.clone();
|
||||
let reconnect_t = self.reconnect.clone();
|
||||
match espflash_un52::FirmwareImage::from_data(&tmp) {
|
||||
Err(e) => *self.flash_err.lock().unwrap() = Some(Err(e)),
|
||||
Ok(f) => {
|
||||
std::thread::spawn(move || {
|
||||
flashing_t.store(true, Ordering::Relaxed);
|
||||
*flashing_err_t.lock().unwrap() =
|
||||
Some(flasher.load_elf_to_flash(&tmp, None, None));
|
||||
flashing_t.store(false, Ordering::Relaxed);
|
||||
reconnect_t.store(true, Ordering::Relaxed);
|
||||
//std::mem::drop(flasher);
|
||||
});
|
||||
let mut lock = self.server.lock().unwrap();
|
||||
match self.init_flash_mode(&mut lock.deref_mut(), firmware.raw.len() as u32) {
|
||||
Err(e) => eprintln!("ECU flash mode reject! {}", e),
|
||||
Ok(size) => {
|
||||
// Start the flasher!
|
||||
let arr = firmware.raw.chunks(size as usize);
|
||||
let mut failure = false;
|
||||
for (block_id, block) in arr.enumerate() {
|
||||
let mut req = vec![0x36, ((block_id+1) & 0xFF) as u8]; // [Transfer data request, block counter]
|
||||
req.extend_from_slice(block); // Block to transfer
|
||||
if let Err(e) = lock.send_byte_array_with_response(&req) {
|
||||
eprintln!("Writing failed! Error {}", e);
|
||||
failure = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !failure {
|
||||
if let Err(e) = self.on_flash_end(&mut lock) {
|
||||
eprintln!("ECU flash check error {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Cannot create ESP flasher: {}", e);
|
||||
*self.flash_err.lock().unwrap() = Some(Err(e))
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
/*
|
||||
if let Some(res) = self.flash_err.lock().unwrap().as_ref() {
|
||||
match res {
|
||||
Ok(_) => ui.label(
|
||||
RichText::new("Flashing completed OK!")
|
||||
.color(Color32::from_rgb(0, 255, 0)),
|
||||
),
|
||||
Err(e) => ui.label(
|
||||
RichText::new(format!("Flashing failed! Error: {}", e))
|
||||
.color(Color32::from_rgb(255, 0, 0)),
|
||||
),
|
||||
};
|
||||
if self.reconnect.load(Ordering::Relaxed) {
|
||||
if self.usb.lock().unwrap().on_flash_end().is_ok() {
|
||||
println!("OK!")
|
||||
}
|
||||
self.reconnect.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
ui.label("Flashing in progress...");
|
||||
ui.label("DO NOT EXIT THE APP");
|
||||
|
|
|
@ -22,14 +22,43 @@ pub struct MainPage {
|
|||
dev: Arc<Mutex<Nag52USB>>,
|
||||
bar: MainStatusBar,
|
||||
show_about_ui: bool,
|
||||
diag_server: Arc<Mutex<Kwp2000DiagnosticServer>>
|
||||
}
|
||||
|
||||
impl MainPage {
|
||||
pub fn new(dev: Arc<Mutex<Nag52USB>>) -> Self {
|
||||
|
||||
let mut channel = Hardware::create_iso_tp_channel(dev.clone()).unwrap();
|
||||
let channel_cfg = ecu_diagnostics::channel::IsoTPSettings {
|
||||
block_size: 8,
|
||||
st_min: 20,
|
||||
extended_addressing: false,
|
||||
pad_frame: true,
|
||||
can_speed: 500_000,
|
||||
can_use_ext_addr: false,
|
||||
};
|
||||
let server_settings = Kwp2000ServerOptions {
|
||||
send_id: 0x07E1,
|
||||
recv_id: 0x07E9,
|
||||
read_timeout_ms: 2000,
|
||||
write_timeout_ms: 2000,
|
||||
global_tp_id: 0,
|
||||
tester_present_interval_ms: 2000,
|
||||
tester_present_require_response: true,
|
||||
global_session_control: false
|
||||
};
|
||||
let mut kwp = Kwp2000DiagnosticServer::new_over_iso_tp(
|
||||
server_settings,
|
||||
channel,
|
||||
channel_cfg,
|
||||
Kwp2000VoidHandler {},
|
||||
).unwrap();
|
||||
|
||||
Self {
|
||||
dev: dev.clone(),
|
||||
bar: MainStatusBar::new(dev),
|
||||
show_about_ui: false,
|
||||
diag_server: Arc::new(Mutex::new(kwp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,15 +86,15 @@ impl InterfacePage for MainPage {
|
|||
ui.vertical(|v| {
|
||||
v.heading("Utilities");
|
||||
if v.button("Firmware updater").on_disabled_hover_ui(|u| {u.label("Broken, will be added soon!");}).clicked() {
|
||||
create_page = Some(PageAction::Add(Box::new(FwUpdateUI::new(self.dev.clone()))));
|
||||
create_page = Some(PageAction::Add(Box::new(FwUpdateUI::new(self.diag_server.clone()))));
|
||||
}
|
||||
if v.button("Diagnostics").clicked() {
|
||||
create_page = Some(PageAction::Add(Box::new(DiagnosticsPage::new(self.dev.clone(), self.bar.clone()))));
|
||||
create_page = Some(PageAction::Add(Box::new(DiagnosticsPage::new(self.diag_server.clone(), self.bar.clone()))));
|
||||
}
|
||||
if v.button("Map tuner").clicked() {}
|
||||
if v.button("Configure drive profiles").clicked() {}
|
||||
if v.button("Configure vehicle / gearbox").clicked() {
|
||||
create_page = Some(PageAction::Add(Box::new(ConfigPage::new(self.dev.clone(), self.bar.clone()))));
|
||||
create_page = Some(PageAction::Add(Box::new(ConfigPage::new(self.diag_server.clone(), self.bar.clone()))));
|
||||
}
|
||||
});
|
||||
if let Some(page) = create_page {
|
||||
|
|
|
@ -120,6 +120,7 @@ impl Nag52USB {
|
|||
tag: split.0.split_once(")").unwrap().1.to_string(),
|
||||
msg: split.1.to_string(),
|
||||
};
|
||||
println!("{:?}", msg);
|
||||
read_tx_log.send(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
use std::{fs::File, io::Read};
|
||||
|
||||
|
||||
|
||||
#[repr(C, packed)]
|
||||
// Derrived from https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L97
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FirmwareHeader {
|
||||
magic_word: u32, // 0xABCD5432
|
||||
secure_version: u32,
|
||||
reserved1: [u32; 2],
|
||||
version: [u8; 32],
|
||||
project_name: [u8; 32],
|
||||
time: [u8; 16],
|
||||
date: [u8; 16],
|
||||
idf_ver: [u8; 32],
|
||||
app_elf_sha: [u8; 32],
|
||||
reserv2: [u32; 20]
|
||||
}
|
||||
|
||||
impl FirmwareHeader {
|
||||
pub fn get_version(&self) -> String {
|
||||
String::from_utf8_lossy(&self.version).to_string()
|
||||
}
|
||||
pub fn get_idf_version(&self) -> String {
|
||||
String::from_utf8_lossy(&self.idf_ver).to_string()
|
||||
}
|
||||
pub fn get_date(&self) -> String {
|
||||
String::from_utf8_lossy(&self.date).to_string()
|
||||
}
|
||||
pub fn get_time(&self) -> String {
|
||||
String::from_utf8_lossy(&self.time).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Firmware {
|
||||
pub raw: Vec<u8>,
|
||||
pub header: FirmwareHeader
|
||||
}
|
||||
|
||||
const HEADER_MAGIC: [u8; 4] = [0x32, 0x54, 0xCD, 0xAB];
|
||||
const HEADER_SIZE: usize = 256;
|
||||
assert_eq_size!([u8; HEADER_SIZE], FirmwareHeader);
|
||||
|
||||
pub enum FirmwareLoadError {
|
||||
InvalidHeader,
|
||||
NotValid(String), // Reason
|
||||
IoError(std::io::Error)
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for FirmwareLoadError {
|
||||
fn from(f: std::io::Error) -> Self {
|
||||
Self::IoError(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FirmwareLoadError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FirmwareLoadError::InvalidHeader => write!(f, "Invalid firmware header"),
|
||||
FirmwareLoadError::NotValid(r) => write!(f, "Invalid firwmare: {}", r),
|
||||
FirmwareLoadError::IoError(io) => write!(f, "IO Error {}", io),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type FirwmareLoadResult<T> = std::result::Result<T, FirmwareLoadError>;
|
||||
|
||||
|
||||
|
||||
pub fn load_binary(path: String) -> FirwmareLoadResult<Firmware> {
|
||||
let mut f = File::open(path)?;
|
||||
let mut buf = Vec::new();
|
||||
f.read_to_end(&mut buf)?;
|
||||
// Todo find a nicer way to do this!
|
||||
let mut header_start_idx = 0;
|
||||
loop {
|
||||
let tmp = &buf[header_start_idx..];
|
||||
if tmp.len() < HEADER_MAGIC.len() || header_start_idx > 50 {
|
||||
return Err(FirmwareLoadError::NotValid("Could not find header magic".into()))
|
||||
}
|
||||
if tmp[..HEADER_MAGIC.len()] == HEADER_MAGIC {
|
||||
break; // Found!
|
||||
}
|
||||
header_start_idx+=1;
|
||||
}
|
||||
|
||||
if buf[header_start_idx..].len() < HEADER_SIZE {
|
||||
return Err(FirmwareLoadError::NotValid("Could not find header magic".into()))
|
||||
}
|
||||
// Ok, read the header
|
||||
let header: FirmwareHeader = unsafe { std::ptr::read(buf[header_start_idx..].as_ptr() as *const _ ) };
|
||||
println!("BUF {:02X?}", &buf[0..24]);
|
||||
Ok(
|
||||
Firmware {
|
||||
raw: buf,
|
||||
header,
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,302 +0,0 @@
|
|||
use std::{path::PathBuf, fmt::Write, io::{Read, BufRead}, u8};
|
||||
|
||||
pub type ElfResult<T> = std::result::Result<T, ElfError>;
|
||||
|
||||
pub const LEN_FILE_HEADER: usize = 0x34;
|
||||
pub const SEC_TYPE_PROG_BITS: u8 = 0x01;
|
||||
pub const SEC_TYPE_STR_TAB: u8 = 0x03;
|
||||
pub const SEC_TYPE_INIT_ARRAY: u8 = 0x0E;
|
||||
pub const SEC_TYPE_FINI_ARRAY: u8 = 0x0F;
|
||||
|
||||
pub const PROG_SEC_TYPES: [u8; 3] = [SEC_TYPE_PROG_BITS, SEC_TYPE_INIT_ARRAY, SEC_TYPE_FINI_ARRAY];
|
||||
pub const SEG_TYPE_LOAD: u8 = 0x01;
|
||||
pub const LEN_SEG_HEADER: u64 = 0x20;
|
||||
pub const LEN_SEC_HEADER: u64 = 0x28;
|
||||
|
||||
|
||||
pub enum ElfError {
|
||||
InvalidMagicHeader,
|
||||
NonXtensaElfFile(u32),
|
||||
UnexpectedHeaderEntrySize(usize),
|
||||
NoHeader,
|
||||
InvalidReadSize(u64, u64),
|
||||
NoStrTab(u64),
|
||||
IoError(std::io::Error)
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ElfError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::InvalidMagicHeader => write!(f, "InvalidMagicHeader"),
|
||||
Self::NonXtensaElfFile(eMachine) => write!(f, "NonXtensaElfFile(0x{:04X})", eMachine),
|
||||
Self::UnexpectedHeaderEntrySize(size) => write!(f, "UnexpectedHeaderEntrySize({})", size),
|
||||
Self::NoHeader => write!(f, "NoHeader"),
|
||||
Self::IoError(arg0) => f.debug_tuple("IoError").field(arg0).finish(),
|
||||
ElfError::InvalidReadSize(want, read) => write!(f, "InvalidReadSize({},{})", want, read),
|
||||
Self::NoStrTab(offset) => write!(f, "NoStrTab({})", offset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ElfError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::IoError(e) => Some(e),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&dyn std::error::Error> {
|
||||
self.source()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ElfError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ElfError::InvalidMagicHeader => write!(f, "Invalid EFL magic header"),
|
||||
ElfError::NonXtensaElfFile(eMachine) => write!(f, "ELF is not a Xtensa ELF file. E_MACHINE: 0x{:04X}", eMachine),
|
||||
ElfError::UnexpectedHeaderEntrySize(size) => write!(f, "ELF header is invalid size 0x{:04X} (not 0x{:04X})", size, LEN_SEC_HEADER),
|
||||
ElfError::NoHeader => write!(f, "ELF has 0 section headers"),
|
||||
ElfError::IoError(e) => write!(f, "IO Error {}", e),
|
||||
ElfError::InvalidReadSize(want, read) => write!(f, "Could not read {} bytes from ELF. Got {}", want, read),
|
||||
ElfError::NoStrTab(offset) => write!(f, "No STRTAB section at offset 0x{:04X}", offset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ElfError {
|
||||
fn from(e: std::io::Error) -> Self {
|
||||
ElfError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ElfSectionHeader {
|
||||
pub (self) name_offset: u32,
|
||||
pub (self) sec_type: u32,
|
||||
pub (self) flags: u32,
|
||||
pub (self) lma: u32,
|
||||
pub (self) sec_offsets: u32,
|
||||
pub (self) size: u32
|
||||
}
|
||||
|
||||
impl ElfSectionHeader {
|
||||
pub (crate) fn from_bytes(b: &[u8]) -> Self {
|
||||
Self {
|
||||
name_offset: u32::from_le_bytes(b[0..4].try_into().unwrap()),
|
||||
sec_type: u32::from_le_bytes(b[4..8].try_into().unwrap()),
|
||||
flags: u32::from_le_bytes(b[8..12].try_into().unwrap()),
|
||||
lma: u32::from_le_bytes(b[12..16].try_into().unwrap()),
|
||||
sec_offsets: u32::from_le_bytes(b[16..20].try_into().unwrap()),
|
||||
size: u32::from_le_bytes(b[20..24].try_into().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ElfSegmentHeader {
|
||||
pub (self) seg_type: u32,
|
||||
pub (self) seg_offset: u32,
|
||||
pub (self) va_addr: u32,
|
||||
pub (self) lma: u32,
|
||||
pub (self) size: u32,
|
||||
pub (self) mem_size: u32,
|
||||
pub (self) flags: u32,
|
||||
pub (self) align: u32
|
||||
}
|
||||
|
||||
impl ElfSegmentHeader {
|
||||
pub (crate) fn from_bytes(b: &[u8]) -> Self {
|
||||
Self {
|
||||
seg_type: u32::from_le_bytes(b[0..4].try_into().unwrap()),
|
||||
seg_offset: u32::from_le_bytes(b[4..8].try_into().unwrap()),
|
||||
va_addr: u32::from_le_bytes(b[8..12].try_into().unwrap()),
|
||||
lma: u32::from_le_bytes(b[12..16].try_into().unwrap()),
|
||||
size: u32::from_le_bytes(b[16..20].try_into().unwrap()),
|
||||
mem_size: u32::from_le_bytes(b[20..24].try_into().unwrap()),
|
||||
flags: u32::from_le_bytes(b[24..28].try_into().unwrap()),
|
||||
align: u32::from_le_bytes(b[28..32].try_into().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ElfSection {
|
||||
pub name: String,
|
||||
pub addr: u64,
|
||||
pub data: Vec<u8>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ElfHeader {
|
||||
pub (self) ident: [u8; 16],
|
||||
pub (self) elf_type: u16,
|
||||
pub (self) machine: u16,
|
||||
pub (self) version: u32,
|
||||
pub (self) entry_point: u32,
|
||||
pub (self) phoff: u32,
|
||||
pub (self) shoff: u32,
|
||||
pub (self) flags: u32,
|
||||
pub (self) ehsize: u16,
|
||||
pub (self) phentsize: u16,
|
||||
pub (self) phnum: u16,
|
||||
pub (self) shentsize: u16,
|
||||
pub (self) shnum: u16,
|
||||
pub (self) shstrndx: u16
|
||||
}
|
||||
|
||||
impl ElfHeader {
|
||||
pub (crate) fn from_bytes(b: &[u8]) -> Self {
|
||||
Self {
|
||||
ident: b[0..16].try_into().unwrap(),
|
||||
elf_type: u16::from_le_bytes(b[16..18].try_into().unwrap()),
|
||||
machine: u16::from_le_bytes(b[18..20].try_into().unwrap()),
|
||||
version: u32::from_le_bytes(b[20..24].try_into().unwrap()),
|
||||
entry_point: u32::from_le_bytes(b[24..28].try_into().unwrap()),
|
||||
phoff: u32::from_le_bytes(b[28..32].try_into().unwrap()),
|
||||
shoff: u32::from_le_bytes(b[32..36].try_into().unwrap()),
|
||||
flags: u32::from_le_bytes(b[36..40].try_into().unwrap()),
|
||||
ehsize: u16::from_le_bytes(b[40..42].try_into().unwrap()),
|
||||
phentsize: u16::from_le_bytes(b[42..44].try_into().unwrap()),
|
||||
phnum: u16::from_le_bytes(b[44..46].try_into().unwrap()),
|
||||
shentsize: u16::from_le_bytes(b[46..48].try_into().unwrap()),
|
||||
shnum: u16::from_le_bytes(b[48..50].try_into().unwrap()),
|
||||
shstrndx: u16::from_le_bytes(b[50..52].try_into().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EspElfFile {
|
||||
header: ElfHeader,
|
||||
sections: Vec<ElfSection>,
|
||||
segments: Vec<ElfSection>
|
||||
}
|
||||
|
||||
|
||||
impl EspElfFile {
|
||||
pub fn new(path: &str) -> ElfResult<Self> { // esptool.py ElfFile._read_elf_file()
|
||||
let mut f = std::fs::File::open(path)?;
|
||||
let mut elf_bytes: Vec<u8> = Vec::new();
|
||||
let _ = f.read_to_end(&mut elf_bytes)?;
|
||||
let header = ElfHeader::from_bytes(&elf_bytes[0..LEN_FILE_HEADER]);
|
||||
println!("{:02X?}", header);
|
||||
if header.ident[0] != 0x7F || String::from_utf8_lossy(&header.ident[1..4]) != "ELF" {
|
||||
return Err(ElfError::InvalidMagicHeader)
|
||||
} else if header.machine != 0x5E && header.machine != 0xF3 {
|
||||
return Err(ElfError::NonXtensaElfFile(header.machine as u32))
|
||||
} else if header.shentsize as u64 != LEN_SEC_HEADER {
|
||||
return Err(ElfError::UnexpectedHeaderEntrySize(header.shentsize as usize))
|
||||
} else if header.shnum == 0 {
|
||||
return Err(ElfError::NoHeader)
|
||||
}
|
||||
let mut cursor = std::io::Cursor::new(elf_bytes);
|
||||
|
||||
let sections = Self::read_sections(&mut cursor, header.shoff as u64, header.shnum as u64, header.shstrndx as u64)?;
|
||||
let segments = Self::read_segments(&mut cursor, header.phoff as u64, header.phnum as u64, header.shstrndx as u64)?;
|
||||
for sec in §ions {
|
||||
println!("Elf section. name {}, address {:04X}, size: {}", sec.name, sec.addr, sec.data.len());
|
||||
}
|
||||
|
||||
for sec in &segments {
|
||||
println!("ELF segment. name {}, address {:04X}, size: {}", sec.name, sec.addr, sec.data.len());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
header,
|
||||
sections,
|
||||
segments,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_sections(cursor: &mut std::io::Cursor<Vec<u8>>, header_offset: u64, header_count: u64, shstrndx: u64) -> ElfResult<Vec<ElfSection>> {
|
||||
cursor.set_position(header_offset);
|
||||
let byte_count = LEN_SEC_HEADER * header_count;
|
||||
let mut section_header: Vec<u8> = vec![0; byte_count as usize];
|
||||
if cursor.read(&mut section_header)? != byte_count as usize {
|
||||
return Err(ElfError::InvalidReadSize(byte_count, section_header.len() as u64))
|
||||
}
|
||||
|
||||
let section_header_offsets: Vec<u64> = (0..section_header.len() as u64).step_by(LEN_SEC_HEADER as usize).collect();
|
||||
|
||||
fn read_section_header(glob_header: &[u8], offset: u64) -> ElfSectionHeader {
|
||||
ElfSectionHeader::from_bytes(&glob_header[offset as usize..])
|
||||
}
|
||||
|
||||
let all_sections: Vec<ElfSectionHeader> = section_header_offsets.iter().map(|offset| read_section_header(§ion_header, *offset) ).collect();
|
||||
let prog_sections: Vec<ElfSectionHeader> = all_sections.iter().filter(|s| PROG_SEC_TYPES.contains(&(s.sec_type as u8)) ).cloned().collect();
|
||||
|
||||
if !section_header_offsets.contains(&(shstrndx * LEN_SEC_HEADER)) {
|
||||
return Err(ElfError::NoStrTab(shstrndx * LEN_SEC_HEADER))
|
||||
}
|
||||
|
||||
let str_tab_header = read_section_header(§ion_header, shstrndx * LEN_SEC_HEADER);
|
||||
|
||||
cursor.set_position(str_tab_header.sec_offsets as u64);
|
||||
let mut string_table: Vec<u8> = vec![0; str_tab_header.size as usize];
|
||||
let _ = cursor.read_exact(&mut string_table)?;
|
||||
|
||||
|
||||
fn lookup_string(str_tbl: &[u8], offset: u64) -> String {
|
||||
let raw = &str_tbl[offset as usize..];
|
||||
let end = raw.iter().position(|x| *x == 0x00).unwrap();
|
||||
String::from_utf8(raw[..end].to_vec()).unwrap()
|
||||
}
|
||||
|
||||
fn read_data(c: &mut std::io::Cursor<Vec<u8>>, offset: u64, size: usize) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = vec![0; size];
|
||||
c.set_position(offset);
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
let result_sections: Vec<ElfSection> = prog_sections.iter()
|
||||
.filter(|sec| sec.lma != 0 && sec.size > 0)
|
||||
.map(|sec| {
|
||||
ElfSection {
|
||||
name: lookup_string(&string_table, sec.name_offset as u64),
|
||||
addr: sec.lma as u64,
|
||||
data: read_data(cursor, sec.sec_offsets as u64, sec.size as usize),
|
||||
}
|
||||
}).collect();
|
||||
|
||||
Ok(result_sections)
|
||||
}
|
||||
|
||||
fn read_segments(cursor: &mut std::io::Cursor<Vec<u8>>, seg_header_offset: u64, seg_header_count: u64, shstrndx: u64) -> ElfResult<Vec<ElfSection>> {
|
||||
cursor.set_position(seg_header_offset);
|
||||
let byte_count = LEN_SEG_HEADER * seg_header_count;
|
||||
let mut segment_header: Vec<u8> = vec![0; byte_count as usize];
|
||||
if cursor.read(&mut segment_header)? != byte_count as usize {
|
||||
return Err(ElfError::InvalidReadSize(byte_count, segment_header.len() as u64))
|
||||
}
|
||||
|
||||
let segment_header_offsets: Vec<u64> = (0..segment_header.len() as u64).step_by(LEN_SEG_HEADER as usize).collect();
|
||||
|
||||
fn read_section_header(glob_header: &[u8], offset: u64) -> ElfSegmentHeader {
|
||||
ElfSegmentHeader::from_bytes(&glob_header[offset as usize..])
|
||||
}
|
||||
|
||||
let all_segments: Vec<ElfSegmentHeader> = segment_header_offsets.iter().map(|offset| read_section_header(&segment_header, *offset) ).collect();
|
||||
let prog_segments: Vec<ElfSegmentHeader> = all_segments.iter().filter(|s| s.seg_type == SEG_TYPE_LOAD as u32).cloned().collect();
|
||||
|
||||
fn read_data(c: &mut std::io::Cursor<Vec<u8>>, offset: u64, size: usize) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = vec![0; size];
|
||||
c.set_position(offset);
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
let result_sections: Vec<ElfSection> = prog_segments.iter()
|
||||
.filter(|seg| seg.lma != 0 && seg.size > 0)
|
||||
.map(|seg| {
|
||||
ElfSection {
|
||||
name: "PHDR".to_string(),
|
||||
addr: seg.lma as u64,
|
||||
data: read_data(cursor, seg.seg_offset as u64, seg.size as usize),
|
||||
}
|
||||
}).collect();
|
||||
|
||||
Ok(result_sections)
|
||||
}
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
use serial_rs::{SerialPort, FlowControl};
|
||||
|
||||
use super::slip_reader::SlipReader;
|
||||
|
||||
|
||||
|
||||
pub const CHIP_NAME: &str = "Espressif device";
|
||||
pub const IS_STUB: bool = false;
|
||||
|
||||
// Commands
|
||||
pub const ESP_SPI_SET_PARAMS: u8 = 0x0B;
|
||||
pub const ESP_SPI_ATTACH: u8 = 0x0D;
|
||||
pub const ESP_SYNC: u8 = 0x08;
|
||||
pub const ROM_INVALID_RECV_MSG: u8 = 0x05;
|
||||
|
||||
pub const DEFAULT_TIMEOUT: u64 = 3000;
|
||||
|
||||
pub struct EspLoader {
|
||||
port: Box<dyn SerialPort>,
|
||||
slip_reader: Option<SlipReader>,
|
||||
sync_stub_detected: bool,
|
||||
dtr: bool,
|
||||
}
|
||||
|
||||
impl EspLoader {
|
||||
pub fn new(mut port: Box<dyn SerialPort>) -> Self {
|
||||
// Before we init Slip reader, set baud
|
||||
let reader = SlipReader::new(port.try_clone().unwrap());
|
||||
Self {
|
||||
port,
|
||||
slip_reader: Some(reader),
|
||||
sync_stub_detected: false,
|
||||
dtr: false
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_input(&mut self) {
|
||||
self.port.clear_input_buffer();
|
||||
let _ = self.slip_reader.replace(SlipReader::new(self.port.try_clone().unwrap()));
|
||||
}
|
||||
|
||||
fn reset_to_bootloader(&mut self, extra_delay: bool) {
|
||||
println!("Going to bootloader {}", extra_delay);
|
||||
self.port.set_data_terminal_ready(false);
|
||||
self.port.set_request_to_send(true);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
self.port.set_data_terminal_ready(true);
|
||||
self.port.set_request_to_send(false);
|
||||
std::thread::sleep(std::time::Duration::from_millis(if extra_delay { 500 } else {50}));
|
||||
self.port.set_data_terminal_ready(false);
|
||||
}
|
||||
|
||||
pub fn connect(&mut self, attempts: u32) -> bool {
|
||||
let mut use_extra_delay = false;
|
||||
for attempt_idx in 0..attempts {
|
||||
if self.connect_attempt(use_extra_delay) {
|
||||
return true;
|
||||
}
|
||||
use_extra_delay = !use_extra_delay;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn connect_attempt(&mut self, extra_delay: bool) -> bool {
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
self.port.clear_input_buffer();
|
||||
self.port.clear_output_buffer();
|
||||
let _ = self.slip_reader.take(); // Don't need SLIP reader for this!
|
||||
self.reset_to_bootloader(extra_delay);
|
||||
let waiting = self.port.bytes_to_read().unwrap() as usize;
|
||||
let mut buffer: Vec<u8> = vec![0; waiting];
|
||||
self.port.read_exact(&mut buffer);
|
||||
let buffer_as_string = String::from_utf8_lossy(&buffer);
|
||||
println!("Buffer {}", buffer_as_string);
|
||||
if !buffer_as_string.to_string().contains("waiting for download") {
|
||||
println!("ESP not in download mode!");
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||
return false;
|
||||
}
|
||||
for x in 0..5 {
|
||||
self.flush_input();
|
||||
self.port.clear_input_buffer();
|
||||
self.port.clear_output_buffer();
|
||||
if self.sync() {
|
||||
return true;
|
||||
} else {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn sync(&mut self) -> bool {
|
||||
println!("Trying sync");
|
||||
let mut buffer: Vec<u8> = vec![0x55; 32+4];
|
||||
buffer[0..4].copy_from_slice(&[0x07, 0x07, 0x12, 0x20]);
|
||||
if let Some((val, data)) = self.command(
|
||||
Some(ESP_SYNC),
|
||||
&buffer,
|
||||
0,
|
||||
true,
|
||||
100
|
||||
).unwrap_or_default() {
|
||||
println!("Command response OK! {:02X?}", data);
|
||||
self.sync_stub_detected = val == 0;
|
||||
for _ in 0..7 {
|
||||
match self.command(None, &[], 0, true, DEFAULT_TIMEOUT).unwrap_or_default() {
|
||||
Some((v, _)) => {
|
||||
self.sync_stub_detected &= v == 0
|
||||
},
|
||||
None => {return false;}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn read(&mut self) -> Option<Vec<u8>> {
|
||||
if let Some(reader) = self.slip_reader.as_mut() {
|
||||
return reader.queue.try_recv().ok()
|
||||
} else {
|
||||
eprintln!("Trying to read without SLIP reader!?");
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn command(&mut self, op: Option<u8>, data: &[u8], chk: u32, wait_response: bool, timeout: u64) -> std::io::Result<Option<(u32, Vec<u8>)>> {
|
||||
if let Some(pid) = op {
|
||||
self.port.write_all(&((data.len() as u16).to_le_bytes()))?;
|
||||
self.port.write_all(&(chk).to_le_bytes())?;
|
||||
self.port.write_all(data)?;
|
||||
}
|
||||
if wait_response == false {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
for retry in 0..100 {
|
||||
if let Some(read) = self.read() {
|
||||
println!("Read {:02X?}", read);
|
||||
if read.len() < 8 {
|
||||
continue;
|
||||
}
|
||||
let resp = read[0];
|
||||
let op_ret = read[1];
|
||||
let len_ret = u16::from_le_bytes(read[2..4].try_into().unwrap());
|
||||
let val = u32::from_le_bytes(read[4..8].try_into().unwrap());
|
||||
if resp != 1 {
|
||||
continue;
|
||||
}
|
||||
let read_data = &read[8..];
|
||||
if op.is_none() || op.unwrap() == op_ret {
|
||||
return Ok(Some((val, read_data.to_vec())))
|
||||
}
|
||||
if data[0] != 0 && data[1] == ROM_INVALID_RECV_MSG {
|
||||
self.flush_input();
|
||||
return Ok(None);
|
||||
}
|
||||
} else {
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
||||
fn set_dtr(&mut self, state: bool) {
|
||||
self.port.set_data_terminal_ready(state);
|
||||
self.dtr = state;
|
||||
}
|
||||
|
||||
fn set_rts(&mut self, state: bool) {
|
||||
self.port.set_request_to_send(state);
|
||||
self.port.set_data_terminal_ready(self.dtr);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
/// ESP Flashing utility in order
|
||||
/// to flash new firmware to the TCM
|
||||
///
|
||||
/// This modules content is based on https://github.com/espressif/esptool/blob/master/esptool.py
|
||||
//! ESP Flashing utility in order
|
||||
//! to flash new firmware to the TCM
|
||||
//!
|
||||
//! This modules content is based on https://github.com/espressif/esptool/blob/master/esptool.py
|
||||
|
||||
pub mod elf_file;
|
||||
pub mod esp_loader;
|
||||
pub mod slip_reader;
|
||||
pub mod bin;
|
|
@ -1,94 +0,0 @@
|
|||
use std::{sync::{mpsc, atomic::{AtomicBool, Ordering}, Arc}, io::{BufReader, Read}, thread::JoinHandle};
|
||||
|
||||
use serial_rs::SerialPort;
|
||||
|
||||
|
||||
|
||||
pub struct SlipReader {
|
||||
pub queue: mpsc::Receiver<Vec<u8>>,
|
||||
stop_err: Option<String>,
|
||||
running: Arc<AtomicBool>,
|
||||
handle: Option<JoinHandle<()>>
|
||||
}
|
||||
|
||||
impl SlipReader {
|
||||
pub fn new(mut port: Box<dyn SerialPort>) -> Self {
|
||||
let (sender, receiver) = mpsc::channel::<Vec<u8>>();
|
||||
|
||||
let is_running = Arc::new(AtomicBool::new(true));
|
||||
let is_running_t = is_running.clone();
|
||||
|
||||
let handle = std::thread::spawn(move|| {
|
||||
let mut partial_packet: Option<Vec<u8>> = None;
|
||||
let mut in_escape: bool = false;
|
||||
let mut successful_slip: bool = false;
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
println!("SLIP reader started");
|
||||
while is_running_t.load(Ordering::Relaxed) {
|
||||
let mut waiting = port.bytes_to_read().unwrap() as usize;
|
||||
if waiting != 0 {
|
||||
println!("Port has {} bytes to read", waiting);
|
||||
buffer = vec![0; waiting];
|
||||
if let Err(e) = port.read_exact(&mut buffer) {
|
||||
// Could not read the buffer!?
|
||||
eprintln!("SLIP error reading buffer: {}", e);
|
||||
return;
|
||||
}
|
||||
for b in &buffer {
|
||||
let mut delete_packet = false;
|
||||
match partial_packet.as_mut() {
|
||||
None => {
|
||||
if *b == 0xC0 {
|
||||
partial_packet = Some(vec![0xC0]);
|
||||
} else {
|
||||
eprintln!("SLIP error reading invalid data. Buffer: {:02X?}", buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Some(p_packet) => {
|
||||
if in_escape {
|
||||
in_escape = false;
|
||||
if *b == 0xDC {
|
||||
p_packet.push(0xC0)
|
||||
} else if *b == 0xDD {
|
||||
p_packet.push(0xDB)
|
||||
} else {
|
||||
eprintln!("SLIP error reading invalid escape. Buffer: {:02X?}", buffer);
|
||||
return;
|
||||
}
|
||||
} else if *b == 0xDB {
|
||||
in_escape = true
|
||||
} else if *b == 0xC0 {
|
||||
sender.send(p_packet.clone());
|
||||
delete_packet = true;
|
||||
successful_slip = true;
|
||||
} else {
|
||||
p_packet.push(*b);
|
||||
}
|
||||
}
|
||||
}
|
||||
if delete_packet {
|
||||
partial_packet = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
println!("SLIP reader stop");
|
||||
});
|
||||
Self {
|
||||
queue: receiver,
|
||||
stop_err: None,
|
||||
handle: Some(handle),
|
||||
running: is_running
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SlipReader {
|
||||
fn drop(&mut self) {
|
||||
self.running.store(false, Ordering::Relaxed);
|
||||
self.handle.take().unwrap().join();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue