use crate::bladerf1::board::FlashSession;
use crate::error::{Error, Result};
use crate::usb::{UsbInterfaceCommands, VendorRequest};
use nusb::Speed;
pub const BLADERF_FLASH_PAGE_SIZE: usize = 256;
pub const BLADERF_FLASH_ERASE_BLOCK_SIZE: usize = 64 * 1_024;
pub const BLADERF_FLASH_ADDR_FIRMWARE: u32 = 0x00000000;
pub const BLADERF_FLASH_BYTE_LEN_FIRMWARE: u32 = 0x00030000;
pub const BLADERF_FLASH_ADDR_CAL: u32 = 0x00030000;
pub const BLADERF_FLASH_BYTE_LEN_CAL: usize = 0x100;
pub const BLADERF_FLASH_ADDR_FPGA: u32 = 0x00040000;
pub(crate) struct FlashMeta {
pub flash_size_bytes: u32,
pub total_pages: u32,
pub total_sectors: u32,
}
impl FlashSession<'_> {
fn chunk_size(&self) -> Result<usize> {
match self.nios.transport().speed() {
Speed::Super | Speed::SuperPlus => Ok(BLADERF_FLASH_PAGE_SIZE),
Speed::High => Ok(64),
_ => Err(Error::UnsupportedSpeed),
}
}
fn read_page_buffer(&mut self, buf: &mut [u8]) -> Result<()> {
let chunk_size = self.chunk_size()?;
for (offset, chunk) in buf.chunks_exact_mut(chunk_size).enumerate() {
self.nios.usb_vendor_cmd_in_w_index_data(
VendorRequest::ReadPageBuffer,
(offset * chunk_size) as u16,
chunk,
)?;
}
Ok(())
}
fn write_page_buffer(&mut self, buf: &[u8]) -> Result<()> {
let chunk_size = self.chunk_size()?;
for (offset, chunk) in buf.chunks_exact(chunk_size).enumerate() {
self.nios.usb_vendor_cmd_out_w_index(
VendorRequest::WritePageBuffer,
(offset * chunk_size) as u16,
chunk,
)?;
}
Ok(())
}
pub(crate) fn read_cal_cache(&mut self, buf: &mut [u8]) -> Result<()> {
let chunk_size = self.chunk_size()?;
for (offset, chunk) in buf.chunks_exact_mut(chunk_size).enumerate() {
self.nios.usb_vendor_cmd_in_w_index_data(
VendorRequest::ReadCalCache,
(offset * chunk_size) as u16,
chunk,
)?;
}
Ok(())
}
pub fn read_page(&mut self, page: u32, buf: &mut [u8]) -> Result<()> {
let total_pages = self.flash_meta.total_pages;
if page >= total_pages {
return Err(Error::Argument(format!(
"flash page {page} out of range (0..{total_pages})"
)));
}
self.nios
.usb_vendor_cmd_int_w_index(VendorRequest::FlashRead, page as u16)?;
self.read_page_buffer(buf)
}
pub fn write_page(&mut self, page: u32, buf: &[u8]) -> Result<()> {
let total_pages = self.flash_meta.total_pages;
if page >= total_pages {
return Err(Error::Argument(format!(
"flash page {page} out of range (0..{total_pages})"
)));
}
self.write_page_buffer(buf)?;
self.nios
.usb_vendor_cmd_int_w_index(VendorRequest::FlashWrite, page as u16)?;
Ok(())
}
pub fn erase_sector(&mut self, sector: u32) -> Result<()> {
let total_sectors = self.flash_meta.total_sectors;
if sector >= total_sectors {
return Err(Error::Argument(format!(
"flash sector {sector} out of range (0..{total_sectors})"
)));
}
self.nios
.usb_vendor_cmd_int_w_index(VendorRequest::FlashErase, sector as u16)?;
Ok(())
}
pub fn read_pages(&mut self, page_start: u32, page_count: usize, buf: &mut [u8]) -> Result<()> {
let required = page_count * BLADERF_FLASH_PAGE_SIZE;
if buf.len() < required {
return Err(Error::Argument(format!(
"buffer too small: {required} bytes required, {} provided",
buf.len()
)));
}
for page_idx in 0..page_count {
let offset = page_idx * BLADERF_FLASH_PAGE_SIZE;
self.read_page(
page_start + page_idx as u32,
&mut buf[offset..offset + BLADERF_FLASH_PAGE_SIZE],
)?;
}
Ok(())
}
pub fn write_pages(&mut self, page_start: u32, page_count: usize, buf: &[u8]) -> Result<()> {
let required = page_count * BLADERF_FLASH_PAGE_SIZE;
if buf.len() < required {
return Err(Error::Argument(format!(
"buffer too small: {required} bytes required, {} provided",
buf.len()
)));
}
for page_idx in 0..page_count {
let offset = page_idx * BLADERF_FLASH_PAGE_SIZE;
self.write_page(
page_start + page_idx as u32,
&buf[offset..offset + BLADERF_FLASH_PAGE_SIZE],
)?;
}
Ok(())
}
pub fn erase_sectors(&mut self, start: u32, count: u32) -> Result<()> {
for sector in start..start + count {
self.erase_sector(sector)?;
}
Ok(())
}
pub fn verify_pages(&mut self, page_start: u32, expected: &[u8]) -> Result<()> {
for (page_idx, expected_page) in expected.chunks_exact(BLADERF_FLASH_PAGE_SIZE).enumerate()
{
let mut actual = [0u8; BLADERF_FLASH_PAGE_SIZE];
self.read_page(page_start + page_idx as u32, &mut actual)?;
if expected_page != actual {
let local = expected_page
.iter()
.zip(&actual)
.position(|(e, a)| e != a)
.unwrap();
return Err(Error::FlashVerificationFailed {
byte_offset: page_idx * BLADERF_FLASH_PAGE_SIZE + local,
expected: expected_page[local],
actual: actual[local],
});
}
}
Ok(())
}
pub fn size_bytes(&self) -> u32 {
self.flash_meta.flash_size_bytes
}
pub fn total_pages(&self) -> u32 {
self.flash_meta.total_pages
}
pub fn total_sectors(&self) -> u32 {
self.flash_meta.total_sectors
}
pub fn fpga_flash_sectors(&self) -> u32 {
self.flash_meta.total_sectors
- BLADERF_FLASH_ADDR_FPGA / BLADERF_FLASH_ERASE_BLOCK_SIZE as u32
}
pub fn fpga_flash_bytes(&self) -> usize {
self.fpga_flash_sectors() as usize * BLADERF_FLASH_ERASE_BLOCK_SIZE
}
}