use core::slice::from_raw_parts; use heapless::{String, Vec}; const SECTOR_SIZE: usize = 4096; // First 6MB is for code const FLASH_START: usize = 0x10000000 + 6144 * 1024; const CONTACT_HEADER_START: usize = FLASH_START; const CONTACT_HEADER_SIZE: usize = 3; pub const MAX_CONTACTS: usize = 100; pub const MAX_CONTACT_NAME_LENGTH: usize = 20; pub const MAX_CONTACT_CALLSIGN_LENGTH: usize = 10; // 1 byte for each length, 1 byte for SSID, 2 bytes for checksum const CONTACT_LENGTH: usize = MAX_CONTACT_NAME_LENGTH + 1 + 1 + MAX_CONTACT_CALLSIGN_LENGTH + 1 + 2; const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); pub struct Storage { contact_header: ContactHeader, } impl Storage { pub fn new() -> Self { Self { // TODO need to invalidate when writing contacts contact_header: ContactHeader::read(), } } pub fn save_contact(&mut self, contact: Contact) { let len = self.contact_header.num_contacts; // if id isn't less than the len, we're inserting a new contact if contact.id >= usize::from(len) { assert!(usize::from(len) < MAX_CONTACTS); self.contact_header.num_contacts += 1; self.contact_header.write(&self); } contact.write(&self); } pub fn contacts(&self) -> ContactIter { ContactIter::new(self.contact_header.num_contacts.into()) } pub fn contacts_len(&self) -> usize { self.contact_header.num_contacts.into() } // Note - calling this twice without saving one // will cause a duplicate ID // We don't increment the len until the contact is actually saved pub fn new_contact(&self) -> Contact { Contact { callsign: String::new(), ssid: 0, name: String::new(), id: usize::from(self.contact_header.num_contacts), } } fn write_data(&self, start_addr: usize, buf: &[u8]) { todo!() } } // Up to 100 contacts // 2 byte CRC checksum struct ContactHeader { num_contacts: u8, } impl ContactHeader { fn read() -> Self { let bytes = unsafe { from_raw_parts(CONTACT_HEADER_START as *mut u8, CONTACT_HEADER_SIZE) }; let num_contacts = bytes[0]; let checksum_expected = u16::from_le_bytes([bytes[1], bytes[2]]); // CRC checksum is very overkill right now // But we may want to add more fields in the future let checksum_actual = X25.checksum(&[num_contacts]); if checksum_expected == checksum_actual && usize::from(num_contacts) < MAX_CONTACTS { Self { num_contacts } } else { Self { num_contacts: 0 } } } fn write(&self, storage: &Storage) { let mut buf = [0; CONTACT_HEADER_SIZE]; buf[0] = self.num_contacts; let checksum = X25.checksum(&[self.num_contacts]); buf[1..].copy_from_slice(&checksum.to_le_bytes()); storage.write_data(CONTACT_HEADER_START, &buf); } } #[derive(Clone)] pub struct Contact { pub callsign: String<MAX_CONTACT_CALLSIGN_LENGTH>, pub ssid: u8, pub name: String<MAX_CONTACT_NAME_LENGTH>, id: usize, } impl Contact { fn read(idx: usize) -> Option<Self> { let start_addr = (CONTACT_HEADER_START + CONTACT_HEADER_SIZE + idx * CONTACT_LENGTH) as *mut u8; let bytes = unsafe { from_raw_parts(start_addr, CONTACT_LENGTH) }; let checksum_expected = u16::from_le_bytes([bytes[CONTACT_LENGTH - 2], bytes[CONTACT_LENGTH - 1]]); let checksum_actual = X25.checksum(&bytes[..(CONTACT_LENGTH - 2)]); if checksum_expected != checksum_actual { return None; } let callsign_len: usize = bytes[0].into(); let name_len: usize = bytes[1].into(); let ssid = bytes[2]; if callsign_len > MAX_CONTACT_CALLSIGN_LENGTH || name_len > MAX_CONTACT_NAME_LENGTH { return None; } let callsign_bytes = &bytes[3..callsign_len]; let name_bytes = &bytes[(3 + MAX_CONTACT_NAME_LENGTH)..(3 + MAX_CONTACT_NAME_LENGTH + name_len)]; let callsign = String::from_utf8(Vec::from_slice(callsign_bytes).ok()?).ok()?; let name = String::from_utf8(Vec::from_slice(name_bytes).ok()?).ok()?; Some(Self { callsign, ssid, name, id: idx, }) } fn write(&self, storage: &Storage) { let start_addr = CONTACT_HEADER_START + CONTACT_HEADER_SIZE + self.id * CONTACT_LENGTH; let mut buf = [0; CONTACT_LENGTH]; buf[0] = self.callsign.len().try_into().unwrap(); buf[1] = self.name.len().try_into().unwrap(); buf[2] = self.ssid; let callsign_bytes = self.callsign.as_bytes(); let name_bytes = self.name.as_bytes(); buf[3..(3 + callsign_bytes.len())].copy_from_slice(&callsign_bytes); buf[(3 + MAX_CONTACT_NAME_LENGTH)..(3 + MAX_CONTACT_NAME_LENGTH + name_bytes.len())] .copy_from_slice(name_bytes); let checksum = X25.checksum(&buf[..(CONTACT_LENGTH - 2)]); buf[(CONTACT_LENGTH - 2)..].copy_from_slice(&checksum.to_le_bytes()); storage.write_data(start_addr, &buf); } } #[derive(Copy, Clone)] pub struct ContactIter { i: usize, len: usize, } impl ContactIter { fn new(len: usize) -> Self { Self { i: 0, len } } } impl Iterator for ContactIter { type Item = Contact; fn next(&mut self) -> Option<Self::Item> { while self.i < self.len { let c = Contact::read(self.i); self.i += 1; if let Some(c) = c { return Some(c); } } None } }