use crate::bladerf1::board::FlashSession;
use crate::error::{Error, Result};
use crate::maybe_future::Op;
use crate::usb::{UsbInterfaceCommands, VendorRequest};
use nusb::MaybeFuture;
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]) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
let chunk_size = self.chunk_size()?;
for (offset, chunk) in buf.chunks_exact_mut(chunk_size).enumerate() {
self.nios
.interface()
.usb_vendor_cmd_in_w_index_data(
VendorRequest::ReadPageBuffer,
(offset * chunk_size) as u16,
chunk,
)
.await?;
}
Ok(())
})
}
fn write_page_buffer(&mut self, buf: &[u8]) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
let chunk_size = self.chunk_size()?;
for (offset, chunk) in buf.chunks_exact(chunk_size).enumerate() {
self.nios
.interface()
.usb_vendor_cmd_out_w_index(
VendorRequest::WritePageBuffer,
(offset * chunk_size) as u16,
chunk,
)
.await?;
}
Ok(())
})
}
pub(crate) fn read_cal_cache(
&mut self,
buf: &mut [u8],
) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
let chunk_size = self.chunk_size()?;
for (offset, chunk) in buf.chunks_exact_mut(chunk_size).enumerate() {
self.nios
.interface()
.usb_vendor_cmd_in_w_index_data(
VendorRequest::ReadCalCache,
(offset * chunk_size) as u16,
chunk,
)
.await?;
}
Ok(())
})
}
pub fn read_page(
&mut self,
page: u32,
buf: &mut [u8],
) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
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
.interface()
.usb_vendor_cmd_int_w_index(VendorRequest::FlashRead, page as u16)
.await?;
self.read_page_buffer(buf).await
})
}
pub fn write_page(&mut self, page: u32, buf: &[u8]) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
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).await?;
self.nios
.interface()
.usb_vendor_cmd_int_w_index(VendorRequest::FlashWrite, page as u16)
.await?;
Ok(())
})
}
pub fn erase_sector(&mut self, sector: u32) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
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
.interface()
.usb_vendor_cmd_int_w_index(VendorRequest::FlashErase, sector as u16)
.await?;
Ok(())
})
}
pub fn read_pages(
&mut self,
page_start: u32,
page_count: usize,
buf: &mut [u8],
) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
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],
)
.await?;
}
Ok(())
})
}
pub fn write_pages(
&mut self,
page_start: u32,
page_count: usize,
buf: &[u8],
) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
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],
)
.await?;
}
Ok(())
})
}
pub fn erase_sectors(
&mut self,
start: u32,
count: u32,
) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
for sector in start..start + count {
self.erase_sector(sector).await?;
}
Ok(())
})
}
pub fn verify_pages(
&mut self,
page_start: u32,
expected: &[u8],
) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
for (page_idx, expected_page) in expected
.as_chunks::<BLADERF_FLASH_PAGE_SIZE>()
.0
.iter()
.enumerate()
{
let mut actual = [0u8; BLADERF_FLASH_PAGE_SIZE];
self.read_page(page_start + page_idx as u32, &mut actual)
.await?;
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
}
}