use std::fmt;
use std::fmt::Debug;
use crate::error::{Error, Result};
pub mod control;
pub trait Command: Debug {
type Response: Response;
const COMMAND_ID: u8;
fn payload(&self) -> Vec<u8>;
fn to_raw(&self) -> Vec<u8> {
let mut bytes = vec![0x81, Self::COMMAND_ID, 0x00];
bytes.extend(self.payload());
bytes[2] = bytes.len() as u8 - 3;
bytes
}
}
pub trait Response {
fn from_payload(bytes: &[u8]) -> Result<Self>
where
Self: Sized;
fn from_raw(resp: &[u8]) -> Result<Self>
where
Self: Sized,
{
if resp[0] == 0x81 {
let reason = resp[1];
let len = resp[2] as usize;
if len != resp[3..].len() {
return Err(Error::InvalidPayloadLength);
}
if reason == 0x55 {
return Err(Error::Protocol(reason, resp.to_vec()));
}
Err(Error::Protocol(reason, resp.to_vec()))
} else if resp[0] == 0x82 {
let len = resp[2] as usize;
if len != resp[3..].len() {
return Err(Error::InvalidPayloadLength);
}
let payload = resp[3..3 + len].to_vec();
Self::from_payload(&payload)
} else {
Err(Error::InvalidPayload)
}
}
}
impl Response for () {
fn from_payload(_bytes: &[u8]) -> Result<Self> {
Ok(())
}
}
impl Response for Vec<u8> {
fn from_payload(bytes: &[u8]) -> Result<Self> {
Ok(bytes.to_vec())
}
}
impl Response for u8 {
fn from_payload(bytes: &[u8]) -> Result<Self> {
if bytes.len() != 1 {
return Err(Error::InvalidPayloadLength);
}
Ok(bytes[0])
}
}
pub struct RawCommand<const N: u8>(pub Vec<u8>);
impl<const N: u8> Command for RawCommand<N> {
type Response = Vec<u8>;
const COMMAND_ID: u8 = N;
fn payload(&self) -> Vec<u8> {
self.0.clone()
}
}
impl<const N: u8> fmt::Debug for RawCommand<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RawCommand<0x{:02x}>({})", N, hex::encode(&self.0))
}
}
#[derive(Debug)]
pub struct SetWriteMemoryRegion {
pub start_addr: u32,
pub len: u32,
}
impl Command for SetWriteMemoryRegion {
type Response = ();
const COMMAND_ID: u8 = 0x01;
fn payload(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(8);
bytes.extend_from_slice(&self.start_addr.to_be_bytes());
bytes.extend_from_slice(&self.len.to_be_bytes());
bytes
}
}
#[derive(Debug)]
pub struct SetReadMemoryRegion {
pub start_addr: u32,
pub len: u32,
}
impl Command for SetReadMemoryRegion {
type Response = ();
const COMMAND_ID: u8 = 0x03;
fn payload(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(8);
bytes.extend_from_slice(&self.start_addr.to_be_bytes());
bytes.extend_from_slice(&self.len.to_be_bytes());
bytes
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Program {
EraseFlash = 0x01,
WriteFlash = 0x02,
WriteFlashAndVerify = 0x04,
WriteFlashOP = 0x05,
Prepare = 0x06,
Unknown07AfterFlashOPWritten = 0x07, Unknown0BAfterFlashOPWritten = 0x0B,
End = 0x08,
ReadMemory = 0x0c,
}
impl Command for Program {
type Response = u8;
const COMMAND_ID: u8 = 0x02;
fn payload(&self) -> Vec<u8> {
vec![*self as u8]
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ConfigChip {
CheckReadProtect, Unprotect,
Protect,
CheckReadProtectEx, UnprotectEx(u8), ProtectEx(u8), Config {
data: u16,
wrp: u32,
},
}
impl ConfigChip {
pub const FLAG_READ_PROTECTED: u8 = 0x01;
pub const FLAG_WRITE_PROTECTED: u8 = 0x11;
}
impl Command for ConfigChip {
type Response = u8;
const COMMAND_ID: u8 = 0x06;
fn payload(&self) -> Vec<u8> {
match *self {
ConfigChip::CheckReadProtect => vec![0x01],
ConfigChip::Unprotect => vec![0x02],
ConfigChip::Protect => vec![0x03],
ConfigChip::CheckReadProtectEx => vec![0x04],
ConfigChip::UnprotectEx(b) => vec![0x02, b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
ConfigChip::ProtectEx(b) => vec![0x03, b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
ConfigChip::Config { data: _, wrp: _ } => todo!("ConfigChip: config flags"),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum GetChipInfo {
V1 = 0x09,
V2 = 0x06,
}
impl Command for GetChipInfo {
type Response = ESignature;
const COMMAND_ID: u8 = 0x11;
fn payload(&self) -> Vec<u8> {
vec![*self as u8]
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct ESignature {
pub flash_size_kb: u16,
pub uid: [u32; 2],
}
impl Response for ESignature {
fn from_payload(_bytes: &[u8]) -> Result<Self>
where
Self: Sized,
{
unreachable!("ESignature is not be parsed from payload; qed")
}
fn from_raw(resp: &[u8]) -> Result<Self> {
if resp.len() < 12 {
return Err(Error::InvalidPayloadLength);
}
let flash_size_kb = u16::from_be_bytes(resp[2..4].try_into().unwrap());
let uid = [
u32::from_be_bytes(resp[4..8].try_into().unwrap()),
u32::from_be_bytes(resp[8..12].try_into().unwrap()),
];
Ok(Self { flash_size_kb, uid })
}
}
impl fmt::Display for ESignature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let bytes: [u8; 8] = unsafe { std::mem::transmute(self.uid) };
write!(
f,
"FlashSize({}KB) UID({})",
self.flash_size_kb,
&bytes
.iter()
.map(|b| format!("{:02x}", b))
.collect::<Vec<_>>()
.join("-")
)
}
}
#[derive(Debug)]
pub enum Reset {
Soft, Normal,
Chip,
}
impl Command for Reset {
type Response = ();
const COMMAND_ID: u8 = 0x0b;
fn payload(&self) -> Vec<u8> {
match self {
Reset::Soft => vec![0x01],
Reset::Normal => vec![0x03],
Reset::Chip => vec![0x02],
}
}
}
#[derive(Debug, Copy, Clone, clap::ValueEnum, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub enum Speed {
Low = 0x03,
Medium = 0x02,
#[default]
High = 0x01,
}
#[derive(Debug)]
pub struct SetSpeed {
pub riscvchip: u8,
pub speed: Speed,
}
impl Command for SetSpeed {
type Response = bool;
const COMMAND_ID: u8 = 0x0c;
fn payload(&self) -> Vec<u8> {
vec![self.riscvchip, self.speed as u8]
}
}
impl Response for bool {
fn from_payload(resp: &[u8]) -> Result<Self> {
if resp.len() != 1 {
return Err(Error::InvalidPayloadLength);
}
Ok(resp[0] == 0x01) }
}
#[derive(Debug)]
pub enum DmiOp {
Nop,
Read { addr: u8 },
Write { addr: u8, data: u32 },
}
impl DmiOp {
pub fn read(addr: u8) -> Self {
DmiOp::Read { addr }
}
pub fn write(addr: u8, data: u32) -> Self {
DmiOp::Write { addr, data }
}
}
impl Command for DmiOp {
type Response = DmiOpResponse;
const COMMAND_ID: u8 = 0x08;
fn payload(&self) -> Vec<u8> {
const DMI_OP_NOP: u8 = 0;
const DMI_OP_READ: u8 = 1;
const DMI_OP_WRITE: u8 = 2;
let mut bytes = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
match self {
DmiOp::Nop => {
bytes[5] = DMI_OP_NOP; }
DmiOp::Read { addr } => {
bytes[0] = *addr;
bytes[5] = DMI_OP_READ;
}
DmiOp::Write { addr, data } => {
bytes[0] = *addr;
bytes[5] = DMI_OP_WRITE;
bytes[1..5].copy_from_slice(&data.to_be_bytes());
}
}
bytes
}
}
#[derive(Debug)]
pub struct DmiOpResponse {
pub addr: u8,
pub data: u32,
pub op: u8,
}
impl DmiOpResponse {
pub fn is_busy(&self) -> bool {
self.op == 0x03
}
pub fn is_success(&self) -> bool {
self.op == 0x00
}
pub fn is_failed(&self) -> bool {
self.op == 0x03 || self.op == 0x02
}
}
impl Response for DmiOpResponse {
fn from_payload(bytes: &[u8]) -> Result<Self> {
if bytes.len() != 6 {
return Err(Error::InvalidPayloadLength);
}
let addr = bytes[0];
let op = bytes[5];
let data = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
Ok(DmiOpResponse { addr, data, op })
}
}
#[derive(Debug)]
pub struct DisableDebug;
impl Command for DisableDebug {
type Response = ();
const COMMAND_ID: u8 = 0x0e;
fn payload(&self) -> Vec<u8> {
vec![0x01]
}
}