diff --git a/Cargo.lock b/Cargo.lock index 50aeb82..c04cdb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,8 +91,10 @@ dependencies = [ "deku", "directories-next", "env_logger", + "futures", "hex", "indicatif", + "js-sys", "log", "main_error", "parse_int", @@ -103,6 +105,8 @@ dependencies = [ "structopt", "thiserror", "toml 0.5.7", + "wasm-bindgen", + "web-sys", "xmas-elf", ] @@ -121,6 +125,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + [[package]] name = "byteorder" version = "1.3.4" @@ -135,6 +145,7 @@ dependencies = [ "cargo-project", "color-eyre", "env_logger", + "futures", "main_error", "paw", "serial", @@ -397,6 +408,101 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ba62103ce691c2fd80fbae2213dfdda9ce60804973ac6b6e97de818ea7f52c8" +[[package]] +name = "futures" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" + +[[package]] +name = "futures-executor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" + +[[package]] +name = "futures-macro" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" + +[[package]] +name = "futures-task" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + [[package]] name = "generator" version = "0.6.23" @@ -512,6 +618,15 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "js-sys" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -643,12 +758,38 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f0b59668fe80c5afe998f0c0bf93322bf2cd66cafeeb80581f291716f3467f2" +[[package]] +name = "pin-project" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a83804639aad6ba65345661744708855f9fbcb71176ea8d28d05aeb11d975e7" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7bcc46b8f73443d15bc1c5fecbb315718491fa9187fa483f0e359323cde8b3a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -673,6 +814,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" + [[package]] name = "proc-macro2" version = "1.0.24" @@ -878,6 +1031,12 @@ dependencies = [ "loom", ] +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + [[package]] name = "strsim" version = "0.8.0" @@ -1123,6 +1282,70 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasm-bindgen" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" + +[[package]] +name = "web-sys" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/blflash/Cargo.toml b/blflash/Cargo.toml index 85d7dcb..83220ce 100644 --- a/blflash/Cargo.toml +++ b/blflash/Cargo.toml @@ -33,3 +33,15 @@ paw = "1.0.0" crc = "1.8.1" hex = "0.4.2" parse_int = "0.4.0" +futures = "0.3" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2" +js-sys = "0.3" + +[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys] +version = "0.3" +features = [ + "Window", + "Navigator" +] \ No newline at end of file diff --git a/blflash/src/async_serial.rs b/blflash/src/async_serial.rs new file mode 100644 index 0000000..cdc231e --- /dev/null +++ b/blflash/src/async_serial.rs @@ -0,0 +1,8 @@ +mod native; +mod wasm; + +#[cfg(target_arch = "wasm32")] +pub use wasm::AsyncSerial; + +#[cfg(not(target_arch = "wasm32"))] +pub use native::AsyncSerial; diff --git a/blflash/src/async_serial/native.rs b/blflash/src/async_serial/native.rs new file mode 100644 index 0000000..2151a19 --- /dev/null +++ b/blflash/src/async_serial/native.rs @@ -0,0 +1,73 @@ +#![cfg(not(target_arch = "wasm32"))] + +use futures::io::{AsyncRead, AsyncWrite}; +use serial::{Result, SerialPort, SerialPortSettings}; +use std::{ + io, + pin::Pin, + task::{Context, Poll}, + thread::sleep, + time::Duration, +}; + +// Async wrapper for SerialPort +// Note: it's not really async. For native usage, async is not necessary. +pub struct AsyncSerial(Box); + +impl AsyncSerial { + pub async fn open(port: &str) -> crate::Result { + Ok(AsyncSerial(Box::new(serial::open(port)))) + } + // pub fn new(serial: impl SerialPort + 'static) -> Self { + // Self(Box::new(serial)) + // } + pub async fn set_rts(&mut self, level: bool) -> Result<()> { + self.0.set_rts(level) + } + pub async fn set_dtr(&mut self, level: bool) -> Result<()> { + self.0.set_dtr(level) + } + pub async fn sleep(&self, duration: Duration) { + sleep(duration) + } + pub async fn set_timeout(&mut self, timeout: Duration) -> Result<()> { + self.0.set_timeout(timeout) + } + pub async fn timeout(&self) -> Duration { + self.0.timeout() + } + pub async fn reconfigure( + &mut self, + setup: &dyn Fn(&mut dyn SerialPortSettings) -> Result<()>, + ) -> Result<()> { + self.0.reconfigure(setup) + } +} + +impl AsyncRead for AsyncSerial { + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Poll::Ready(self.0.read(buf)) + } +} + +impl AsyncWrite for AsyncSerial { + fn poll_write( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(self.0.write(buf)) + } + + fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(self.0.flush()) + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} diff --git a/blflash/src/async_serial/wasm.rs b/blflash/src/async_serial/wasm.rs new file mode 100644 index 0000000..fd28adf --- /dev/null +++ b/blflash/src/async_serial/wasm.rs @@ -0,0 +1,79 @@ +#![cfg(target_arch = "wasm32")] + +use crate::Error; +use futures::io::{AsyncRead, AsyncWrite}; +use js_sys::Reflect; +use serial::{Result, SerialPortSettings}; +use std::{ + io, + pin::Pin, + task::{Context, Poll}, + thread::sleep, + time::Duration, +}; +use wasm_bindgen::prelude::*; +use web_sys::window; + +pub struct AsyncSerial(JsValue); + +impl AsyncSerial { + pub async fn open(port: &str) -> crate::Result { + let window = window().ok_or(Error::WebError("Failed to get window"))?; + let serial = Reflect::get(window.navigator().as_ref(), &"serial".into()) + .map_err(|_| Error::WebError("Failed to get serial from navigator"))?; + if serial.is_undefined() { + return Err(Error::WebError("serial is not supported on your browser")); + } + + todo!() + } + pub async fn set_rts(&mut self, level: bool) -> Result<()> { + todo!() + } + pub async fn set_dtr(&mut self, level: bool) -> Result<()> { + todo!() + } + pub async fn sleep(&self, duration: Duration) { + sleep(duration) + } + pub async fn set_timeout(&mut self, timeout: Duration) -> Result<()> { + todo!() + } + pub async fn timeout(&self) -> Duration { + todo!() + } + pub async fn reconfigure( + &mut self, + setup: &dyn Fn(&mut dyn SerialPortSettings) -> Result<()>, + ) -> Result<()> { + todo!() + } +} + +impl AsyncRead for AsyncSerial { + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + todo!() + } +} + +impl AsyncWrite for AsyncSerial { + fn poll_write( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + todo!() + } + + fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + todo!() + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + todo!() + } +} diff --git a/blflash/src/connection.rs b/blflash/src/connection.rs index 3366d98..0513772 100644 --- a/blflash/src/connection.rs +++ b/blflash/src/connection.rs @@ -1,13 +1,16 @@ #![macro_use] -use crate::{Error, RomError}; +use crate::{ + async_serial::AsyncSerial, + {Error, RomError}, +}; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; use deku::prelude::*; -use std::io::{Cursor, Read, Write}; -use std::thread::sleep; +use futures::{AsyncReadExt, AsyncWriteExt}; +use std::io::{Cursor, Write}; use std::time::Duration; -use serial::{BaudRate, SerialPort, SerialPortSettings}; +use serial::{BaudRate, SerialPortSettings}; pub const DEFAULT_BAUDRATE: BaudRate = BaudRate::Baud115200; @@ -57,90 +60,83 @@ pub trait Command: DekuContainerWrite { } pub struct Connection { - serial: Box, + serial: AsyncSerial, baud_rate: Option, } impl Connection { - pub fn new(serial: impl SerialPort + 'static) -> Self { + pub fn new(serial: AsyncSerial) -> Self { Connection { - serial: Box::new(serial), + serial, baud_rate: None, } } - pub fn into_inner(self) -> Box { + pub fn into_inner(self) -> AsyncSerial { self.serial } - pub fn reset(&mut self) -> Result<(), Error> { - self.serial.set_rts(false)?; - sleep(Duration::from_millis(50)); - self.serial.set_dtr(true)?; - sleep(Duration::from_millis(50)); - self.serial.set_dtr(false)?; - sleep(Duration::from_millis(50)); + pub async fn reset(&mut self) -> Result<(), Error> { + self.serial.set_rts(false).await?; + self.serial.sleep(Duration::from_millis(50)).await; + self.serial.set_dtr(true).await?; + self.serial.sleep(Duration::from_millis(50)).await; + self.serial.set_dtr(false).await?; + self.serial.sleep(Duration::from_millis(50)).await; Ok(()) } - pub fn reset_to_flash(&mut self) -> Result<(), Error> { - self.serial.set_rts(true)?; - sleep(Duration::from_millis(50)); - self.serial.set_dtr(true)?; - sleep(Duration::from_millis(50)); - self.serial.set_dtr(false)?; - sleep(Duration::from_millis(50)); - self.serial.set_rts(false)?; - sleep(Duration::from_millis(50)); + pub async fn reset_to_flash(&mut self) -> Result<(), Error> { + self.serial.set_rts(true).await?; + self.serial.sleep(Duration::from_millis(50)).await; + self.serial.set_dtr(true).await?; + self.serial.sleep(Duration::from_millis(50)).await; + self.serial.set_dtr(false).await?; + self.serial.sleep(Duration::from_millis(50)).await; + self.serial.set_rts(false).await?; + self.serial.sleep(Duration::from_millis(50)).await; Ok(()) } - pub fn set_timeout(&mut self, timeout: Duration) -> Result<(), Error> { - self.serial.set_timeout(timeout)?; + pub async fn timeout(&self) -> Duration { + self.serial.timeout().await + } + + pub async fn set_timeout(&mut self, timeout: Duration) -> Result<(), Error> { + self.serial.set_timeout(timeout).await?; Ok(()) } - pub fn set_baud(&mut self, speed: BaudRate) -> Result<(), Error> { + pub async fn set_baud(&mut self, speed: BaudRate) -> Result<(), Error> { self.baud_rate = Some(speed); self.serial - .reconfigure(&|setup: &mut dyn SerialPortSettings| setup.set_baud_rate(speed))?; + .reconfigure(&|setup: &mut dyn SerialPortSettings| setup.set_baud_rate(speed)) + .await?; Ok(()) } - pub fn with_timeout Result>( - &mut self, - timeout: Duration, - mut f: F, - ) -> Result { - let old_timeout = self.serial.timeout(); - self.serial.set_timeout(timeout)?; - let result = f(self); - self.serial.set_timeout(old_timeout)?; - result - } - - fn read_exact(&mut self, len: usize) -> Result, Error> { + async fn read_exact(&mut self, len: usize) -> Result, Error> { let mut buf = vec![0u8; len]; - self.serial.read_exact(&mut buf)?; + self.serial.read_exact(&mut buf).await?; Ok(buf) } - pub fn read_response(&mut self, len: usize) -> Result, Error> { - let resp = self.read_exact(2)?; + pub async fn read_response(&mut self, len: usize) -> Result, Error> { + let resp = self.read_exact(2).await?; match &resp[0..2] { // OK [0x4f, 0x4b] => { if len > 0 { - self.read_exact(len) + self.read_exact(len).await } else { Ok(vec![]) } } // FL [0x46, 0x4c] => { - let code = self.read_exact(2)?; + let code = self.read_exact(2).await?; let mut reader = Cursor::new(code); let code = reader.read_u16::()?; Err(Error::RomError(RomError::from(code))) @@ -157,28 +153,28 @@ impl Connection { * (duration.as_millis() as usize) } - pub fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { - Ok(self.serial.write_all(buf)?) + pub async fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { + Ok(self.serial.write_all(buf).await?) } - pub fn flush(&mut self) -> Result<(), Error> { - Ok(self.serial.flush()?) + pub async fn flush(&mut self) -> Result<(), Error> { + Ok(self.serial.flush().await?) } - pub fn command(&mut self, command: C) -> Result { + pub async fn command(&mut self, command: C) -> Result { let req = self.to_cmd(command)?; - self.write_all(&req)?; - self.flush()?; + self.write_all(&req).await?; + self.flush().await?; Ok(if let Some(resp) = C::Response::no_response_payload() { - self.read_response(0)?; + self.read_response(0).await?; resp } else { - let len = LittleEndian::read_u16(&self.read_response(2)?); + let len = LittleEndian::read_u16(&self.read_response(2).await?); let buf = Vec::new(); let mut writer = Cursor::new(buf); writer.write_u16::(len)?; - writer.write_all(&self.read_exact(len as usize)?)?; + writer.write_all(&self.read_exact(len as usize).await?)?; C::Response::from_payload(&writer.into_inner())? }) } diff --git a/blflash/src/error.rs b/blflash/src/error.rs index 5f81659..a431760 100644 --- a/blflash/src/error.rs +++ b/blflash/src/error.rs @@ -1,5 +1,6 @@ use thiserror::Error; +#[allow(dead_code)] #[derive(Error, Debug)] #[non_exhaustive] pub enum Error { @@ -29,6 +30,8 @@ pub enum Error { ParseError(#[from] deku::error::DekuError), #[error("Parse toml error")] TomlError(#[from] toml::de::Error), + #[error("WebError {0}")] + WebError(&'static str), } #[derive(Copy, Clone, Debug)] diff --git a/blflash/src/flasher.rs b/blflash/src/flasher.rs index 89bda8e..c5d8ba6 100644 --- a/blflash/src/flasher.rs +++ b/blflash/src/flasher.rs @@ -1,8 +1,8 @@ -use crate::chip::Chip; use crate::Error; +use crate::{async_serial::AsyncSerial, chip::Chip}; use crate::{connection::Connection, elf::RomSegment}; use indicatif::{HumanBytes, ProgressBar, ProgressStyle}; -use serial::{BaudRate, SerialPort}; +use serial::BaudRate; use sha2::{Digest, Sha256}; use std::{ io::{Cursor, Read, Write}, @@ -28,9 +28,9 @@ pub struct Flasher { } impl Flasher { - pub fn connect( + pub async fn connect( chip: impl Chip + 'static, - serial: impl SerialPort + 'static, + serial: AsyncSerial, initial_speed: BaudRate, flash_speed: BaudRate, ) -> Result { @@ -40,10 +40,13 @@ impl Flasher { chip: Box::new(chip), flash_speed, }; - flasher.connection.set_baud(initial_speed)?; - flasher.start_connection()?; - flasher.connection.set_timeout(Duration::from_secs(10))?; - flasher.boot_info = flasher.boot_rom().get_boot_info()?; + flasher.connection.set_baud(initial_speed).await?; + flasher.start_connection().await?; + flasher + .connection + .set_timeout(Duration::from_secs(10)) + .await?; + flasher.boot_info = flasher.boot_rom().get_boot_info().await?; Ok(flasher) } @@ -56,12 +59,12 @@ impl Flasher { &self.boot_info } - pub fn load_segments<'a>( + pub async fn load_segments<'a>( &'a mut self, force: bool, segments: impl Iterator>, ) -> Result<(), Error> { - self.load_eflash_loader()?; + self.load_eflash_loader().await?; for segment in segments { let local_hash = Sha256::digest(&segment.data[0..segment.size() as usize]); @@ -70,7 +73,8 @@ impl Flasher { if !force { let sha256 = self .eflash_loader() - .sha256_read(segment.addr, segment.size())?; + .sha256_read(segment.addr, segment.size()) + .await?; if sha256 == &local_hash[..] { log::info!( "Skip segment addr: {:x} size: {} sha256 matches", @@ -87,7 +91,8 @@ impl Flasher { segment.size() ); self.eflash_loader() - .flash_erase(segment.addr, segment.addr + segment.size())?; + .flash_erase(segment.addr, segment.addr + segment.size()) + .await?; let mut reader = Cursor::new(&segment.data); let mut cur = segment.addr; @@ -96,7 +101,7 @@ impl Flasher { log::info!("Program flash... {:x}", local_hash); let pb = get_bar(segment.size() as u64); loop { - let size = self.eflash_loader().flash_program(cur, &mut reader)?; + let size = self.eflash_loader().flash_program(cur, &mut reader).await?; // log::trace!("program {:x} {:x}", cur, size); cur += size; pb.inc(size as u64); @@ -114,7 +119,8 @@ impl Flasher { let sha256 = self .eflash_loader() - .sha256_read(segment.addr, segment.size())?; + .sha256_read(segment.addr, segment.size()) + .await?; if sha256 != &local_hash[..] { log::warn!( "sha256 not match: {} != {}", @@ -126,18 +132,19 @@ impl Flasher { Ok(()) } - pub fn check_segments<'a>( + pub async fn check_segments<'a>( &'a mut self, segments: impl Iterator>, ) -> Result<(), Error> { - self.load_eflash_loader()?; + self.load_eflash_loader().await?; for segment in segments { let local_hash = Sha256::digest(&segment.data[0..segment.size() as usize]); let sha256 = self .eflash_loader() - .sha256_read(segment.addr, segment.size())?; + .sha256_read(segment.addr, segment.size()) + .await?; if sha256 != &local_hash[..] { log::warn!( "{:x} sha256 not match: {} != {}", @@ -152,8 +159,12 @@ impl Flasher { Ok(()) } - pub fn dump_flash(&mut self, range: Range, mut writer: impl Write) -> Result<(), Error> { - self.load_eflash_loader()?; + pub async fn dump_flash( + &mut self, + range: Range, + mut writer: impl Write, + ) -> Result<(), Error> { + self.load_eflash_loader().await?; const BLOCK_SIZE: usize = 4096; let mut cur = range.start; @@ -161,7 +172,8 @@ impl Flasher { while cur < range.end { let data = self .eflash_loader() - .flash_read(cur, (range.end - cur).min(BLOCK_SIZE as u32))?; + .flash_read(cur, (range.end - cur).min(BLOCK_SIZE as u32)) + .await?; writer.write_all(&data)?; cur += data.len() as u32; pb.inc(data.len() as u64); @@ -171,18 +183,18 @@ impl Flasher { Ok(()) } - pub fn load_eflash_loader(&mut self) -> Result<(), Error> { + pub async fn load_eflash_loader(&mut self) -> Result<(), Error> { let input = self.chip.get_eflash_loader().to_vec(); let len = input.len(); let mut reader = Cursor::new(input); - self.boot_rom().load_boot_header(&mut reader)?; - self.boot_rom().load_segment_header(&mut reader)?; + self.boot_rom().load_boot_header(&mut reader).await?; + self.boot_rom().load_segment_header(&mut reader).await?; let start = Instant::now(); log::info!("Sending eflash_loader..."); let pb = get_bar(len as u64); loop { - let size = self.boot_rom().load_segment_data(&mut reader)?; + let size = self.boot_rom().load_segment_data(&mut reader).await?; pb.inc(size as u64); if size == 0 { break; @@ -196,19 +208,19 @@ impl Flasher { HumanBytes((len as f64 / elapsed.as_millis() as f64 * 1000.0) as u64) ); - self.boot_rom().check_image()?; - self.boot_rom().run_image()?; + self.boot_rom().check_image().await?; + self.boot_rom().run_image().await?; sleep(Duration::from_millis(500)); - self.connection.set_baud(self.flash_speed)?; - self.handshake()?; + self.connection.set_baud(self.flash_speed).await?; + self.handshake().await?; log::info!("Entered eflash_loader"); Ok(()) } - pub fn reset(&mut self) -> Result<(), Error> { - Ok(self.connection.reset()?) + pub async fn reset(&mut self) -> Result<(), Error> { + Ok(self.connection.reset().await?) } fn boot_rom(&mut self) -> BootRom { @@ -219,34 +231,39 @@ impl Flasher { EflashLoader(&mut self.connection) } - fn handshake(&mut self) -> Result<(), Error> { - self.connection - .with_timeout(Duration::from_millis(200), |connection| { - let len = connection.calc_duration_length(Duration::from_millis(5)); - log::trace!("5ms send count {}", len); - let data: Vec = std::iter::repeat(0x55u8).take(len).collect(); - let start = Instant::now(); - connection.write_all(&data)?; - connection.flush()?; - log::trace!("handshake sent elapsed {:?}", start.elapsed()); - sleep(Duration::from_millis(200)); + async fn handshake(&mut self) -> Result<(), Error> { + let connection = &mut self.connection; + let old_timeout = connection.timeout().await; + connection.set_timeout(Duration::from_millis(200)).await?; + let result = async { + let len = connection.calc_duration_length(Duration::from_millis(5)); + log::trace!("5ms send count {}", len); + let data: Vec = std::iter::repeat(0x55u8).take(len).collect(); + let start = Instant::now(); + connection.write_all(&data).await?; + connection.flush().await?; + log::trace!("handshake sent elapsed {:?}", start.elapsed()); + sleep(Duration::from_millis(200)); - for _ in 0..5 { - if connection.read_response(0).is_ok() { - return Ok(()); - } + for _ in 0..5 { + if connection.read_response(0).await.is_ok() { + return Ok(()); } + } - Err(Error::Timeout) - }) + Err(Error::Timeout) + } + .await; + self.connection.set_timeout(old_timeout).await?; + result } - fn start_connection(&mut self) -> Result<(), Error> { + async fn start_connection(&mut self) -> Result<(), Error> { log::info!("Start connection..."); - self.connection.reset_to_flash()?; + self.connection.reset_to_flash().await?; for i in 1..=10 { - self.connection.flush()?; - if self.handshake().is_ok() { + self.connection.flush().await?; + if self.handshake().await.is_ok() { log::info!("Connection Succeed"); return Ok(()); } else { @@ -260,30 +277,35 @@ impl Flasher { pub struct BootRom<'a>(&'a mut Connection); impl<'a> BootRom<'a> { - pub fn run_image(&mut self) -> Result<(), Error> { - self.0.command(protocol::RunImage {})?; + pub async fn run_image(&mut self) -> Result<(), Error> { + self.0.command(protocol::RunImage {}).await?; Ok(()) } - pub fn check_image(&mut self) -> Result<(), Error> { - self.0.command(protocol::CheckImage {})?; + pub async fn check_image(&mut self) -> Result<(), Error> { + self.0.command(protocol::CheckImage {}).await?; Ok(()) } - pub fn load_boot_header(&mut self, reader: &mut impl Read) -> Result<(), Error> { + pub async fn load_boot_header(&mut self, reader: &mut impl Read) -> Result<(), Error> { let mut boot_header = vec![0u8; protocol::LOAD_BOOT_HEADER_LEN]; reader.read_exact(&mut boot_header)?; - self.0.command(protocol::LoadBootHeader { boot_header })?; + self.0 + .command(protocol::LoadBootHeader { boot_header }) + .await?; Ok(()) } - pub fn load_segment_header(&mut self, reader: &mut impl Read) -> Result<(), Error> { + pub async fn load_segment_header(&mut self, reader: &mut impl Read) -> Result<(), Error> { let mut segment_header = vec![0u8; protocol::LOAD_SEGMENT_HEADER_LEN]; reader.read_exact(&mut segment_header)?; - let resp = self.0.command(protocol::LoadSegmentHeaderReq { - segment_header: segment_header.clone(), - })?; + let resp = self + .0 + .command(protocol::LoadSegmentHeaderReq { + segment_header: segment_header.clone(), + }) + .await?; if resp.data != segment_header { log::warn!( @@ -296,7 +318,7 @@ impl<'a> BootRom<'a> { Ok(()) } - pub fn load_segment_data(&mut self, reader: &mut impl Read) -> Result { + pub async fn load_segment_data(&mut self, reader: &mut impl Read) -> Result { let mut segment_data = vec![0u8; 4000]; let size = reader.read(&mut segment_data)?; if size == 0 { @@ -304,28 +326,38 @@ impl<'a> BootRom<'a> { } segment_data.truncate(size); - self.0.command(protocol::LoadSegmentData { segment_data })?; + self.0 + .command(protocol::LoadSegmentData { segment_data }) + .await?; Ok(size as u32) } - pub fn get_boot_info(&mut self) -> Result { - self.0.command(protocol::BootInfoReq {}) + pub async fn get_boot_info(&mut self) -> Result { + self.0.command(protocol::BootInfoReq {}).await } } pub struct EflashLoader<'a>(&'a mut Connection); impl<'a> EflashLoader<'a> { - pub fn sha256_read(&mut self, addr: u32, len: u32) -> Result<[u8; 32], Error> { - Ok(self.0.command(protocol::Sha256Read { addr, len })?.digest) + pub async fn sha256_read(&mut self, addr: u32, len: u32) -> Result<[u8; 32], Error> { + Ok(self + .0 + .command(protocol::Sha256Read { addr, len }) + .await? + .digest) } - pub fn flash_read(&mut self, addr: u32, size: u32) -> Result, Error> { - Ok(self.0.command(protocol::FlashRead { addr, size })?.data) + pub async fn flash_read(&mut self, addr: u32, size: u32) -> Result, Error> { + Ok(self + .0 + .command(protocol::FlashRead { addr, size }) + .await? + .data) } - pub fn flash_program(&mut self, addr: u32, reader: &mut impl Read) -> Result { + pub async fn flash_program(&mut self, addr: u32, reader: &mut impl Read) -> Result { let mut data = vec![0u8; 4000]; let size = reader.read(&mut data)?; if size == 0 { @@ -333,13 +365,15 @@ impl<'a> EflashLoader<'a> { } data.truncate(size); - self.0.command(protocol::FlashProgram { addr, data })?; + self.0 + .command(protocol::FlashProgram { addr, data }) + .await?; Ok(size as u32) } - pub fn flash_erase(&mut self, start: u32, end: u32) -> Result<(), Error> { - self.0.command(protocol::FlashErase { start, end })?; + pub async fn flash_erase(&mut self, start: u32, end: u32) -> Result<(), Error> { + self.0.command(protocol::FlashErase { start, end }).await?; Ok(()) } diff --git a/blflash/src/lib.rs b/blflash/src/lib.rs index b1265ad..5da1adb 100644 --- a/blflash/src/lib.rs +++ b/blflash/src/lib.rs @@ -4,7 +4,11 @@ pub mod elf; mod error; mod flasher; pub mod image; +use async_serial::AsyncSerial; +use serde::Deserialize; +mod async_serial; +pub type Result = std::result::Result; pub use error::{Error, RomError}; pub use flasher::Flasher; @@ -16,7 +20,7 @@ use crate::{ elf::{FirmwareImage, RomSegment}, image::BootHeaderCfgFile, }; -use serial::{BaudRate, CharSize, FlowControl, Parity, SerialPort, SerialPortSettings, StopBits}; +use serial::{BaudRate, CharSize, FlowControl, Parity, SerialPortSettings, StopBits}; use std::{ borrow::Cow, fs::{read, File}, @@ -24,7 +28,7 @@ use std::{ }; use structopt::StructOpt; -#[derive(StructOpt)] +#[derive(StructOpt, Deserialize)] pub struct Connection { /// Serial port #[structopt(short, long)] @@ -37,7 +41,7 @@ pub struct Connection { pub initial_baud_rate: usize, } -#[derive(StructOpt)] +#[derive(StructOpt, Deserialize)] pub struct Boot2Opt { /// Path to partition_cfg.toml, default to be partition/partition_cfg_2M.toml #[structopt(long, parse(from_os_str))] @@ -53,7 +57,7 @@ pub struct Boot2Opt { pub without_boot2: bool, } -#[derive(StructOpt)] +#[derive(StructOpt, Deserialize)] pub struct FlashOpt { #[structopt(flatten)] pub conn: Connection, @@ -67,7 +71,7 @@ pub struct FlashOpt { pub boot: Boot2Opt, } -#[derive(StructOpt)] +#[derive(StructOpt, Deserialize)] pub struct CheckOpt { #[structopt(flatten)] pub conn: Connection, @@ -78,7 +82,7 @@ pub struct CheckOpt { pub boot: Boot2Opt, } -#[derive(StructOpt)] +#[derive(StructOpt, Deserialize)] pub struct DumpOpt { #[structopt(flatten)] pub conn: Connection, @@ -104,25 +108,28 @@ pub enum Opt { } impl Connection { - pub fn open_serial(&self) -> Result { - let mut serial = serial::open(&self.port)?; - serial.reconfigure(&|setup: &mut dyn SerialPortSettings| { - setup.set_char_size(CharSize::Bits8); - setup.set_stop_bits(StopBits::Stop1); - setup.set_parity(Parity::ParityNone); - setup.set_flow_control(FlowControl::FlowNone); - Ok(()) - })?; + pub async fn open_serial(&self) -> Result { + let mut serial = AsyncSerial::open(&self.port).await?; + serial + .reconfigure(&|setup: &mut dyn SerialPortSettings| { + setup.set_char_size(CharSize::Bits8); + setup.set_stop_bits(StopBits::Stop1); + setup.set_parity(Parity::ParityNone); + setup.set_flow_control(FlowControl::FlowNone); + Ok(()) + }) + .await?; Ok(serial) } - pub fn create_flasher(&self, chip: impl Chip + 'static) -> Result { - let serial = self.open_serial()?; + pub async fn create_flasher(&self, chip: impl Chip + 'static) -> Result { + let serial = self.open_serial().await?; Flasher::connect( chip, serial, BaudRate::from_speed(self.initial_baud_rate), BaudRate::from_speed(self.baud_rate), ) + .await } } @@ -192,47 +199,49 @@ pub fn read_image<'a>(chip: &dyn Chip, image: &'a [u8]) -> Result, }) } -pub fn flash(opt: FlashOpt) -> Result<(), Error> { +pub async fn flash(opt: FlashOpt) -> Result<(), Error> { let chip = Bl602; let image = read(&opt.image)?; let image = read_image(&chip, &image)?; - let mut flasher = opt.conn.create_flasher(chip)?; + let mut flasher = opt.conn.create_flasher(chip).await?; log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); log::trace!("Boot info: {:x?}", flasher.boot_info()); let segments = opt.boot.get_segments(&chip, Vec::from(image))?; - flasher.load_segments(opt.force, segments.into_iter())?; - flasher.reset()?; + flasher + .load_segments(opt.force, segments.into_iter()) + .await?; + flasher.reset().await?; log::info!("Success"); Ok(()) } -pub fn check(opt: CheckOpt) -> Result<(), Error> { +pub async fn check(opt: CheckOpt) -> Result<(), Error> { let chip = Bl602; let image = read(&opt.image)?; let image = read_image(&chip, &image)?; - let mut flasher = opt.conn.create_flasher(Bl602)?; + let mut flasher = opt.conn.create_flasher(Bl602).await?; log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); log::trace!("Boot info: {:x?}", flasher.boot_info()); let segments = opt.boot.get_segments(&chip, Vec::from(image))?; - flasher.check_segments(segments.into_iter())?; + flasher.check_segments(segments.into_iter()).await?; Ok(()) } -pub fn dump(opt: DumpOpt) -> Result<(), Error> { +pub async fn dump(opt: DumpOpt) -> Result<(), Error> { let mut output = File::create(opt.output)?; - let mut flasher = opt.conn.create_flasher(Bl602)?; + let mut flasher = opt.conn.create_flasher(Bl602).await?; log::info!("Bootrom version: {}", flasher.boot_info().bootrom_version); log::trace!("Boot info: {:x?}", flasher.boot_info()); - flasher.dump_flash(opt.start..opt.end, &mut output)?; + flasher.dump_flash(opt.start..opt.end, &mut output).await?; log::info!("Success"); diff --git a/blflash/src/main.rs b/blflash/src/main.rs index 5fa4a9e..230408d 100644 --- a/blflash/src/main.rs +++ b/blflash/src/main.rs @@ -1,5 +1,6 @@ use blflash::{check, dump, flash, Opt}; use env_logger::Env; +use futures::executor::block_on; use main_error::MainError; #[paw::main] @@ -8,11 +9,13 @@ fn main(args: Opt) -> Result<(), MainError> { .format_timestamp(None) .init(); - match args { - Opt::Flash(opt) => flash(opt)?, - Opt::Check(opt) => check(opt)?, - Opt::Dump(opt) => dump(opt)?, - }; + block_on(async { + match args { + Opt::Flash(opt) => flash(opt).await, + Opt::Check(opt) => check(opt).await, + Opt::Dump(opt) => dump(opt).await, + } + })?; Ok(()) } diff --git a/cargo-blflash/Cargo.toml b/cargo-blflash/Cargo.toml index 4dbfb12..ece13b0 100644 --- a/cargo-blflash/Cargo.toml +++ b/cargo-blflash/Cargo.toml @@ -17,3 +17,4 @@ color-eyre = "0.5.10" structopt = "0.3.21" paw = "1.0.0" env_logger = "0.8.2" +futures = "0.3" diff --git a/cargo-blflash/src/main.rs b/cargo-blflash/src/main.rs index 1eb6277..28a65f5 100644 --- a/cargo-blflash/src/main.rs +++ b/cargo-blflash/src/main.rs @@ -32,7 +32,7 @@ enum Opt { Blflash(BlflashOpt), } -fn blflash_main(args: BlflashOpt) -> Result<()> { +async fn blflash_main(args: BlflashOpt) -> Result<()> { let chip = Bl602; let target = chip.target(); @@ -51,7 +51,7 @@ fn blflash_main(args: BlflashOpt) -> Result<()> { boot: args.boot, }; - flash(flash_opt)?; + flash(flash_opt).await?; Ok(()) } @@ -62,9 +62,11 @@ fn main(args: Opt) -> Result<()> { .format_timestamp(None) .init(); - match args { - Opt::Blflash(opt) => blflash_main(opt), - } + futures::executor::block_on(async { + match args { + Opt::Blflash(opt) => blflash_main(opt).await, + } + }) } fn get_artifact_path(target: &str, release: bool, example: &Option) -> Result { diff --git a/libblflash/Cargo.toml b/libblflash/Cargo.toml new file mode 100644 index 0000000..0b45240 --- /dev/null +++ b/libblflash/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "libblflash" +version = "0.1.0" +authors = ["spacemeowx2 "] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +blflash = { version = "0.3", path = "../blflash" } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } +wasm-bindgen-futures = "0.4" diff --git a/libblflash/src/lib.rs b/libblflash/src/lib.rs new file mode 100644 index 0000000..8ee7b3f --- /dev/null +++ b/libblflash/src/lib.rs @@ -0,0 +1,16 @@ +use blflash::{Boot2Opt, Connection, Error, FlashOpt}; +use wasm_bindgen::prelude::*; + +fn map_result(r: Result<(), Error>) -> JsValue { + match r { + Ok(_) => JsValue::UNDEFINED, + Err(e) => JsValue::from_str(&e.to_string()), + } +} + +#[wasm_bindgen] +pub async fn flash(opt: JsValue) -> JsValue { + let opt: FlashOpt = opt.into_serde().unwrap(); + let result = blflash::flash(opt).await; + map_result(result) +}