Skip to content

Add no_std support w/ alloc & embedded-io #834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,154 changes: 664 additions & 490 deletions Cargo.lock

Large diffs are not rendered by default.

29 changes: 18 additions & 11 deletions espflash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ required-features = ["cli", "serialport"]

[dependencies]
addr2line = { version = "0.24.2", optional = true }
base64 = "0.22.1"
base64 = { version = "0.22.1", default-features = false }
bitflags = "2.9.0"
bytemuck = { version = "1.21.0", features = ["derive"] }
clap = { version = "4.5.24", features = ["derive", "env", "wrap_help"], optional = true }
Expand All @@ -38,23 +38,27 @@ defmt-decoder = { version = "=0.4.0", features = ["unstable"], optional = true
dialoguer = { version = "0.11.0", optional = true }
directories = { version = "5.0.1", optional = true }
env_logger = { version = "0.11.6", optional = true }
esp-idf-part = "0.5.0"
flate2 = "1.0.35"
esp-idf-part = { git = "https://github.com/i404788/esp-idf-part.git", default-features = false }
flate2 = { version = "1.0.35", optional = true }
indicatif = { version = "0.17.9", optional = true }
log = "0.4.22"
md-5 = "0.10.6"
miette = "7.4.0"
object = "0.36.7"
md-5 = { version = "0.10.6", default-features = false}
miette = { version = "7.4.0", optional = true}
object = { version = "0.36.7", default-features = false, features = ['read'] }
regex = { version = "1.11.1", optional = true }
serde = { version = "1.0.217", features = ["derive"] }
serde = { version = "1.0.219", features = ["derive"], optional = true }
serialport = { version = "4.7.0", default-features = false, optional = true }
sha2 = "0.10.8"
sha2 = { version = "0.10.8", default-features = false}
slip-codec = { version = "0.4.0", optional = true }
strum = { version = "0.26.3", features = ["derive"] }
thiserror = "2.0.10"
strum = { version = "0.26.3", default-features = false, features = ["derive"] }
thiserror = { version = "2.0.10", default-features = false }
toml = { version = "0.8.19", optional = true }
update-informer = { version = "1.2.0", optional = true }

heapless = { version = "0.8.0" }
embedded-io = { version = "0.6.1", features = ["alloc"]}
embedded-io-adapters = { version = "0.6.1"}

[target.'cfg(unix)'.dependencies]
libc = "0.2.169"

Expand All @@ -75,7 +79,10 @@ cli = [
"dep:update-informer",
"miette/fancy",
"serialport",
"std"
]

# Enables connecting to a device via serial port
serialport = ["dep:regex", "dep:serialport", "dep:slip-codec", "dep:toml"]
serialport = ["dep:regex", "dep:serialport", "dep:slip-codec", "dep:toml", "std"]

std = ["dep:miette", "dep:flate2", "object/default", "dep:serde", "thiserror/std", "strum/std", "esp-idf-part/std", "md-5/std", "sha2/std", "base64/std", "embedded-io/std", "embedded-io-adapters/std"]
16 changes: 6 additions & 10 deletions espflash/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
//! [espflash]: https://crates.io/crates/espflash

use std::{
collections::HashMap,
fs::{self, File},
io::{Read, Write},
num::ParseIntError,
path::{Path, PathBuf},
};

use alloc::collections::BTreeMap;
use alloc::vec::Vec;

use clap::{Args, ValueEnum};
use clap_complete::Shell;
use comfy_table::{modifiers, presets::UTF8_FULL, Attribute, Cell, Color, Table};
Expand All @@ -35,13 +37,7 @@ use crate::{
connection::reset::{ResetAfterOperation, ResetBeforeOperation},
error::{Error, MissingPartition, MissingPartitionTable},
flasher::{
FlashData,
FlashFrequency,
FlashMode,
FlashSettings,
FlashSize,
Flasher,
ProgressCallbacks,
FlashData, FlashFrequency, FlashMode, FlashSettings, FlashSize, Flasher, ProgressCallbacks,
FLASH_SECTOR_SIZE,
},
image_format::Metadata,
Expand Down Expand Up @@ -830,7 +826,7 @@ pub fn erase_partitions(
.ok_or_else(|| MissingPartition::from(label))?;

parts_to_erase
.get_or_insert(HashMap::new())
.get_or_insert(BTreeMap::new())
.insert(part.offset(), part);
}
}
Expand All @@ -845,7 +841,7 @@ pub fn erase_partitions(
&& part.subtype() == esp_idf_part::SubType::Data(ty)
{
parts_to_erase
.get_or_insert(HashMap::new())
.get_or_insert(BTreeMap::new())
.insert(part.offset(), part);
}
}
Expand Down
86 changes: 79 additions & 7 deletions espflash/src/connection/command.rs → espflash/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
//! Commands to work with a flasher stub running on a target device

use std::{io::Write, mem::size_of, time::Duration};
use core::{mem::size_of, time::Duration};

use alloc::vec::Vec;
use embedded_io::Write;

use bytemuck::{bytes_of, Pod, Zeroable};
use strum::Display;

use crate::flasher::{SpiAttachParams, SpiSetParams};
use crate::{
flasher::{SpiAttachParams, SpiSetParams},
Error,
};

const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
const ERASE_REGION_TIMEOUT_PER_MB: Duration = Duration::from_secs(30);
Expand Down Expand Up @@ -81,7 +87,7 @@ impl CommandType {
pub fn timeout_for_size(&self, size: u32) -> Duration {
fn calc_timeout(timeout_per_mb: Duration, size: u32) -> Duration {
let mb = size as f64 / 1_000_000.0;
std::cmp::max(
core::cmp::max(
FLASH_DEFLATE_END_TIMEOUT,
Duration::from_millis((timeout_per_mb.as_millis() as f64 * mb) as u64),
)
Expand Down Expand Up @@ -238,7 +244,7 @@ impl Command<'_> {
}

/// Write a command
pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
pub fn write<W: Write>(&self, mut writer: W) -> Result<(), W::Error> {
// Write the Direction and Command Indentifier
writer.write_all(&[0, self.command_type() as u8])?;
match *self {
Expand Down Expand Up @@ -456,7 +462,7 @@ impl Command<'_> {
}

/// Write a data array and its checksum to a writer
fn write_basic<W: Write>(mut writer: W, data: &[u8], checksum: u32) -> std::io::Result<()> {
fn write_basic<W: Write>(mut writer: W, data: &[u8], checksum: u32) -> Result<(), W::Error> {
writer.write_all(&((data.len() as u16).to_le_bytes()))?;
writer.write_all(&(checksum.to_le_bytes()))?;
writer.write_all(data)?;
Expand All @@ -471,7 +477,7 @@ fn begin_command<W: Write>(
block_size: u32,
offset: u32,
supports_encryption: bool,
) -> std::io::Result<()> {
) -> Result<(), W::Error> {
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
#[repr(C)]
struct BeginParams {
Expand Down Expand Up @@ -508,7 +514,7 @@ fn data_command<W: Write>(
pad_to: usize,
pad_byte: u8,
sequence: u32,
) -> std::io::Result<()> {
) -> Result<(), W::Error> {
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
#[repr(C)]
struct BlockParams {
Expand Down Expand Up @@ -553,3 +559,69 @@ fn checksum(data: &[u8], mut checksum: u8) -> u8 {

checksum
}

#[derive(Debug, Clone)]
pub enum CommandResponseValue {
ValueU32(u32),
ValueU128(u128),
Vector(Vec<u8>),
}

impl TryInto<u32> for CommandResponseValue {
type Error = Error;

fn try_into(self) -> Result<u32, Self::Error> {
match self {
CommandResponseValue::ValueU32(value) => Ok(value),
CommandResponseValue::ValueU128(_) => Err(Error::InvalidResponse(
"expected `u32` but found `u128`".into(),
)),
CommandResponseValue::Vector(_) => Err(Error::InvalidResponse(
"expected `u32` but found `Vec`".into(),
)),
}
}
}

impl TryInto<u128> for CommandResponseValue {
type Error = Error;

fn try_into(self) -> Result<u128, Self::Error> {
match self {
CommandResponseValue::ValueU32(_) => Err(Error::InvalidResponse(
"expected `u128` but found `u32`".into(),
)),
CommandResponseValue::ValueU128(value) => Ok(value),
CommandResponseValue::Vector(_) => Err(Error::InvalidResponse(
"expected `u128` but found `Vec`".into(),
)),
}
}
}

impl TryInto<Vec<u8>> for CommandResponseValue {
type Error = Error;

fn try_into(self) -> Result<Vec<u8>, Self::Error> {
match self {
CommandResponseValue::ValueU32(_) => Err(Error::InvalidResponse(
"expected `Vec` but found `u32`".into(),
)),
CommandResponseValue::ValueU128(_) => Err(Error::InvalidResponse(
"expected `Vec` but found `u128`".into(),
)),
CommandResponseValue::Vector(value) => Ok(value),
}
}
}

/// A response from a target device following a command
#[derive(Debug, Clone)]
pub struct CommandResponse {
pub resp: u8,
pub return_op: u8,
pub return_length: u16,
pub value: CommandResponseValue,
pub error: u8,
pub status: u8,
}
Loading
Loading