libbladerf-rs 0.3.0

Fully Rust native BladeRF driver
use crate::bladerf1::nios_client::NiosClient;
use crate::error::{Error, Result};
use crate::flash::{
    BLADERF_FLASH_PAGE_SIZE, BLADERF_FLASH_TOTAL_PAGES, BLADERF_FLASH_TOTAL_SECTORS,
};
use crate::transport::usb::{
    BLADE_USB_CMD_FLASH_ERASE, BLADE_USB_CMD_FLASH_READ, BLADE_USB_CMD_FLASH_WRITE,
    BLADE_USB_CMD_READ_CAL_CACHE, BLADE_USB_CMD_READ_PAGE_BUFFER, BLADE_USB_CMD_WRITE_PAGE_BUFFER,
    USB_IF_CONFIG, USB_IF_RF_LINK, USB_IF_SPI_FLASH, UsbInterfaceCommands,
};
use nusb::Speed;

pub fn get_chunk_size(speed: Speed) -> Result<usize> {
    match speed {
        Speed::Super | Speed::SuperPlus => Ok(BLADERF_FLASH_PAGE_SIZE),
        Speed::High => Ok(64),
        _ => Err(Error::UnsupportedSpeed),
    }
}

pub fn with_flash_mode<T>(
    nios: &mut NiosClient,
    f: impl FnOnce(&mut NiosClient) -> Result<T>,
) -> Result<T> {
    nios.usb_change_setting(USB_IF_SPI_FLASH)?;
    let result = f(nios);
    let restore_setting = match nios.nios_config_read() {
        Ok(gpio) if gpio != 0 => USB_IF_RF_LINK,
        _ => USB_IF_CONFIG,
    };
    if let Err(e) = nios.usb_change_setting(restore_setting) {
        log::warn!("Failed to restore USB alt setting after flash mode: {e:#}");
    }
    result
}

fn flash_cmd(nios: &mut NiosClient, cmd: u8, w_index: u16) -> Result<()> {
    let status = nios.usb_vendor_cmd_int_w_index(cmd, w_index)?;
    if status != 0 {
        Err(Error::FlashError(status))
    } else {
        Ok(())
    }
}

fn write_chunks(
    nios: &mut NiosClient,
    chunk_size: usize,
    buf: &[u8; BLADERF_FLASH_PAGE_SIZE],
) -> Result<()> {
    for offset in (0..BLADERF_FLASH_PAGE_SIZE).step_by(chunk_size) {
        let end = (offset + chunk_size).min(BLADERF_FLASH_PAGE_SIZE);
        nios.usb_vendor_cmd_out_w_index(
            BLADE_USB_CMD_WRITE_PAGE_BUFFER,
            offset as u16,
            &buf[offset..end],
        )?;
    }
    Ok(())
}

fn read_chunks(
    nios: &mut NiosClient,
    chunk_size: usize,
    cmd: u8,
    buf: &mut [u8; BLADERF_FLASH_PAGE_SIZE],
) -> Result<()> {
    for offset in (0..BLADERF_FLASH_PAGE_SIZE).step_by(chunk_size) {
        let end = (offset + chunk_size).min(BLADERF_FLASH_PAGE_SIZE);
        nios.usb_vendor_cmd_in_w_index_data(cmd, offset as u16, &mut buf[offset..end])?;
    }
    Ok(())
}

pub fn erase_sector(nios: &mut NiosClient, _chunk_size: usize, sector: u16) -> Result<()> {
    if sector >= BLADERF_FLASH_TOTAL_SECTORS {
        return Err(Error::Argument(format!(
            "flash sector {sector} out of range (0..{BLADERF_FLASH_TOTAL_SECTORS})"
        )));
    }
    with_flash_mode(nios, |nios| {
        flash_cmd(nios, BLADE_USB_CMD_FLASH_ERASE, sector)
    })
}

pub fn read_page(
    nios: &mut NiosClient,
    chunk_size: usize,
    page: u16,
    buf: &mut [u8; BLADERF_FLASH_PAGE_SIZE],
) -> Result<()> {
    if page >= BLADERF_FLASH_TOTAL_PAGES {
        return Err(Error::Argument(format!(
            "flash page {page} out of range (0..{BLADERF_FLASH_TOTAL_PAGES})"
        )));
    }
    with_flash_mode(nios, |nios| {
        flash_cmd(nios, BLADE_USB_CMD_FLASH_READ, page)?;
        read_chunks(nios, chunk_size, BLADE_USB_CMD_READ_PAGE_BUFFER, buf)
    })
}

pub fn write_page(
    nios: &mut NiosClient,
    chunk_size: usize,
    page: u16,
    buf: &[u8; BLADERF_FLASH_PAGE_SIZE],
) -> Result<()> {
    if page >= BLADERF_FLASH_TOTAL_PAGES {
        return Err(Error::Argument(format!(
            "flash page {page} out of range (0..{BLADERF_FLASH_TOTAL_PAGES})"
        )));
    }
    with_flash_mode(nios, |nios| {
        write_chunks(nios, chunk_size, buf)?;
        flash_cmd(nios, BLADE_USB_CMD_FLASH_WRITE, page)
    })
}

pub fn erase_and_write_page(
    nios: &mut NiosClient,
    chunk_size: usize,
    sector: u16,
    page: u16,
    buf: &[u8; BLADERF_FLASH_PAGE_SIZE],
) -> Result<()> {
    if sector >= BLADERF_FLASH_TOTAL_SECTORS {
        return Err(Error::Argument(format!(
            "flash sector {sector} out of range (0..{BLADERF_FLASH_TOTAL_SECTORS})"
        )));
    }
    if page >= BLADERF_FLASH_TOTAL_PAGES {
        return Err(Error::Argument(format!(
            "flash page {page} out of range (0..{BLADERF_FLASH_TOTAL_PAGES})"
        )));
    }
    with_flash_mode(nios, |nios| {
        flash_cmd(nios, BLADE_USB_CMD_FLASH_ERASE, sector)?;
        write_chunks(nios, chunk_size, buf)?;
        flash_cmd(nios, BLADE_USB_CMD_FLASH_WRITE, page)
    })
}

pub fn read_cal_cache(
    nios: &mut NiosClient,
    chunk_size: usize,
    buf: &mut [u8; BLADERF_FLASH_PAGE_SIZE],
) -> Result<()> {
    with_flash_mode(nios, |nios| {
        read_chunks(nios, chunk_size, BLADE_USB_CMD_READ_CAL_CACHE, buf)
    })
}