use {
crate::{
error::{Error, Result},
image::fwpkg::Fwpkg,
port::{Port, SerialConfig},
},
std::fmt,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum ChipFamily {
#[default]
Ws63,
Bs2x,
Bs25,
Ws53,
Sw39,
Generic,
}
impl ChipFamily {
#[must_use]
pub fn default_baud(&self) -> u32 {
115200
}
#[must_use]
pub fn high_speed_baud(&self) -> u32 {
match self {
Self::Bs2x | Self::Bs25 => 2_000_000,
_ => 921_600,
}
}
#[must_use]
pub fn supported_bauds(&self) -> &'static [u32] {
match self {
Self::Bs2x | Self::Bs25 => &[115_200, 230_400, 460_800, 921_600, 2_000_000],
_ => &[115_200, 230_400, 460_800, 921_600],
}
}
pub fn supports_usb_dfu(&self) -> bool {
matches!(self, Self::Bs2x | Self::Bs25)
}
pub fn supports_efuse(&self) -> bool {
true }
pub fn requires_signed_firmware(&self) -> bool {
matches!(self, Self::Ws63 | Self::Bs2x | Self::Bs25)
}
pub fn from_name(name: &str) -> Option<Self> {
match name
.to_lowercase()
.as_str()
{
"ws63" => Some(Self::Ws63),
"bs2x" | "bs21" => Some(Self::Bs2x),
"bs25" => Some(Self::Bs25),
"ws53" => Some(Self::Ws53),
"sw39" => Some(Self::Sw39),
"generic" | "auto" => Some(Self::Generic),
_ => None,
}
}
}
impl fmt::Display for ChipFamily {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ws63 => write!(f, "WS63"),
Self::Bs2x => write!(f, "BS2X"),
Self::Bs25 => write!(f, "BS25"),
Self::Ws53 => write!(f, "WS53"),
Self::Sw39 => write!(f, "SW39"),
Self::Generic => write!(f, "Generic"),
}
}
}
#[derive(Debug, Clone)]
pub struct ChipConfig {
pub family: ChipFamily,
pub init_baud: u32,
pub target_baud: u32,
pub late_baud_switch: bool,
pub handshake_timeout_secs: u32,
pub transfer_timeout_secs: u32,
}
impl ChipConfig {
pub fn new(family: ChipFamily) -> Self {
Self {
family,
init_baud: family.default_baud(),
target_baud: family.high_speed_baud(),
late_baud_switch: false,
handshake_timeout_secs: 30,
transfer_timeout_secs: 60,
}
}
#[must_use]
pub fn with_baud(mut self, baud: u32) -> Self {
self.target_baud = baud;
self
}
#[must_use]
pub fn with_late_baud(mut self, late: bool) -> Self {
self.late_baud_switch = late;
self
}
#[must_use]
pub fn with_handshake_timeout(mut self, secs: u32) -> Self {
self.handshake_timeout_secs = secs;
self
}
}
impl Default for ChipConfig {
fn default() -> Self {
Self::new(ChipFamily::default())
}
}
pub trait Flasher {
fn connect(&mut self) -> Result<()>;
fn flash_fwpkg(
&mut self,
fwpkg: &Fwpkg,
filter: Option<&[&str]>,
progress: &mut dyn FnMut(&str, usize, usize),
) -> Result<()>;
fn write_bins(&mut self, loaderboot: &[u8], bins: &[(&[u8], u32)]) -> Result<()>;
fn erase_all(&mut self) -> Result<()>;
fn reset(&mut self) -> Result<()>;
fn connection_baud(&self) -> u32;
fn target_baud(&self) -> Option<u32>;
fn close(&mut self);
}
impl ChipFamily {
#[cfg(feature = "native")]
pub fn create_flasher(
&self,
port_name: &str,
target_baud: u32,
late_baud: bool,
verbose: u8,
) -> Result<Box<dyn Flasher>> {
match self {
Self::Ws63 => {
let flasher = super::ws63::flasher::Ws63Flasher::open(port_name, target_baud)?
.with_late_baud(late_baud)
.with_verbose(verbose);
Ok(Box::new(flasher))
},
Self::Bs2x | Self::Bs25 => {
Err(Error::Unsupported("BS2X series support coming soon".into()))
},
Self::Ws53 | Self::Sw39 => Err(Error::Unsupported(format!(
"{self} series support coming soon"
))),
Self::Generic => Err(Error::Unsupported(
"Cannot create flasher for generic chip family".into(),
)),
}
}
#[cfg(feature = "native")]
pub fn create_flasher_with_port<P: Port + 'static>(
&self,
port: P,
target_baud: u32,
late_baud: bool,
verbose: u8,
) -> Result<Box<dyn Flasher>> {
self.create_flasher_with_port_and_cancel(
port,
target_baud,
late_baud,
verbose,
crate::CancelContext::none(),
)
}
#[cfg(feature = "native")]
pub fn create_flasher_with_port_and_cancel<P: Port + 'static>(
&self,
port: P,
target_baud: u32,
late_baud: bool,
verbose: u8,
cancel: crate::CancelContext,
) -> Result<Box<dyn Flasher>> {
match self {
Self::Ws63 => {
let flasher =
super::ws63::flasher::Ws63Flasher::with_cancel(port, target_baud, cancel)
.with_late_baud(late_baud)
.with_verbose(verbose);
Ok(Box::new(flasher))
},
_ => Err(Error::Unsupported(format!(
"Unsupported chip family for generic port: {self}"
))),
}
}
#[cfg(feature = "native")]
pub fn create_flasher_with_config(
&self,
config: SerialConfig,
late_baud: bool,
verbose: u8,
) -> Result<Box<dyn Flasher>> {
match self {
Self::Ws63 => {
let flasher = super::ws63::flasher::Ws63Flasher::open_with_config(config)?
.with_late_baud(late_baud)
.with_verbose(verbose);
Ok(Box::new(flasher))
},
Self::Bs2x | Self::Bs25 => {
Err(Error::Unsupported("BS2X series support coming soon".into()))
},
Self::Ws53 | Self::Sw39 => Err(Error::Unsupported(format!(
"{self} series support coming soon"
))),
Self::Generic => Err(Error::Unsupported(
"Cannot create flasher for generic chip family".into(),
)),
}
}
}
pub trait ChipOps {
fn family(&self) -> ChipFamily;
fn config(&self) -> &ChipConfig;
fn prepare_binary(&self, data: &[u8], _addr: u32) -> Result<Vec<u8>> {
Ok(data.to_vec())
}
fn needs_signing(&self, _addr: u32) -> bool {
false
}
fn flash_base(&self) -> u32 {
0x00000000
}
fn flash_size(&self) -> u32 {
0x00800000 }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chip_family_from_name() {
assert_eq!(ChipFamily::from_name("ws63"), Some(ChipFamily::Ws63));
assert_eq!(ChipFamily::from_name("BS2X"), Some(ChipFamily::Bs2x));
assert_eq!(ChipFamily::from_name("bs21"), Some(ChipFamily::Bs2x));
assert_eq!(ChipFamily::from_name("bs25"), Some(ChipFamily::Bs25));
assert_eq!(ChipFamily::from_name("ws53"), Some(ChipFamily::Ws53));
assert_eq!(ChipFamily::from_name("sw39"), Some(ChipFamily::Sw39));
assert_eq!(ChipFamily::from_name("generic"), Some(ChipFamily::Generic));
assert_eq!(ChipFamily::from_name("auto"), Some(ChipFamily::Generic));
assert_eq!(ChipFamily::from_name("unknown"), None);
assert_eq!(ChipFamily::from_name(""), None);
}
#[test]
fn test_chip_family_from_name_case_insensitive() {
assert_eq!(ChipFamily::from_name("WS63"), Some(ChipFamily::Ws63));
assert_eq!(ChipFamily::from_name("Ws63"), Some(ChipFamily::Ws63));
assert_eq!(ChipFamily::from_name("BS25"), Some(ChipFamily::Bs25));
}
#[test]
fn test_chip_config_defaults() {
let config = ChipConfig::new(ChipFamily::Ws63);
assert_eq!(config.init_baud, 115200);
assert_eq!(config.target_baud, 921600);
assert!(!config.late_baud_switch);
assert_eq!(config.handshake_timeout_secs, 30);
assert_eq!(config.transfer_timeout_secs, 60);
}
#[test]
fn test_chip_config_bs2x_defaults() {
let config = ChipConfig::new(ChipFamily::Bs2x);
assert_eq!(config.init_baud, 115200);
assert_eq!(config.target_baud, 2_000_000);
}
#[test]
fn test_chip_config_builder() {
let config = ChipConfig::new(ChipFamily::Ws63)
.with_baud(460800)
.with_late_baud(true)
.with_handshake_timeout(10);
assert_eq!(config.target_baud, 460800);
assert!(config.late_baud_switch);
assert_eq!(config.handshake_timeout_secs, 10);
}
#[test]
fn test_chip_config_default_trait() {
let config = ChipConfig::default();
assert_eq!(config.family, ChipFamily::Ws63); }
#[test]
fn test_chip_family_default() {
let family = ChipFamily::default();
assert_eq!(family, ChipFamily::Ws63);
}
#[test]
fn test_chip_family_display() {
assert_eq!(ChipFamily::Ws63.to_string(), "WS63");
assert_eq!(ChipFamily::Bs2x.to_string(), "BS2X");
assert_eq!(ChipFamily::Bs25.to_string(), "BS25");
assert_eq!(ChipFamily::Ws53.to_string(), "WS53");
assert_eq!(ChipFamily::Sw39.to_string(), "SW39");
assert_eq!(ChipFamily::Generic.to_string(), "Generic");
}
#[test]
fn test_chip_family_default_baud() {
for family in [
ChipFamily::Ws63,
ChipFamily::Bs2x,
ChipFamily::Bs25,
ChipFamily::Generic,
] {
assert_eq!(family.default_baud(), 115200, "Failed for {family}");
}
}
#[test]
fn test_chip_family_high_speed_baud() {
assert_eq!(ChipFamily::Ws63.high_speed_baud(), 921_600);
assert_eq!(ChipFamily::Bs2x.high_speed_baud(), 2_000_000);
assert_eq!(ChipFamily::Bs25.high_speed_baud(), 2_000_000);
assert_eq!(ChipFamily::Generic.high_speed_baud(), 921_600);
}
#[test]
fn test_chip_family_supported_bauds() {
let ws63_bauds = ChipFamily::Ws63.supported_bauds();
assert!(ws63_bauds.contains(&115_200));
assert!(ws63_bauds.contains(&921_600));
assert!(!ws63_bauds.contains(&2_000_000));
let bs2x_bauds = ChipFamily::Bs2x.supported_bauds();
assert!(bs2x_bauds.contains(&2_000_000));
}
#[test]
fn test_chip_family_usb_dfu() {
assert!(!ChipFamily::Ws63.supports_usb_dfu());
assert!(ChipFamily::Bs2x.supports_usb_dfu());
assert!(ChipFamily::Bs25.supports_usb_dfu());
assert!(!ChipFamily::Generic.supports_usb_dfu());
}
#[test]
fn test_chip_family_efuse() {
for family in [
ChipFamily::Ws63,
ChipFamily::Bs2x,
ChipFamily::Bs25,
ChipFamily::Generic,
] {
assert!(family.supports_efuse());
}
}
#[test]
fn test_chip_family_signed_firmware() {
assert!(ChipFamily::Ws63.requires_signed_firmware());
assert!(ChipFamily::Bs2x.requires_signed_firmware());
assert!(ChipFamily::Bs25.requires_signed_firmware());
assert!(!ChipFamily::Generic.requires_signed_firmware());
}
#[test]
fn test_chip_family_clone_eq() {
let a = ChipFamily::Ws63;
let b = a;
assert_eq!(a, b);
let c = ChipFamily::Bs2x;
assert_ne!(a, c);
}
#[test]
fn test_chip_family_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(ChipFamily::Ws63);
set.insert(ChipFamily::Bs2x);
set.insert(ChipFamily::Ws63); assert_eq!(set.len(), 2);
}
#[cfg(feature = "native")]
#[test]
fn test_create_flasher_unsupported_chip() {
let result = ChipFamily::Bs2x.create_flasher("/dev/null", 115200, false, 0);
assert!(result.is_err());
let result = ChipFamily::Generic.create_flasher("/dev/null", 115200, false, 0);
assert!(result.is_err());
}
}