sftool 0.2.2

SiFli SoC serial utility command-line tool
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use sftool_lib::stub_config as lib;
use sftool_lib::utils::Utils;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StubConfigSpec {
    #[serde(default)]
    pub pins: Vec<PinSpec>,
    #[serde(default)]
    pub flash: Vec<FlashSpec>,
    pub pmic: Option<PmicSpec>,
    pub sd0: Option<Sd0Spec>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum PinPortSpec {
    #[serde(rename = "PA")]
    Pa,
    #[serde(rename = "PB")]
    Pb,
    #[serde(rename = "PBR")]
    Pbr,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PinLevelSpec {
    Low,
    High,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PinSpec {
    pub port: PinPortSpec,
    pub number: u8,
    pub level: PinLevelSpec,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FlashMediaSpec {
    Nor,
    Nand,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlashSpec {
    pub media: FlashMediaSpec,
    pub driver_index: u8,
    pub manufacturer_id: u8,
    pub device_type: u8,
    pub density_id: u8,
    pub flags: u8,
    pub capacity_bytes: SizeValue,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum PmicChannelSpec {
    #[serde(rename = "1v8_lvsw100_1")]
    LvSw1001,
    #[serde(rename = "1v8_lvsw100_2")]
    LvSw1002,
    #[serde(rename = "1v8_lvsw100_3")]
    LvSw1003,
    #[serde(rename = "1v8_lvsw100_4")]
    LvSw1004,
    #[serde(rename = "1v8_lvsw100_5")]
    LvSw1005,
    #[serde(rename = "vbat_hvsw150_1")]
    HvSw1501,
    #[serde(rename = "vbat_hvsw150_2")]
    HvSw1502,
    #[serde(rename = "ldo33")]
    Ldo33,
    #[serde(rename = "ldo30")]
    Ldo30,
    #[serde(rename = "ldo28")]
    Ldo28,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PmicSpec {
    pub disabled: bool,
    pub scl_port: PinPortSpec,
    pub scl_pin: u8,
    pub sda_port: PinPortSpec,
    pub sda_pin: u8,
    pub channels: Vec<PmicChannelSpec>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum Sd0PinmuxSpec {
    #[serde(rename = "clk_pa34_or_pa09")]
    ClkPa34OrPa09,
    #[serde(rename = "clk_pa60_or_pa39")]
    ClkPa60OrPa39,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum Sd0InitSequenceSpec {
    #[serde(rename = "emmc_then_sd")]
    EmmcThenSd,
    #[serde(rename = "sd_then_emmc")]
    SdThenEmmc,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sd0Spec {
    pub base_address: AddressValue,
    pub pinmux: Sd0PinmuxSpec,
    pub init_sequence: Sd0InitSequenceSpec,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AddressValue {
    Number(u64),
    Text(String),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SizeValue {
    Number(u64),
    Text(String),
}

impl AddressValue {
    fn to_u32(&self, label: &str) -> Result<u32> {
        match self {
            AddressValue::Number(value) => {
                u32::try_from(*value).context(format!("{label} exceeds u32 range"))
            }
            AddressValue::Text(value) => {
                if value
                    .chars()
                    .last()
                    .map(|c| matches!(c, 'k' | 'K' | 'm' | 'M' | 'g' | 'G'))
                    .unwrap_or(false)
                {
                    anyhow::bail!("{label} does not accept k/M/G suffix");
                }
                Utils::str_to_u32(value).map_err(|e| anyhow::anyhow!("{label} parse error: {e}"))
            }
        }
    }
}

impl SizeValue {
    fn to_u32(&self, label: &str) -> Result<u32> {
        match self {
            SizeValue::Number(value) => {
                u32::try_from(*value).context(format!("{label} exceeds u32 range"))
            }
            SizeValue::Text(value) => {
                Utils::str_to_u32(value).map_err(|e| anyhow::anyhow!("{label} parse error: {e}"))
            }
        }
    }
}

impl StubConfigSpec {
    pub fn to_stub_config(&self) -> Result<lib::StubConfig> {
        if self.pins.len() > 12 {
            anyhow::bail!("pins length exceeds 12");
        }
        if self.flash.len() > 12 {
            anyhow::bail!("flash length exceeds 12");
        }

        let pins = self
            .pins
            .iter()
            .map(|pin| {
                Ok(lib::PinConfig {
                    port: pin.port.into(),
                    number: pin.number,
                    level: pin.level.into(),
                })
            })
            .collect::<Result<Vec<_>>>()?;

        let flash = self
            .flash
            .iter()
            .map(|entry| {
                Ok(lib::FlashConfig {
                    media: entry.media.into(),
                    driver_index: entry.driver_index,
                    manufacturer_id: entry.manufacturer_id,
                    device_type: entry.device_type,
                    density_id: entry.density_id,
                    flags: entry.flags,
                    capacity_bytes: entry.capacity_bytes.to_u32("capacity_bytes")?,
                })
            })
            .collect::<Result<Vec<_>>>()?;

        let pmic = self.pmic.as_ref().map(|pmic| lib::PmicConfig {
            disabled: pmic.disabled,
            scl_port: pmic.scl_port.into(),
            scl_pin: pmic.scl_pin,
            sda_port: pmic.sda_port.into(),
            sda_pin: pmic.sda_pin,
            channels: pmic.channels.iter().map(|c| (*c).into()).collect(),
        });

        let sd0 = match &self.sd0 {
            Some(sd0) => Some(lib::Sd0Config {
                base_address: sd0.base_address.to_u32("base_address")?,
                pinmux: sd0.pinmux.into(),
                init_sequence: sd0.init_sequence.into(),
            }),
            None => None,
        };

        Ok(lib::StubConfig {
            pins,
            flash,
            pmic,
            sd0,
        })
    }

    pub fn from_stub_config(config: &lib::StubConfig) -> Self {
        let pins = config
            .pins
            .iter()
            .map(|pin| PinSpec {
                port: pin.port.into(),
                number: pin.number,
                level: pin.level.into(),
            })
            .collect();

        let flash = config
            .flash
            .iter()
            .map(|entry| FlashSpec {
                media: entry.media.into(),
                driver_index: entry.driver_index,
                manufacturer_id: entry.manufacturer_id,
                device_type: entry.device_type,
                density_id: entry.density_id,
                flags: entry.flags,
                capacity_bytes: SizeValue::Number(entry.capacity_bytes as u64),
            })
            .collect();

        let pmic = config.pmic.as_ref().map(|pmic| PmicSpec {
            disabled: pmic.disabled,
            scl_port: pmic.scl_port.into(),
            scl_pin: pmic.scl_pin,
            sda_port: pmic.sda_port.into(),
            sda_pin: pmic.sda_pin,
            channels: pmic.channels.iter().map(|c| (*c).into()).collect(),
        });

        let sd0 = config.sd0.as_ref().map(|sd0| Sd0Spec {
            base_address: AddressValue::Number(sd0.base_address as u64),
            pinmux: sd0.pinmux.into(),
            init_sequence: sd0.init_sequence.into(),
        });

        Self {
            pins,
            flash,
            pmic,
            sd0,
        }
    }
}

impl From<PinPortSpec> for lib::PinPort {
    fn from(value: PinPortSpec) -> Self {
        match value {
            PinPortSpec::Pa => lib::PinPort::Pa,
            PinPortSpec::Pb => lib::PinPort::Pb,
            PinPortSpec::Pbr => lib::PinPort::Pbr,
        }
    }
}

impl From<lib::PinPort> for PinPortSpec {
    fn from(value: lib::PinPort) -> Self {
        match value {
            lib::PinPort::Pa => PinPortSpec::Pa,
            lib::PinPort::Pb => PinPortSpec::Pb,
            lib::PinPort::Pbr => PinPortSpec::Pbr,
        }
    }
}

impl From<PinLevelSpec> for lib::PinLevel {
    fn from(value: PinLevelSpec) -> Self {
        match value {
            PinLevelSpec::Low => lib::PinLevel::Low,
            PinLevelSpec::High => lib::PinLevel::High,
        }
    }
}

impl From<lib::PinLevel> for PinLevelSpec {
    fn from(value: lib::PinLevel) -> Self {
        match value {
            lib::PinLevel::Low => PinLevelSpec::Low,
            lib::PinLevel::High => PinLevelSpec::High,
        }
    }
}

impl From<FlashMediaSpec> for lib::FlashMedia {
    fn from(value: FlashMediaSpec) -> Self {
        match value {
            FlashMediaSpec::Nor => lib::FlashMedia::Nor,
            FlashMediaSpec::Nand => lib::FlashMedia::Nand,
        }
    }
}

impl From<lib::FlashMedia> for FlashMediaSpec {
    fn from(value: lib::FlashMedia) -> Self {
        match value {
            lib::FlashMedia::Nor => FlashMediaSpec::Nor,
            lib::FlashMedia::Nand => FlashMediaSpec::Nand,
        }
    }
}

impl From<PmicChannelSpec> for lib::PmicChannel {
    fn from(value: PmicChannelSpec) -> Self {
        match value {
            PmicChannelSpec::LvSw1001 => lib::PmicChannel::LvSw1001,
            PmicChannelSpec::LvSw1002 => lib::PmicChannel::LvSw1002,
            PmicChannelSpec::LvSw1003 => lib::PmicChannel::LvSw1003,
            PmicChannelSpec::LvSw1004 => lib::PmicChannel::LvSw1004,
            PmicChannelSpec::LvSw1005 => lib::PmicChannel::LvSw1005,
            PmicChannelSpec::HvSw1501 => lib::PmicChannel::HvSw1501,
            PmicChannelSpec::HvSw1502 => lib::PmicChannel::HvSw1502,
            PmicChannelSpec::Ldo33 => lib::PmicChannel::Ldo33,
            PmicChannelSpec::Ldo30 => lib::PmicChannel::Ldo30,
            PmicChannelSpec::Ldo28 => lib::PmicChannel::Ldo28,
        }
    }
}

impl From<lib::PmicChannel> for PmicChannelSpec {
    fn from(value: lib::PmicChannel) -> Self {
        match value {
            lib::PmicChannel::LvSw1001 => PmicChannelSpec::LvSw1001,
            lib::PmicChannel::LvSw1002 => PmicChannelSpec::LvSw1002,
            lib::PmicChannel::LvSw1003 => PmicChannelSpec::LvSw1003,
            lib::PmicChannel::LvSw1004 => PmicChannelSpec::LvSw1004,
            lib::PmicChannel::LvSw1005 => PmicChannelSpec::LvSw1005,
            lib::PmicChannel::HvSw1501 => PmicChannelSpec::HvSw1501,
            lib::PmicChannel::HvSw1502 => PmicChannelSpec::HvSw1502,
            lib::PmicChannel::Ldo33 => PmicChannelSpec::Ldo33,
            lib::PmicChannel::Ldo30 => PmicChannelSpec::Ldo30,
            lib::PmicChannel::Ldo28 => PmicChannelSpec::Ldo28,
        }
    }
}

impl From<Sd0PinmuxSpec> for lib::Sd0Pinmux {
    fn from(value: Sd0PinmuxSpec) -> Self {
        match value {
            Sd0PinmuxSpec::ClkPa34OrPa09 => lib::Sd0Pinmux::ClkPa34OrPa09,
            Sd0PinmuxSpec::ClkPa60OrPa39 => lib::Sd0Pinmux::ClkPa60OrPa39,
        }
    }
}

impl From<lib::Sd0Pinmux> for Sd0PinmuxSpec {
    fn from(value: lib::Sd0Pinmux) -> Self {
        match value {
            lib::Sd0Pinmux::ClkPa34OrPa09 => Sd0PinmuxSpec::ClkPa34OrPa09,
            lib::Sd0Pinmux::ClkPa60OrPa39 => Sd0PinmuxSpec::ClkPa60OrPa39,
        }
    }
}

impl From<Sd0InitSequenceSpec> for lib::Sd0InitSequence {
    fn from(value: Sd0InitSequenceSpec) -> Self {
        match value {
            Sd0InitSequenceSpec::EmmcThenSd => lib::Sd0InitSequence::EmmcThenSd,
            Sd0InitSequenceSpec::SdThenEmmc => lib::Sd0InitSequence::SdThenEmmc,
        }
    }
}

impl From<lib::Sd0InitSequence> for Sd0InitSequenceSpec {
    fn from(value: lib::Sd0InitSequence) -> Self {
        match value {
            lib::Sd0InitSequence::EmmcThenSd => Sd0InitSequenceSpec::EmmcThenSd,
            lib::Sd0InitSequence::SdThenEmmc => Sd0InitSequenceSpec::SdThenEmmc,
        }
    }
}