use super::error::{Error, ErrorKind};
use core::ops::{Range, RangeInclusive};
use core::slice::from_ref;
const ZONE_READWRITE_32: u8 = 0x80;
#[derive(Copy, Clone, Debug)]
pub enum Size {
Word = 0x04,
Block = 0x20,
}
impl Size {
pub(crate) fn len(&self) -> usize {
match self {
Self::Word => Self::Word as usize,
Self::Block => Self::Block as usize,
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum Zone {
Config = 0x00,
Data = 0x01,
Otp = 0x02,
}
impl Zone {
pub fn locate_index(index: usize) -> (u8, u8, u8) {
let block = index / Size::Block.len();
let offset = index % Size::Block.len() / Size::Word.len();
let position = index % Size::Word.len();
(block as u8, offset as u8, position as u8)
}
pub(crate) fn get_slot_addr(&self, slot: Slot, block: u8) -> Result<u16, Error> {
match self {
Self::Data if slot.is_private_key() && block == 0 => Ok((slot as u16) << 3),
Self::Data if slot.is_certificate() && block <= 2 => {
Ok((slot as u16) << 3 | (block as u16) << 8)
}
_ => Err(ErrorKind::BadParam.into()),
}
}
pub(crate) fn get_addr(&self, block: u8, offset: u8) -> Result<u16, Error> {
if block.leading_zeros() < 3 || offset.leading_zeros() < 4 {
return Err(ErrorKind::BadParam.into());
}
let block = (block as u16) << 3;
let offset = (offset & 0x07) as u16;
let addr = block | offset;
match self {
Self::Config | Self::Otp => Ok(addr),
Self::Data => Err(ErrorKind::BadParam.into()),
}
}
pub(crate) fn encode(&self, size: Size) -> u8 {
match size {
Size::Word => *self as u8,
Size::Block => *self as u8 | ZONE_READWRITE_32,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Slot {
PrivateKey00 = 0x00,
PrivateKey01 = 0x01,
PrivateKey02 = 0x02,
PrivateKey03 = 0x03,
PrivateKey04 = 0x04,
PrivateKey05 = 0x05,
PrivateKey06 = 0x06,
PrivateKey07 = 0x07,
Data08 = 0x08,
Certificate09 = 0x09,
Certificate0a = 0x0a,
Certificate0b = 0x0b,
Certificate0c = 0x0c,
Certificate0d = 0x0d,
Certificate0e = 0x0e,
Certificate0f = 0x0f,
}
impl Slot {
pub fn is_private_key(&self) -> bool {
*self <= Self::PrivateKey07
}
pub fn is_certificate(&self) -> bool {
Self::Certificate09 <= *self
}
pub fn keys() -> KeysIter {
KeysIter(0x00..=0x0f)
}
}
pub struct KeysIter(RangeInclusive<usize>);
impl Iterator for KeysIter {
type Item = Slot;
fn next(&mut self) -> Option<Self::Item> {
use Slot::*;
self.0.next().and_then(|i| match i {
x if x == PrivateKey00 as usize => PrivateKey00.into(),
x if x == PrivateKey01 as usize => PrivateKey01.into(),
x if x == PrivateKey02 as usize => PrivateKey02.into(),
x if x == PrivateKey03 as usize => PrivateKey03.into(),
x if x == PrivateKey04 as usize => PrivateKey04.into(),
x if x == PrivateKey05 as usize => PrivateKey05.into(),
x if x == PrivateKey06 as usize => PrivateKey06.into(),
x if x == PrivateKey07 as usize => PrivateKey07.into(),
x if x == Data08 as usize => Data08.into(),
x if x == Certificate09 as usize => Certificate09.into(),
x if x == Certificate0a as usize => Certificate0a.into(),
x if x == Certificate0b as usize => Certificate0b.into(),
x if x == Certificate0c as usize => Certificate0c.into(),
x if x == Certificate0d as usize => Certificate0d.into(),
x if x == Certificate0e as usize => Certificate0e.into(),
x if x == Certificate0f as usize => Certificate0f.into(),
_ => None,
})
}
}
pub(crate) struct CertificateRepr(RangeInclusive<usize>);
impl CertificateRepr {
pub(crate) fn new() -> Self {
Self(0..=2)
}
}
impl Iterator for CertificateRepr {
type Item = &'static [Range<usize>];
fn next(&mut self) -> Option<Self::Item> {
self.0.next().and_then(|i| match i {
0 => from_ref(&(0x04..0x20)).into(),
1 => [0x00..0x04, 0x08..0x20].as_ref().into(),
2 => from_ref(&(0x00..0x08)).into(),
_ => None,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::Memory;
use core::convert::identity;
use core::iter::repeat;
use heapless::Vec;
use Slot::*;
use Zone::*;
#[test]
fn locate_index() {
assert_eq!(
(0, 5, 0),
Zone::locate_index(Memory::<(), ()>::SLOT_CONFIG_INDEX)
);
assert_eq!(
(2, 6, 2),
Zone::locate_index(Memory::<(), ()>::CHIP_OPTIONS_INDEX)
);
assert_eq!(
(3, 0, 0),
Zone::locate_index(Memory::<(), ()>::KEY_CONFIG_INDEX)
);
}
#[test]
fn get_slot_addr() {
assert_eq!(0x0038, Data.get_slot_addr(PrivateKey07, 0).unwrap());
for (&addr, block) in [0x0078, 0x0178, 0x0278].iter().zip(0..=2) {
let result = Data.get_slot_addr(Certificate0f, block).unwrap();
assert_eq!(addr, result);
}
}
#[test]
fn get_addr() {
assert_eq!(0x0005, Config.get_addr(0, 5).unwrap());
assert_eq!(0x0016, Config.get_addr(2, 6).unwrap());
assert_eq!(0x0018, Config.get_addr(3, 0).unwrap());
}
#[test]
fn certificate_representation() {
let slot_buffer = repeat(0)
.take(4)
.chain(0x00..0x20)
.chain(repeat(0).take(4))
.chain(0x20..0x40)
.chain(repeat(0).take(24))
.collect::<Vec<u8, 96>>();
assert_eq!(Size::Block.len() * 3, slot_buffer.len());
slot_buffer
.chunks(Size::Block.len())
.zip(CertificateRepr::new())
.scan(0, |i, (chunk, ranges)| {
for range in ranges {
for j in &chunk[range.clone()] {
assert_eq!(i, j);
*i += 1;
}
}
Some(())
})
.for_each(identity)
}
}