feat: make functions async

This commit is contained in:
spacemeowx2 2021-01-08 14:00:47 +08:00
parent b0967e75f7
commit e587a44f08
14 changed files with 640 additions and 166 deletions

223
Cargo.lock generated
View File

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

View File

@ -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"
]

View File

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

View File

@ -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<dyn SerialPort>);
impl AsyncSerial {
pub async fn open(port: &str) -> crate::Result<Self> {
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<io::Result<usize>> {
Poll::Ready(self.0.read(buf))
}
}
impl AsyncWrite for AsyncSerial {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(self.0.write(buf))
}
fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(self.0.flush())
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}

View File

@ -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<Self> {
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<io::Result<usize>> {
todo!()
}
}
impl AsyncWrite for AsyncSerial {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
todo!()
}
fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
todo!()
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
todo!()
}
}

View File

@ -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<dyn SerialPort>,
serial: AsyncSerial,
baud_rate: Option<BaudRate>,
}
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<dyn SerialPort> {
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<T, F: FnMut(&mut Connection) -> Result<T, Error>>(
&mut self,
timeout: Duration,
mut f: F,
) -> Result<T, Error> {
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<Vec<u8>, Error> {
async fn read_exact(&mut self, len: usize) -> Result<Vec<u8>, 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<Vec<u8>, Error> {
let resp = self.read_exact(2)?;
pub async fn read_response(&mut self, len: usize) -> Result<Vec<u8>, 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::<LittleEndian>()?;
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<C: Command>(&mut self, command: C) -> Result<C::Response, Error> {
pub async fn command<C: Command>(&mut self, command: C) -> Result<C::Response, Error> {
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::<LittleEndian>(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())?
})
}

View File

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

View File

@ -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<Self, Error> {
@ -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<Item = RomSegment<'a>>,
) -> 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<Item = RomSegment<'a>>,
) -> 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<u32>, mut writer: impl Write) -> Result<(), Error> {
self.load_eflash_loader()?;
pub async fn dump_flash(
&mut self,
range: Range<u32>,
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<u8> = 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<u8> = 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<u32, Error> {
pub async fn load_segment_data(&mut self, reader: &mut impl Read) -> Result<u32, Error> {
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<protocol::BootInfo, Error> {
self.0.command(protocol::BootInfoReq {})
pub async fn get_boot_info(&mut self) -> Result<protocol::BootInfo, Error> {
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<Vec<u8>, Error> {
Ok(self.0.command(protocol::FlashRead { addr, size })?.data)
pub async fn flash_read(&mut self, addr: u32, size: u32) -> Result<Vec<u8>, Error> {
Ok(self
.0
.command(protocol::FlashRead { addr, size })
.await?
.data)
}
pub fn flash_program(&mut self, addr: u32, reader: &mut impl Read) -> Result<u32, Error> {
pub async fn flash_program(&mut self, addr: u32, reader: &mut impl Read) -> Result<u32, Error> {
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(())
}

View File

@ -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<T, E = Error> = std::result::Result<T, E>;
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<impl SerialPort, Error> {
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<AsyncSerial, Error> {
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<Flasher, Error> {
let serial = self.open_serial()?;
pub async fn create_flasher(&self, chip: impl Chip + 'static) -> Result<Flasher, Error> {
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<Cow<'a, [u8]>,
})
}
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");

View File

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

View File

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

View File

@ -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<String>) -> Result<PathBuf> {

15
libblflash/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "libblflash"
version = "0.1.0"
authors = ["spacemeowx2 <spacemeowx2@gmail.com>"]
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"

16
libblflash/src/lib.rs Normal file
View File

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