Initial KWP flashing

This commit is contained in:
Ashcon Mohseninia 2022-05-02 10:00:30 +01:00
parent 0b0280d553
commit bca8965924
11 changed files with 228 additions and 718 deletions

View File

@ -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;

View File

@ -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);
};
}
}

View File

@ -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)
}
}

View File

@ -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");

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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,
}
)
}

View File

@ -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 &sections {
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(&section_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(&section_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)
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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();
}
}