libbladerf-rs 0.3.0

Fully Rust native BladeRF driver
use crate::bladerf1::BladeRf1;
use crate::bladerf1::board::xb;
use crate::channel::Channel;
use crate::error::{Error, Result};
use std::ops::RangeInclusive;
pub(crate) const BLADERF_XB_CONFIG_TX_PATH_MIX: u32 = 0x04;
pub(crate) const BLADERF_XB_CONFIG_TX_PATH_BYPASS: u32 = 0x08;
pub(crate) const BLADERF_XB_CONFIG_TX_BYPASS: u32 = 0x04;
pub(crate) const BLADERF_XB_CONFIG_TX_BYPASS_MASK: u32 = 0x0C;
pub(crate) const BLADERF_XB_CONFIG_RX_PATH_MIX: u32 = 0x10;
pub(crate) const BLADERF_XB_CONFIG_RX_PATH_BYPASS: u32 = 0x20;
pub(crate) const BLADERF_XB_CONFIG_RX_BYPASS: u32 = 0x10;
pub(crate) const BLADERF_XB_CONFIG_RX_BYPASS_MASK: u32 = 0x30;
pub(crate) const BLADERF_XB_RF_ON: u32 = 0x0800;
pub(crate) const BLADERF_XB_TX_ENABLE: u32 = 0x1000;
pub(crate) const BLADERF_XB_RX_ENABLE: u32 = 0x2000;
pub(crate) const BLADERF_XB_TX_MASK: u32 = 0x0C000000;
pub(crate) const BLADERF_XB_TX_SHIFT: u32 = 26;
pub(crate) const BLADERF_XB_RX_MASK: u32 = 0x30000000;
pub(crate) const BLADERF_XB_RX_SHIFT: u32 = 28;
pub(crate) const LMS_RX_SWAP: u8 = 0x40;
pub(crate) const LMS_TX_SWAP: u8 = 0x08;
type FilterEntry = (RangeInclusive<u64>, Xb200Filter);
pub(crate) const AUTO_1DB_FILTERS: &[FilterEntry] = &[
    (37_774_405..=59_535_436, Xb200Filter::_50M),
    (128_326_173..=166_711_171, Xb200Filter::_144M),
    (187_593_160..=245_346_403, Xb200Filter::_222M),
];
pub(crate) const AUTO_3DB_FILTERS: &[FilterEntry] = &[
    (34_782_924..=61_899_260, Xb200Filter::_50M),
    (121_956_957..=178_444_099, Xb200Filter::_144M),
    (177_522_675..=260_140_935, Xb200Filter::_222M),
];
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Xb200Filter {
    _50M = 0,
    _144M = 1,
    _222M = 2,
    Custom = 3,
    Auto1db = 4,
    Auto3db = 5,
}
impl TryFrom<u32> for Xb200Filter {
    type Error = Error;
    fn try_from(value: u32) -> Result<Self> {
        match value {
            0 => Ok(Xb200Filter::_50M),
            1 => Ok(Xb200Filter::_144M),
            2 => Ok(Xb200Filter::_222M),
            3 => Ok(Xb200Filter::Custom),
            4 => Ok(Xb200Filter::Auto1db),
            5 => Ok(Xb200Filter::Auto3db),
            _ => {
                log::error!("invalid filter selection!");
                Err(Error::Argument("invalid XB200 filter value".into()))
            }
        }
    }
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Xb200Path {
    Bypass = 0,
    Mix = 1,
}
impl BladeRf1 {
    pub fn xb200_attach(&mut self) -> Result<()> {
        let muxout: usize = 6;
        let mux_lut = [
            "THREE-STATE OUTPUT",
            "DVdd",
            "DGND",
            "R COUNTER OUTPUT",
            "N DIVIDER OUTPUT",
            "ANALOG LOCK DETECT",
            "DIGITAL LOCK DETECT",
            "RESERVED",
        ];
        log::trace!("Attaching XB200 transverter board");
        let mut val8 = crate::bladerf1::hardware::si5338::read(&mut self.nios, 39)?;
        log::trace!("[xb200_attach] si5338_read: {val8}");
        val8 |= 2;
        crate::bladerf1::hardware::si5338::write(&mut self.nios, 39, val8)?;
        crate::bladerf1::hardware::si5338::write(&mut self.nios, 34, 0x22)?;
        let mut val = self.config_gpio_read()?;
        val |= 0x80000000;
        self.config_gpio_write(val)?;
        self.nios
            .nios_expansion_gpio_dir_write(0xffffffff, 0x3C00383E)?;
        self.nios.nios_expansion_gpio_write(0xffffffff, 0x800)?;
        self.nios.nios_xb200_synth_write(0x580005)?;
        self.nios.nios_xb200_synth_write(0x99A16C)?;
        self.nios.nios_xb200_synth_write(0xC004B3)?;
        log::trace!("MUXOUT: {}", mux_lut[muxout]);
        let value = 0x60008E42 | (1 << 8) | ((muxout as u32) << 26);
        self.nios.nios_xb200_synth_write(value)?;
        self.nios.nios_xb200_synth_write(0x08008011)?;
        self.nios.nios_xb200_synth_write(0x00410000)?;
        val = self.nios.nios_expansion_gpio_read()?;
        log::trace!("[xb200_attach] expansion_gpio_read: {val}");
        if (val & 0x1) != 0 {
            log::debug!("MUXOUT Bit set: OK")
        } else {
            log::debug!("MUXOUT Bit not set: FAIL");
        }
        self.nios
            .nios_expansion_gpio_write(0xffffffff, 0x3C000800)?;
        Ok(())
    }
    pub fn xb200_enable(&mut self, enable: bool) -> Result<()> {
        let orig = self.nios.nios_expansion_gpio_read()?;
        log::trace!("[xb200_enable] expansion_gpio_read: {orig}");
        let mut val = orig;
        if enable {
            val |= BLADERF_XB_RF_ON;
        } else {
            val &= !BLADERF_XB_RF_ON;
        }
        if val == orig {
            Ok(())
        } else {
            self.nios.nios_expansion_gpio_write(0xffffffff, val)
        }
    }
    pub fn xb200_init(&mut self) -> Result<()> {
        log::trace!("Setting RX path");
        self.xb200_set_path(Channel::Rx, Xb200Path::Bypass)?;
        log::trace!("Setting TX path");
        self.xb200_set_path(Channel::Tx, Xb200Path::Bypass)?;
        log::trace!("Setting RX filter");
        self.xb200_set_filterbank(Channel::Rx, Xb200Filter::Auto1db)?;
        log::trace!("Setting TX filter");
        self.xb200_set_filterbank(Channel::Tx, Xb200Filter::Auto1db)
    }
    pub fn xb200_get_filterbank(&mut self, ch: Channel) -> Result<Xb200Filter> {
        let val = self.nios.nios_expansion_gpio_read()?;
        log::trace!("[xb200_get_filterbank] expansion_gpio_read: {val}");
        let shift = if ch == Channel::Rx {
            BLADERF_XB_RX_SHIFT
        } else {
            BLADERF_XB_TX_SHIFT
        };
        Xb200Filter::try_from((val >> shift) & 3)
    }
    pub fn set_filterbank_mux(&mut self, ch: Channel, filter: Xb200Filter) -> Result<()> {
        let (mask, shift) = if ch == Channel::Rx {
            (BLADERF_XB_RX_MASK, BLADERF_XB_RX_SHIFT)
        } else {
            (BLADERF_XB_TX_MASK, BLADERF_XB_TX_SHIFT)
        };
        let orig = self.nios.nios_expansion_gpio_read()?;
        log::trace!("[set_filterbank_mux] expansion_gpio_read: {orig}");
        let mut val = orig & !mask;
        val |= (filter as u32) << shift;
        if orig != val {
            let dir = if mask == BLADERF_XB_TX_MASK {
                "TX"
            } else {
                "RX"
            };
            log::trace!("Engaging {filter:?} band XB-200 {dir} filter");
            self.nios.nios_expansion_gpio_write(0xffffffff, val)?;
        }
        Ok(())
    }
    pub fn xb200_set_filterbank(&mut self, ch: Channel, filter: Xb200Filter) -> Result<()> {
        if !xb::xb200_is_enabled(&mut self.nios)? {
            log::error!("xb_200 not enabled! need to enable?");
            return Err(Error::Unsupported("XB200 not enabled"));
        }
        if filter == Xb200Filter::Auto1db || filter == Xb200Filter::Auto3db {
            let frequency = self.get_frequency(ch)?;
            log::trace!("[xb200_set_filterbank] get_frequency {frequency}");
            self.xb200_auto_filter_selection(ch, frequency)
        } else {
            self.set_filterbank_mux(ch, filter)
        }
    }
    pub fn xb200_auto_filter_selection(&mut self, channel: Channel, frequency: u64) -> Result<()> {
        if frequency >= 300_000_000 {
            return Ok(());
        }
        if !xb::xb200_is_enabled(&mut self.nios)? {
            log::error!("xb_200 not enabled! need to enable?");
            return Err(Error::Unsupported("XB200 not enabled"));
        }
        let fb = self.xb200_get_filterbank(channel)?;
        log::trace!("xb_200 current filterbank: {fb:?}");
        let filter = match fb {
            Xb200Filter::Auto1db => select_filter_from_table(frequency, AUTO_1DB_FILTERS),
            Xb200Filter::Auto3db => select_filter_from_table(frequency, AUTO_3DB_FILTERS),
            _ => {
                log::debug!("not setting filterbank! current value: {fb:?}!");
                return Ok(());
            }
        };
        self.set_filterbank_mux(channel, filter)
    }
    pub fn xb200_set_path(&mut self, ch: Channel, path: Xb200Path) -> Result<()> {
        let mut lval = crate::bladerf1::hardware::lms6002d::read(&mut self.nios, 0x5A)?;
        let swap_mask = if ch == Channel::Rx {
            LMS_RX_SWAP
        } else {
            LMS_TX_SWAP
        };
        if path == Xb200Path::Mix {
            lval |= swap_mask;
        } else {
            lval &= !swap_mask;
        }
        crate::bladerf1::hardware::lms6002d::write(&mut self.nios, 0x5A, lval)?;
        let mut val = self.nios.nios_expansion_gpio_read()?;
        log::trace!("[xb200_set_path] expansion_gpio_read: {val}");
        if (val & BLADERF_XB_RF_ON) == 0 {
            self.xb200_attach()?;
        }
        let mask = if ch == Channel::Rx {
            BLADERF_XB_CONFIG_RX_BYPASS_MASK | BLADERF_XB_RX_ENABLE
        } else {
            BLADERF_XB_CONFIG_TX_BYPASS_MASK | BLADERF_XB_TX_ENABLE
        };
        val |= BLADERF_XB_RF_ON;
        val &= !mask;
        if ch == Channel::Rx {
            if path == Xb200Path::Mix {
                val |= BLADERF_XB_RX_ENABLE | BLADERF_XB_CONFIG_RX_PATH_MIX;
            } else {
                val |= BLADERF_XB_CONFIG_RX_PATH_BYPASS;
            }
        } else if path == Xb200Path::Mix {
            val |= BLADERF_XB_TX_ENABLE | BLADERF_XB_CONFIG_TX_PATH_MIX;
        } else {
            val |= BLADERF_XB_CONFIG_TX_PATH_BYPASS;
        }
        self.nios.nios_expansion_gpio_write(0xffffffff, val)
    }
    pub fn xb200_get_path(&mut self, ch: Channel) -> Result<Xb200Path> {
        let val = self.nios.nios_expansion_gpio_read()?;
        log::trace!("[xb200_get_path] expansion_gpio_read: {val:#010x}");
        let bypass_bit = if ch == Channel::Rx {
            BLADERF_XB_CONFIG_RX_BYPASS
        } else {
            BLADERF_XB_CONFIG_TX_BYPASS
        };
        log::trace!(
            "[xb200_get_path] bypass_bit={bypass_bit:#x}, val & bypass_bit = {:#x}",
            val & bypass_bit
        );
        if val & bypass_bit != 0 {
            log::trace!("[xb200_get_path] returning Mix");
            Ok(Xb200Path::Mix)
        } else {
            log::trace!("[xb200_get_path] returning Bypass");
            Ok(Xb200Path::Bypass)
        }
    }
}
pub(crate) fn select_filter_from_table(frequency: u64, table: &[FilterEntry]) -> Xb200Filter {
    for (range, filter) in table {
        if range.contains(&frequency) {
            return *filter;
        }
    }
    Xb200Filter::Custom
}