use crate::bladerf1::hardware::lms6002d::{Band, Tune};
use crate::bladerf1::protocol::{nios_decode_retune, nios_encode_retune};
use crate::channel::Channel;
use crate::error::{Error, Result};
use crate::protocol::nios::packet_generic::NiosNum;
use crate::protocol::nios::targets::NiosPkt8x16AddrAgcCorr;
use crate::protocol::nios::{
NiosPkt8x16AddrIqCorr, NiosPkt8x16Target, NiosPkt8x32Target, NiosPkt8x64Target,
NiosPkt8x64TimestampAddr, NiosPkt32x32Target, nios_decode_read, nios_decode_write,
nios_encode_read, nios_encode_write,
};
use crate::usb::UsbTransport;
use crate::usb::{
BladeRf1UsbInterfaceCommands, UsbAltSetting, UsbInterfaceCommands, VendorRequest,
};
use crate::version::SemanticVersion;
use std::time::Duration;
pub struct NiosCore {
transport: UsbTransport,
active_streams: u8,
}
impl NiosCore {
pub fn new(transport: UsbTransport) -> Self {
Self {
transport,
active_streams: 0,
}
}
pub fn transport(&self) -> &UsbTransport {
&self.transport
}
pub fn transport_mut(&mut self) -> &mut UsbTransport {
&mut self.transport
}
pub(crate) fn active_streams(&self) -> u8 {
self.active_streams
}
pub(crate) fn stream_started(&mut self) {
self.active_streams += 1;
}
pub(crate) fn stream_stopped(&mut self) {
self.active_streams -= 1;
}
pub fn nios_read<A: NiosNum + Send, D: NiosNum + Send>(
&mut self,
id: impl Into<u8>,
addr: A,
) -> Result<D> {
let out_buf = self.transport.out_buffer()?;
log::trace!("nios_read: DMA buffer len = {} bytes", out_buf.len());
nios_encode_read::<A, D>(out_buf, id.into(), addr)?;
let response = self.transport.submit(None)?;
log::trace!("nios_read: response len = {} bytes", response.len());
nios_decode_read::<A, D>(response)
}
pub fn nios_write<A: NiosNum + Send, D: NiosNum + Send>(
&mut self,
id: impl Into<u8>,
addr: A,
data: D,
) -> Result<()> {
let out_buf = self.transport.out_buffer()?;
nios_encode_write::<A, D>(out_buf, id.into(), addr, data)?;
let response = self.transport.submit(None)?;
nios_decode_write::<A, D>(response)
}
pub fn nios_config_read(&mut self) -> Result<u32> {
self.nios_read::<u8, u32>(NiosPkt8x32Target::Control, 0)
}
pub fn nios_config_write(&mut self, value: u32) -> Result<()> {
self.nios_write::<u8, u32>(NiosPkt8x32Target::Control, 0, value)
}
pub fn nios_config_modify(&mut self, f: impl FnOnce(u32) -> u32) -> Result<()> {
let data = self.nios_config_read()?;
self.nios_config_write(f(data))
}
pub fn nios_expansion_gpio_read(&mut self) -> Result<u32> {
self.nios_read::<u32, u32>(NiosPkt32x32Target::Exp, u32::MAX)
}
pub fn nios_expansion_gpio_write(&mut self, mask: u32, val: u32) -> Result<()> {
self.nios_write::<u32, u32>(NiosPkt32x32Target::Exp, mask, val)
}
pub fn nios_expansion_gpio_dir_read(&mut self) -> Result<u32> {
self.nios_read::<u32, u32>(NiosPkt32x32Target::ExpDir, u32::MAX)
}
pub fn nios_expansion_gpio_dir_write(&mut self, mask: u32, val: u32) -> Result<()> {
self.nios_write::<u32, u32>(NiosPkt32x32Target::ExpDir, mask, val)
}
pub fn nios_get_fpga_version(&mut self) -> Result<SemanticVersion> {
let regval = self.nios_read::<u8, u32>(NiosPkt8x32Target::Version, 0)?;
log::trace!("Read FPGA version word: {regval:#010x}");
let version = SemanticVersion::new(
((regval >> 24) & 0xff) as u16,
((regval >> 16) & 0xff) as u16,
((regval & 0xffff) as u16).to_be(),
);
Ok(version)
}
pub fn nios_get_iq_gain_correction(&mut self, ch: Channel) -> Result<i16> {
let addr = match ch {
Channel::Rx => NiosPkt8x16AddrIqCorr::RxGain,
Channel::Tx => NiosPkt8x16AddrIqCorr::TxGain,
};
Ok(self.nios_read::<u8, u16>(NiosPkt8x16Target::IqCorr, addr.into())? as i16)
}
pub fn nios_get_iq_phase_correction(&mut self, ch: Channel) -> Result<i16> {
let addr = match ch {
Channel::Rx => NiosPkt8x16AddrIqCorr::RxPhase,
Channel::Tx => NiosPkt8x16AddrIqCorr::TxPhase,
};
Ok(self.nios_read::<u8, u16>(NiosPkt8x16Target::IqCorr, addr.into())? as i16)
}
pub fn nios_set_iq_gain_correction(&mut self, ch: Channel, value: i16) -> Result<()> {
let addr = match ch {
Channel::Rx => NiosPkt8x16AddrIqCorr::RxGain,
Channel::Tx => NiosPkt8x16AddrIqCorr::TxGain,
};
self.nios_write::<u8, u16>(NiosPkt8x16Target::IqCorr, addr.into(), value as u16)
}
pub fn nios_set_iq_phase_correction(&mut self, ch: Channel, value: i16) -> Result<()> {
let addr = match ch {
Channel::Rx => NiosPkt8x16AddrIqCorr::RxPhase,
Channel::Tx => NiosPkt8x16AddrIqCorr::TxPhase,
};
self.nios_write::<u8, u16>(NiosPkt8x16Target::IqCorr, addr.into(), value as u16)
}
pub fn get_alt_setting(&self) -> UsbAltSetting {
let raw = self.transport.interface().get_alt_setting();
UsbAltSetting::try_from(raw).unwrap_or_else(|_| {
log::warn!("unknown USB alt setting {raw:#x}, treating as Null");
UsbAltSetting::Null
})
}
#[allow(clippy::too_many_arguments)]
pub fn nios_retune(
&mut self,
channel: Channel,
timestamp: crate::bladerf1::protocol::RetuneTimestamp,
nint: u16,
nfrac: u32,
freqsel: u8,
vcocap: u8,
band: Band,
tune: Tune,
xb_gpio: u8,
) -> Result<crate::bladerf1::protocol::RetuneResult> {
if timestamp == crate::bladerf1::protocol::RetuneTimestamp::Now {
log::trace!("Clearing Retune Queue");
}
let out_buf = self.transport.out_buffer()?;
nios_encode_retune(
out_buf, channel, timestamp, nint, nfrac, freqsel, vcocap, band, tune, xb_gpio,
)?;
let response = self.transport.submit(None)?;
let response_pkt = nios_decode_retune(response)?;
if !response_pkt.is_success() {
let is_immediate = response_pkt.duration()
== u64::from(crate::bladerf1::protocol::RetuneTimestamp::Now);
return if is_immediate {
Err(Error::TuningFailed)
} else {
Err(Error::RetuneQueueFull)
};
}
Ok(crate::bladerf1::protocol::RetuneResult::new(
response_pkt.duration(),
))
}
pub fn nios_xb200_synth_write(&mut self, value: u32) -> Result<()> {
self.nios_write::<u8, u32>(NiosPkt8x32Target::Adf4_351, 0, value)
}
pub fn nios_get_timestamp(&mut self, channel: Channel) -> Result<u64> {
let addr = match channel {
Channel::Rx => NiosPkt8x64TimestampAddr::Rx,
Channel::Tx => NiosPkt8x64TimestampAddr::Tx,
};
self.nios_read::<u8, u64>(NiosPkt8x64Target::Timestamp, addr.into())
}
}
impl UsbInterfaceCommands for NiosCore {
fn usb_vendor_cmd_int(&self, cmd: VendorRequest) -> Result<u32> {
self.transport.usb_vendor_cmd_int(cmd)
}
fn usb_vendor_cmd_int_w_value(&self, cmd: VendorRequest, wvalue: u16) -> Result<u32> {
self.transport.usb_vendor_cmd_int_w_value(cmd, wvalue)
}
fn usb_vendor_cmd_int_w_index(&self, cmd: VendorRequest, windex: u16) -> Result<u32> {
self.transport.usb_vendor_cmd_int_w_index(cmd, windex)
}
fn usb_vendor_cmd_out_w_index(
&self,
cmd: VendorRequest,
windex: u16,
data: &[u8],
) -> Result<()> {
self.transport.usb_vendor_cmd_out_w_index(cmd, windex, data)
}
fn usb_vendor_cmd_in_w_index_data(
&self,
cmd: VendorRequest,
windex: u16,
buf: &mut [u8],
) -> Result<()> {
self.transport
.usb_vendor_cmd_in_w_index_data(cmd, windex, buf)
}
fn usb_change_setting(&mut self, setting: UsbAltSetting) -> Result<()> {
self.transport.usb_change_setting(setting)
}
}
impl BladeRf1UsbInterfaceCommands for NiosCore {
fn usb_enable_module(&self, channel: Channel, enable: bool) -> Result<()> {
self.transport.usb_enable_module(channel, enable)
}
fn usb_set_firmware_loopback(&mut self, enable: bool) -> Result<()> {
self.transport.usb_set_firmware_loopback(enable)
}
fn usb_get_firmware_loopback(&self) -> Result<bool> {
self.transport.usb_get_firmware_loopback()
}
fn usb_device_reset(&self) -> Result<()> {
self.transport.usb_device_reset()
}
fn usb_is_firmware_ready(&self) -> Result<bool> {
self.transport.usb_is_firmware_ready()
}
fn usb_is_fpga_configured(&self) -> Result<bool> {
self.transport.usb_is_fpga_configured()
}
fn usb_begin_fpga_prog(&self) -> Result<()> {
self.transport.usb_begin_fpga_prog()
}
fn usb_bulk_out(&self, endpoint: u8, data: &[u8], timeout: Duration) -> Result<()> {
self.transport.usb_bulk_out(endpoint, data, timeout)
}
}
impl NiosCore {
pub fn nios_set_agc_dc_correction(
&mut self,
corr: &crate::bladerf1::hardware::lms6002d::dc_calibration::AgcDcCorrection,
) -> Result<()> {
self.nios_write::<u8, u16>(
NiosPkt8x16Target::AgcCorr,
NiosPkt8x16AddrAgcCorr::DcQMax.into(),
corr.max.q as u16,
)?;
self.nios_write::<u8, u16>(
NiosPkt8x16Target::AgcCorr,
NiosPkt8x16AddrAgcCorr::DcIMax.into(),
corr.max.i as u16,
)?;
self.nios_write::<u8, u16>(
NiosPkt8x16Target::AgcCorr,
NiosPkt8x16AddrAgcCorr::DcQMid.into(),
corr.mid.q as u16,
)?;
self.nios_write::<u8, u16>(
NiosPkt8x16Target::AgcCorr,
NiosPkt8x16AddrAgcCorr::DcIMid.into(),
corr.mid.i as u16,
)?;
self.nios_write::<u8, u16>(
NiosPkt8x16Target::AgcCorr,
NiosPkt8x16AddrAgcCorr::DcQMin.into(),
corr.min.q as u16,
)?;
self.nios_write::<u8, u16>(
NiosPkt8x16Target::AgcCorr,
NiosPkt8x16AddrAgcCorr::DcIMin.into(),
corr.min.i as u16,
)
}
}