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::maybe_future::Op;
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::UsbAltSetting;
use crate::usb::UsbTransport;
use crate::version::SemanticVersion;
use nusb::MaybeFuture;
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 interface(&self) -> &nusb::Interface {
self.transport.interface()
}
pub fn usb_change_setting(
&mut self,
setting: UsbAltSetting,
) -> impl MaybeFuture<Output = Result<()>> {
self.transport.usb_change_setting(setting)
}
pub fn usb_set_firmware_loopback(
&mut self,
enable: bool,
) -> impl MaybeFuture<Output = Result<()>> {
self.transport.usb_set_firmware_loopback(enable)
}
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> + Send,
addr: A,
) -> impl MaybeFuture<Output = Result<D>> {
Op::new(async move {
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).await?;
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> + Send,
addr: A,
data: D,
) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
let out_buf = self.transport.out_buffer()?;
nios_encode_write::<A, D>(out_buf, id.into(), addr, data)?;
let response = self.transport.submit(None).await?;
nios_decode_write::<A, D>(response)
})
}
pub fn nios_config_read(&mut self) -> impl MaybeFuture<Output = Result<u32>> {
self.nios_read::<u8, u32>(NiosPkt8x32Target::Control, 0)
}
pub fn nios_config_write(&mut self, value: u32) -> impl MaybeFuture<Output = Result<()>> {
self.nios_write::<u8, u32>(NiosPkt8x32Target::Control, 0, value)
}
pub fn nios_config_modify(
&mut self,
f: impl FnOnce(u32) -> u32 + Send,
) -> impl MaybeFuture<Output = Result<()>> {
Op::new(async move {
let data = self.nios_config_read().await?;
self.nios_config_write(f(data)).await
})
}
pub fn nios_expansion_gpio_read(&mut self) -> impl MaybeFuture<Output = Result<u32>> {
self.nios_read::<u32, u32>(NiosPkt32x32Target::Exp, u32::MAX)
}
pub fn nios_expansion_gpio_write(
&mut self,
mask: u32,
val: u32,
) -> impl MaybeFuture<Output = Result<()>> {
self.nios_write::<u32, u32>(NiosPkt32x32Target::Exp, mask, val)
}
pub fn nios_expansion_gpio_dir_read(&mut self) -> impl MaybeFuture<Output = Result<u32>> {
self.nios_read::<u32, u32>(NiosPkt32x32Target::ExpDir, u32::MAX)
}
pub fn nios_expansion_gpio_dir_write(
&mut self,
mask: u32,
val: u32,
) -> impl MaybeFuture<Output = Result<()>> {
self.nios_write::<u32, u32>(NiosPkt32x32Target::ExpDir, mask, val)
}
pub fn nios_get_fpga_version(&mut self) -> impl MaybeFuture<Output = Result<SemanticVersion>> {
Op::new(async move {
let regval = self
.nios_read::<u8, u32>(NiosPkt8x32Target::Version, 0)
.await?;
log::trace!("Read FPGA version word: {regval:#010x}");
let version = SemanticVersion::new(
(regval & 0xff) as u16,
((regval >> 8) & 0xff) as u16,
((regval >> 16) & 0xffff) as u16,
);
Ok(version)
})
}
pub fn nios_get_iq_gain_correction(
&mut self,
ch: Channel,
) -> impl MaybeFuture<Output = Result<i16>> {
let addr = match ch {
Channel::Rx => NiosPkt8x16AddrIqCorr::RxGain,
Channel::Tx => NiosPkt8x16AddrIqCorr::TxGain,
};
self.nios_read::<u8, u16>(NiosPkt8x16Target::IqCorr, addr.into())
.map_ok(|v| v as i16)
}
pub fn nios_get_iq_phase_correction(
&mut self,
ch: Channel,
) -> impl MaybeFuture<Output = Result<i16>> {
let addr = match ch {
Channel::Rx => NiosPkt8x16AddrIqCorr::RxPhase,
Channel::Tx => NiosPkt8x16AddrIqCorr::TxPhase,
};
self.nios_read::<u8, u16>(NiosPkt8x16Target::IqCorr, addr.into())
.map_ok(|v| v as i16)
}
pub fn nios_set_iq_gain_correction(
&mut self,
ch: Channel,
value: i16,
) -> impl MaybeFuture<Output = 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,
) -> impl MaybeFuture<Output = 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,
) -> impl MaybeFuture<Output = Result<crate::bladerf1::protocol::RetuneResult>> {
Op::new(async move {
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).await?;
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) -> impl MaybeFuture<Output = Result<()>> {
self.nios_write::<u8, u32>(NiosPkt8x32Target::Adf4_351, 0, value)
}
pub fn nios_get_timestamp(
&mut self,
channel: Channel,
) -> impl MaybeFuture<Output = Result<u64>> {
let addr = match channel {
Channel::Rx => NiosPkt8x64TimestampAddr::Rx,
Channel::Tx => NiosPkt8x64TimestampAddr::Tx,
};
self.nios_read::<u8, u64>(NiosPkt8x64Target::Timestamp, addr.into())
}
}
impl NiosCore {
pub fn nios_set_agc_dc_correction(
&mut self,
corr: &crate::bladerf1::hardware::lms6002d::dc_calibration::AgcDcCorrection,
) -> impl MaybeFuture<Output = Result<()>> {
let writes = [
(NiosPkt8x16AddrAgcCorr::DcQMax, corr.max.q),
(NiosPkt8x16AddrAgcCorr::DcIMax, corr.max.i),
(NiosPkt8x16AddrAgcCorr::DcQMid, corr.mid.q),
(NiosPkt8x16AddrAgcCorr::DcIMid, corr.mid.i),
(NiosPkt8x16AddrAgcCorr::DcQMin, corr.min.q),
(NiosPkt8x16AddrAgcCorr::DcIMin, corr.min.i),
];
Op::new(async move {
for (addr, value) in writes {
self.nios_write::<u8, u16>(NiosPkt8x16Target::AgcCorr, addr.into(), value as u16)
.await?;
}
Ok(())
})
}
}