use std::{convert::TryInto, io};
use crate::{
constants::{
COMMAND_RET_FLASH_FAIL, COMMAND_RET_INVALID_ADR,
COMMAND_RET_INVALID_CMD, COMMAND_RET_SUCCESS, COMMAND_RET_UNKNOWN_CMD,
MAX_BYTES_PER_TRANSFER,
},
Device, Family,
};
pub const CCFG_SIZE: usize = 88;
pub const INVALID_ADDR: [u8; 8] =
[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
const REG32_SIZE: usize = 4;
const CC26XX_FLASH_O_FLASH_SIZE: u32 = 0x4003002C;
const CC26XX_FCFG1_O_MAC_15_4_0: u32 = 0x000002F0;
const CC2538_FLASH_CTRL_O_DIECFG0: u32 = 0x400D3014;
pub fn erase_flash_range<P, F>(
device: &mut Device<P>,
start_address: u32,
byte_count: u32,
mut progress: F,
) -> io::Result<()>
where
P: serial::SerialPort,
F: FnMut(f32, u32),
{
let family = device.family();
if family.supports_erase() {
progress(0.0, 0);
device.erase(start_address, byte_count)?;
progress(100.0, start_address + byte_count);
} else if family.supports_sector_erase() {
let sector_size = family.sector_size();
let sector_count = if (byte_count % sector_size) != 0 {
(byte_count / sector_size) + 1
} else {
byte_count / sector_size
};
for i in 0..sector_count {
let sector_address = start_address + (i * sector_size);
log::info!("Erasing sector #{}, address: {:#X}", i, sector_address);
progress((100.0 * i as f32) / sector_count as f32, sector_address);
device.sector_erase(sector_address)?;
let ret = device.get_status()?;
if ret != COMMAND_RET_SUCCESS {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"CMD_SECTOR_ERASE failed: `{}` ({:#X})",
status_code_to_str(ret),
ret
),
));
}
}
} else {
unreachable!();
}
Ok(())
}
#[derive(Debug)]
pub struct Transfer<'a> {
pub data: &'a [u8],
pub start_address: u32,
pub expect_ack: bool,
}
pub fn write_flash_range<'a, P, F>(
device: &mut Device<P>,
transfers: &[Transfer<'a>],
mut progress: F,
) -> io::Result<()>
where
P: serial::SerialPort,
F: FnMut(usize, f32, u32, u32),
{
let family = device.family();
log::info!("{} transfers", transfers.len());
for (txfer_index, transfer) in transfers.iter().enumerate() {
let chunks = transfer.data.len() / MAX_BYTES_PER_TRANSFER;
log::info!("Chunks for transfer #{}: {}", txfer_index, chunks);
device.download(
transfer.start_address,
transfer.data.len().try_into().unwrap(),
)?;
let ret = device.get_status()?;
if ret != COMMAND_RET_SUCCESS {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"CMD_DOWNLOAD failed: `{}` ({:#X})",
status_code_to_str(ret),
ret
),
));
}
let mut bytes_left = transfer.data.len();
let mut data_offset = 0;
let mut chunk_index = 0;
while bytes_left > 0 {
let bytes_in_transfer = MAX_BYTES_PER_TRANSFER.min(bytes_left);
let chunk = &transfer.data[data_offset..];
let chunk = &chunk[..bytes_in_transfer];
let chunk_addr = transfer.start_address + data_offset as u32;
log::info!(
"Writing chunk #{} ({} B) at address {:#X}",
chunk_index,
chunk.len(),
chunk_addr
);
progress(
txfer_index,
(100.0 * chunk_index as f32) / chunks as f32,
chunk_index,
chunk_addr,
);
let ack = device.send_data(&chunk)?;
if transfer.expect_ack {
if !ack {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Chunk #{} of size {} not acknowledged at address {:#X} (page: {}) at transfer #{}",
chunk_index, chunk.len(), chunk_addr, family.address_to_page(chunk_addr),
txfer_index,
)
));
}
let ret = device.get_status()?;
if ret != COMMAND_RET_SUCCESS {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"CMD_SEND_DATA failed: `{}` ({:#X})",
status_code_to_str(ret),
ret
),
));
}
}
bytes_left -= bytes_in_transfer;
data_offset += bytes_in_transfer;
chunk_index += 1;
}
}
Ok(())
}
pub fn read_flash_size<P>(device: &mut Device<P>) -> io::Result<u32>
where
P: serial::SerialPort,
{
let addr = match device.family() {
Family::CC2538 => CC2538_FLASH_CTRL_O_DIECFG0,
Family::CC26X0 | Family::CC26X2 => CC26XX_FLASH_O_FLASH_SIZE,
};
let mut reg = [0u8; REG32_SIZE];
device.memory_read_32(addr, &mut reg)?;
match device.family() {
Family::CC2538 => {
let flash_ctrl = u32::from_le_bytes(reg);
let flash_size = (flash_ctrl >> 4) & 0x07;
match flash_size {
1 => Ok(0x20000),
2 => Ok(0x40000),
3 => Ok(0x60000),
4 => Ok(0x80000),
0 => Ok(0x10000),
_ => Ok(0x10000),
}
}
Family::CC26X0 | Family::CC26X2 => {
let mut flash_size = u32::from_le_bytes(reg);
flash_size &= 0xFF;
Ok(flash_size * device.family().sector_size())
}
}
}
pub fn read_ieee_address<P>(
device: &mut Device<P>,
) -> io::Result<([u8; 8], [u8; 8])>
where
P: serial::SerialPort,
{
let primary_addr_offset = match device.family() {
Family::CC2538 => 0x00280028,
Family::CC26X0 | Family::CC26X2 => CC26XX_FCFG1_O_MAC_15_4_0,
};
let secondary_addr_offset = match device.family() {
Family::CC2538 => 0x0027ffcc,
Family::CC26X0 | Family::CC26X2 => {
let ccfg_offset = read_flash_size(device)? - CCFG_SIZE as u32;
ccfg_offset + 0x20
}
};
let mut primary = [0u8; 8];
device.memory_read_32(primary_addr_offset, &mut primary)?;
let mut secondary = [0u8; 8];
device.memory_read_32(secondary_addr_offset, &mut secondary)?;
Ok((primary, secondary))
}
pub fn status_code_to_str(ret: u8) -> &'static str {
match ret {
COMMAND_RET_SUCCESS => "COMMAND_RET_SUCCESS",
COMMAND_RET_UNKNOWN_CMD => "COMMAND_RET_UNKNOWN_CMD",
COMMAND_RET_INVALID_CMD => "COMMAND_RET_INVALID_CMD",
COMMAND_RET_INVALID_ADR => "COMMAND_RET_INVALID_ADR",
COMMAND_RET_FLASH_FAIL => "COMMAND_RET_FLASH_FAIL",
_ => "Unknown",
}
}