diff --git a/CMakeLists.txt b/CMakeLists.txt index 16804b5..c9d447c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 2.8) set(MAJOR_VERSION 0) set(API_COMPAT 0) set(MINOR_VERSION 1) -set(MAINT_VERSION git) +set(MAINT_VERSION 2) set(LIBVER "${MAJOR_VERSION}.${API_COMPAT}.${MINOR_VERSION}") @@ -92,7 +92,7 @@ else() include_directories(${LIBLMS7COMPACT_INCLUDE_DIRS}) add_definitions(-DHAVE_LMS_NFE) - set(LMS_FE_FILENAME "xtrx_fe_nlms7.c") + set(LMS_FE_FILENAME "xtrx_fe_nlms7.c" "xtrx_fe_octorx0.c") set(LMS_FE_LIBRARY ${LIBLMS7COMPACT_LIBRARIES}) endif() @@ -150,7 +150,7 @@ set(CPACK_PACKAGE_NAME "libxtrx") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "XTRX API library") set(CPACK_PACKAGE_VENDOR "Fairwaves, Inc.") set(CPACK_PACKAGE_CONTACT "http://fairwaves.co/wp/contact-us/") -set(CPACK_PACKAGE_VERSION ${LIBVER}-1) +set(CPACK_PACKAGE_VERSION ${LIBVER}-${MAINT_VERSION}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17), libxtrxll (>= 0.0.1), libxtrxdsp (>= 0.0.1), liblms7002m (>= 0.0.1)") if(ENABLE_SOAPY) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, soapysdr (>= 0.5.2)") diff --git a/examples/xtrx_fft/main.cpp b/examples/xtrx_fft/main.cpp index 14582d2..6be7ed8 100644 --- a/examples/xtrx_fft/main.cpp +++ b/examples/xtrx_fft/main.cpp @@ -23,8 +23,9 @@ #include "mainwindow.h" -void qInitResources_application() +int qInitResources_application() { + return 0; } int main(int argc, char *argv[]) diff --git a/examples/xtrx_fft/mainwindow.cpp b/examples/xtrx_fft/mainwindow.cpp index d1bfe40..4dc7251 100644 --- a/examples/xtrx_fft/mainwindow.cpp +++ b/examples/xtrx_fft/mainwindow.cpp @@ -108,7 +108,11 @@ void MainWindow::redraw(int idx) customPlot->graph(1)->setData(x, *pz[idx]); } +#if defined(QCUSTOMPLOT_VERSION) && (QCUSTOMPLOT_VERSION >= 0x020000) + customPlot->replot(QCustomPlot::rpImmediateRefresh); +#else customPlot->replot(QCustomPlot::rpImmediate); +#endif } @@ -141,8 +145,8 @@ void MainWindow::on_btStartStop_clicked() QString devstr = ui->cbDev->currentText(); if (dev == NULL) { - ui->widget->graph(0)->clearData(); - ui->widget->graph(1)->clearData(); + ui->widget->graph(0)->data()->clear(); + ui->widget->graph(1)->data()->clear(); ui->statusbar->showMessage(QString("Samplerate %1 MSPS Gain: %2").arg(samplerate).arg(gain)); res = xtrx_open(devstr.toLatin1(), 4, &dev); diff --git a/soapy/SoapyXTRX.cpp b/soapy/SoapyXTRX.cpp index 8056127..70b22ac 100644 --- a/soapy/SoapyXTRX.cpp +++ b/soapy/SoapyXTRX.cpp @@ -8,6 +8,7 @@ #include #include #include +#include void SoapyXTRX::xtrx_logfunc(int sevirity, const char* message) @@ -18,11 +19,40 @@ void SoapyXTRX::xtrx_logfunc(int sevirity, const char* message) SoapySDR::log(SOAPY_SDR_INFO, message); } +std::map> XTRXHandle::s_created; + +std::shared_ptr XTRXHandle::get(const std::string& name) +{ + auto idx = s_created.find(name); + if (idx != s_created.end()) { + if (std::shared_ptr obj = idx->second.lock()) + return obj; + } + + std::shared_ptr obj = std::make_shared(name); + s_created.insert(make_pair(name, obj)); + return obj; +} + +XTRXHandle::XTRXHandle(const std::string& name) +{ + int res = xtrx_open_string(name.c_str(), &_dev); + if (res < 0) + throw std::runtime_error(std::string("XTRXHandle::XTRXHandle(")+name.c_str()+") - unable to open the device: error: " + strerror(-res)); + devcnt = res; + + SoapySDR::log(SOAPY_SDR_INFO, std::string("Created: `") + name.c_str() + "`"); +} + +XTRXHandle::~XTRXHandle() +{ + xtrx_close(_dev); +} + SoapyXTRX::SoapyXTRX(const SoapySDR::Kwargs &args) { SoapySDR::logf(SOAPY_SDR_INFO, "Make connection: '%s'", args.count("dev") ? args.at("dev").c_str() : "*"); - //xtrx_set_logfunction(&SoapyXTRX::xtrx_logfunc); unsigned loglevel = 3; #ifdef __linux const char* lenv = getenv("SOAPY_XTRX_LOGLEVEL"); @@ -30,26 +60,29 @@ SoapyXTRX::SoapyXTRX(const SoapySDR::Kwargs &args) loglevel = atoi(lenv); } #endif - const std::string& dev = args.at("dev"); - if (args.count("loglvl")) { - loglevel = std::stoi(args.at("loglvl")); + const std::string& dev = (args.count("dev")) ? args.at("dev") : ""; + + if (args.count("loglevel")) { + loglevel = std::stoi(args.at("loglevel")); } + xtrx_log_setlevel(loglevel, NULL); - int res = xtrx_open(dev.c_str(), loglevel, &_dev); - if (res) - throw std::runtime_error("SoapyXTRX::SoapyXTRX("+dev+") - unable to open the device"); + _dev = XTRXHandle::get(dev); if (args.count("refclk")) { - _ref_clk = std::stoi(args.at("refclk")); - xtrx_set_ref_clk(_dev, _ref_clk, XTRX_CLKSRC_INT); + xtrx_set_ref_clk(_dev->dev(), std::stoi(args.at("refclk")), XTRX_CLKSRC_INT); + + SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyXTRX::SoapyXTRX() set ref to internal clock"); + } + if (args.count("extclk")) { + xtrx_set_ref_clk(_dev->dev(), std::stoi(args.at("extclk")), XTRX_CLKSRC_EXT); - SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyXTRX::SoapyXTRX() set RefClk to %d", _ref_clk); + SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyXTRX::SoapyXTRX() set ref to external clock"); } } SoapyXTRX::~SoapyXTRX(void) { - xtrx_close(_dev); } /******************************************************************* @@ -57,12 +90,12 @@ SoapyXTRX::~SoapyXTRX(void) ******************************************************************/ std::string SoapyXTRX::getDriverKey(void) const { - return "xtrx"; + return "xtrxsoapy"; } std::string SoapyXTRX::getHardwareKey(void) const { - return "/dev/xtrx0"; + return "xtrxdev"; } SoapySDR::Kwargs SoapyXTRX::getHardwareInfo(void) const @@ -98,15 +131,15 @@ std::vector SoapyXTRX::listAntennas(const int direction, const size } if (direction == SOAPY_SDR_TX) { - ants.push_back("BAND1"); - ants.push_back("BAND2"); + ants.push_back("TXH"); + ants.push_back("TXW"); } return ants; } void SoapyXTRX::setAntenna(const int direction, const size_t channel, const std::string &name) { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyXTRX::setAntenna(%d, %s)", int(channel), name.c_str()); xtrx_antenna_t a; @@ -121,8 +154,8 @@ void SoapyXTRX::setAntenna(const int direction, const size_t channel, const std: } else if (direction == SOAPY_SDR_TX) { - if (name == "BAND1" || name == "B1") a = XTRX_TX_L; - else if (name == "BAND2" || name == "B2") a = XTRX_TX_W; + if (name == "BAND1" || name == "B1" || name == "TXH") a = XTRX_TX_H; + else if (name == "BAND2" || name == "B2" || name == "TXW") a = XTRX_TX_W; else throw std::runtime_error("SoapyXTRX::setAntenna(TX, "+name+") - unknown antenna name"); _tx_ant = a; @@ -130,7 +163,7 @@ void SoapyXTRX::setAntenna(const int direction, const size_t channel, const std: throw std::runtime_error("SoapyXTRX::setAntenna(?)"); } - int res = xtrx_set_antenna(_dev, a); + int res = xtrx_set_antenna(_dev->dev(), a); if (res) { throw std::runtime_error("SoapyXTRX::setAntenna(TX, "+name+") xtrx_set_antenna() err"); } @@ -138,7 +171,7 @@ void SoapyXTRX::setAntenna(const int direction, const size_t channel, const std: std::string SoapyXTRX::getAntenna(const int direction, const size_t /*channel*/) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (direction == SOAPY_SDR_RX) { switch (_rx_ant) @@ -154,8 +187,8 @@ std::string SoapyXTRX::getAntenna(const int direction, const size_t /*channel*/) { switch (_tx_ant) { - case XTRX_TX_L: return "BAND1"; - case XTRX_TX_W: return "BAND2"; + case XTRX_TX_H: return "TXH"; + case XTRX_TX_W: return "TXW"; default: return "NONE"; } } @@ -174,7 +207,7 @@ bool SoapyXTRX::hasDCOffsetMode(const int direction, const size_t /*channel*/) c void SoapyXTRX::setDCOffsetMode(const int direction, const size_t /*channel*/, const bool /*automatic*/) { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (direction == SOAPY_SDR_RX) { //rfic->SetRxDCRemoval(automatic); } @@ -182,7 +215,7 @@ void SoapyXTRX::setDCOffsetMode(const int direction, const size_t /*channel*/, c bool SoapyXTRX::getDCOffsetMode(const int direction, const size_t /*channel*/) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (direction == SOAPY_SDR_RX) { // return rfic->GetRxDCRemoval(); } @@ -197,7 +230,7 @@ bool SoapyXTRX::hasDCOffset(const int direction, const size_t /*channel*/) const void SoapyXTRX::setDCOffset(const int direction, const size_t /*channel*/, const std::complex &/*offset*/) { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (direction == SOAPY_SDR_TX) { //rfic->SetTxDCOffset(offset.real(), offset.imag()); } @@ -205,7 +238,7 @@ void SoapyXTRX::setDCOffset(const int direction, const size_t /*channel*/, const std::complex SoapyXTRX::getDCOffset(const int /*direction*/, const size_t /*channel*/) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); double I = 0.0, Q = 0.0; //if (direction == SOAPY_SDR_TX) rfic->GetTxDCOffset(I, Q); return std::complex(I, Q); @@ -223,7 +256,7 @@ void SoapyXTRX::setIQBalance(const int /*direction*/, const size_t /*channel*/, std::complex SoapyXTRX::getIQBalance(const int /*direction*/, const size_t /*channel*/) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); return std::complex(0,0); } @@ -249,13 +282,14 @@ std::vector SoapyXTRX::listGains(const int direction, const size_t void SoapyXTRX::setGain(const int direction, const size_t channel, const double value) { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); xtrx_channel_t chan = to_xtrx_channels(channel); + SoapySDR::logf(SOAPY_SDR_FATAL /*SOAPY_SDR_DEBUG(*/, "SoapyXTRX::setGain(, %d, --, %g dB)", int(channel), value); if (direction == SOAPY_SDR_RX) { double actual; - xtrx_set_gain(_dev, chan, XTRX_RX_LNA_GAIN, value, &actual); + xtrx_set_gain(_dev->dev(), chan, XTRX_RX_LNA_GAIN, value, &actual); } else SoapySDR::Device::setGain(direction, channel, value); @@ -263,31 +297,31 @@ void SoapyXTRX::setGain(const int direction, const size_t channel, const double void SoapyXTRX::setGain(const int direction, const size_t channel, const std::string &name, const double value) { - std::unique_lock lock(_accessMutex); - SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyXTRX::setGain(, %d, %s, %g dB)", int(channel), name.c_str(), value); + std::unique_lock lock(_dev->accessMutex); + SoapySDR::logf(SOAPY_SDR_FATAL /*SOAPY_SDR_DEBUG(*/, "SoapyXTRX::setGain(, %d, %s, %g dB)", int(channel), name.c_str(), value); xtrx_channel_t chan = to_xtrx_channels(channel); if (direction == SOAPY_SDR_RX and (name == "LNA" || name == "LB")) { - xtrx_set_gain(_dev, chan, XTRX_RX_LNA_GAIN, value, &_actual_rx_gain_lna[channel]); + xtrx_set_gain(_dev->dev(), chan, XTRX_RX_LNA_GAIN, value, &_actual_rx_gain_lna[channel]); return; } else if (direction == SOAPY_SDR_RX and name == "TIA") { - xtrx_set_gain(_dev, chan, XTRX_RX_TIA_GAIN, value, &_actual_rx_gain_tia[channel]); + xtrx_set_gain(_dev->dev(), chan, XTRX_RX_TIA_GAIN, value, &_actual_rx_gain_tia[channel]); return; } else if (direction == SOAPY_SDR_RX and name == "PGA") { - xtrx_set_gain(_dev, chan, XTRX_RX_PGA_GAIN, value, &_actual_rx_gain_pga[channel]); + xtrx_set_gain(_dev->dev(), chan, XTRX_RX_PGA_GAIN, value, &_actual_rx_gain_pga[channel]); return; } else if (direction == SOAPY_SDR_TX and name == "PAD") { - xtrx_set_gain(_dev, chan, XTRX_TX_PAD_GAIN, value, &_actual_tx_gain_pad[channel]); + xtrx_set_gain(_dev->dev(), chan, XTRX_TX_PAD_GAIN, value, &_actual_tx_gain_pad[channel]); } else throw std::runtime_error("SoapyXTRX::setGain("+name+") - unknown gain name"); @@ -297,7 +331,7 @@ void SoapyXTRX::setGain(const int direction, const size_t channel, const std::st double SoapyXTRX::getGain(const int direction, const size_t channel, const std::string &name) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); to_xtrx_channels(channel); if (direction == SOAPY_SDR_RX and (name == "LNA" || name == "LB")) @@ -364,7 +398,7 @@ SoapySDR::ArgInfoList SoapyXTRX::getFrequencyArgsInfo(const int direction, const void SoapyXTRX::setFrequency(const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &/*args*/) { xtrx_channel_t chan = to_xtrx_channels(channel); - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyXTRX::setFrequency(, %d, %s, %g MHz)", int(channel), name.c_str(), frequency/1e6); int res; @@ -375,9 +409,9 @@ void SoapyXTRX::setFrequency(const int direction, const size_t channel, const st if (targetRfFreq > 3.8e9) targetRfFreq = 3.8e9; if (direction == SOAPY_SDR_TX) { - res = xtrx_tune(_dev, XTRX_TUNE_TX_FDD, targetRfFreq, &_actual_rf_tx); + res = xtrx_tune(_dev->dev(), XTRX_TUNE_TX_FDD, targetRfFreq, &_actual_rf_tx); } else { - res = xtrx_tune(_dev, XTRX_TUNE_RX_FDD, targetRfFreq, &_actual_rf_rx); + res = xtrx_tune(_dev->dev(), XTRX_TUNE_RX_FDD, targetRfFreq, &_actual_rf_rx); } if (res) { throw std::runtime_error("SoapyXTRX::setFrequency("+name+") unable to tune!"); @@ -387,9 +421,9 @@ void SoapyXTRX::setFrequency(const int direction, const size_t channel, const st else if (name == "BB") { if (direction == SOAPY_SDR_TX) { - res = xtrx_tune_ex(_dev, XTRX_TUNE_BB_TX, chan, frequency, &_actual_bb_tx[channel]); + res = xtrx_tune_ex(_dev->dev(), XTRX_TUNE_BB_TX, chan, frequency, &_actual_bb_tx[channel]); } else { - res = xtrx_tune_ex(_dev, XTRX_TUNE_BB_RX, chan, frequency, &_actual_bb_rx[channel]); + res = xtrx_tune_ex(_dev->dev(), XTRX_TUNE_BB_RX, chan, frequency, &_actual_bb_rx[channel]); } if (res) { throw std::runtime_error("SoapyXTRX::setFrequency("+name+") unable to tune!"); @@ -402,7 +436,7 @@ void SoapyXTRX::setFrequency(const int direction, const size_t channel, const st double SoapyXTRX::getFrequency(const int direction, const size_t channel, const std::string &name) const { to_xtrx_channels(channel); - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (name == "RF") { if (direction == SOAPY_SDR_TX) { @@ -433,7 +467,7 @@ std::vector SoapyXTRX::listFrequencies(const int /*direction*/, con SoapySDR::RangeList SoapyXTRX::getFrequencyRange(const int direction, const size_t /*channel*/, const std::string &name) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); SoapySDR::RangeList ranges; if (name == "RF") { @@ -442,7 +476,7 @@ SoapySDR::RangeList SoapyXTRX::getFrequencyRange(const int direction, const size else if (name == "BB") { uint64_t out = 0; - int res = xtrx_val_get(_dev, (direction == SOAPY_SDR_TX) ? XTRX_TX : XTRX_RX, XTRX_CH_AB, XTRX_LMS7_DATA_RATE, &out); + int res = xtrx_val_get(_dev->dev(), (direction == SOAPY_SDR_TX) ? XTRX_TX : XTRX_RX, XTRX_CH_AB, XTRX_LMS7_DATA_RATE, &out); if (res) ranges.push_back(SoapySDR::Range(-0.0, 0.0)); else @@ -464,7 +498,7 @@ SoapySDR::RangeList SoapyXTRX::getFrequencyRange(const int /*direction*/, const void SoapyXTRX::setSampleRate(const int direction, const size_t channel, const double rate) { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyXTRX::setSampleRate(%d, %s, %g MHz)", int(channel), (direction == SOAPY_SDR_TX) ? "TX" : "RX", rate/1e6); double master_clock; if (direction == SOAPY_SDR_RX) @@ -484,7 +518,7 @@ void SoapyXTRX::setSampleRate(const int direction, const size_t channel, const d return; } - int ret = xtrx_set_samplerate(_dev, 0, _tmp_rx, _tmp_tx, + int ret = xtrx_set_samplerate(_dev->dev(), 0, _tmp_rx, _tmp_tx, 0, //XTRX_SAMPLERATE_FORCE_UPDATE, &master_clock, &_actual_rx_rate, &_actual_tx_rate); @@ -498,7 +532,7 @@ void SoapyXTRX::setSampleRate(const int direction, const size_t channel, const d double SoapyXTRX::getSampleRate(const int direction, const size_t /*channel*/) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (direction == SOAPY_SDR_RX) { @@ -544,17 +578,17 @@ void SoapyXTRX::setBandwidth(const int direction, const size_t channel, const do { if (bw == 0.0) return; //special ignore value - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyXTRX::setBandwidth(, %d, %g MHz)", int(channel), bw/1e6); xtrx_channel_t chan = to_xtrx_channels(channel); if (direction == SOAPY_SDR_RX) { - xtrx_tune_rx_bandwidth(_dev, chan, bw, &_actual_rx_bandwidth[channel]); + xtrx_tune_rx_bandwidth(_dev->dev(), chan, bw, &_actual_rx_bandwidth[channel]); } else if (direction == SOAPY_SDR_TX) { - xtrx_tune_tx_bandwidth(_dev, chan, bw, &_actual_tx_bandwidth[channel]); + xtrx_tune_tx_bandwidth(_dev->dev(), chan, bw, &_actual_tx_bandwidth[channel]); } //restore dc offset mode @@ -562,7 +596,7 @@ void SoapyXTRX::setBandwidth(const int direction, const size_t channel, const do double SoapyXTRX::getBandwidth(const int direction, const size_t channel) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); /*xtrx_channel_t chan = */to_xtrx_channels(channel); if (direction == SOAPY_SDR_RX) @@ -600,18 +634,23 @@ SoapySDR::RangeList SoapyXTRX::getBandwidthRange(const int direction, const size void SoapyXTRX::setMasterClockRate(const double rate) { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); //_ref_clk = rate; - - // xtrx_set_ref_clk(_dev, _ref_clk, _ref_source); - + // xtrx_set_ref_clk(_dev->dev(), _ref_clk, _ref_source); // TODO: get reference clock in case of autodetection } double SoapyXTRX::getMasterClockRate(void) const { - std::unique_lock lock(_accessMutex); - return _ref_clk; + return 0; + //std::unique_lock lock(_dev->accessMutex); + //int64_t v; + + //int res = xtrx_val_get(_dev->dev(), XTRX_TRX, XTRX_CH_AB, XTRX_REF_REFCLK, &v); + //if (res) + // throw std::runtime_error("SoapyXTRX::getMasterClockRate() unable to get master clock!"); + + //return v; } SoapySDR::RangeList SoapyXTRX::getMasterClockRates(void) const @@ -629,7 +668,7 @@ std::vector SoapyXTRX::listClockSources(void) const void SoapyXTRX::setClockSource(const std::string &source) { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (source == "internal") _ref_source = XTRX_CLKSRC_INT; else if (source == "extrernal") @@ -639,7 +678,7 @@ void SoapyXTRX::setClockSource(const std::string &source) else return; - xtrx_set_ref_clk(_dev, _ref_clk, _ref_source); + xtrx_set_ref_clk(_dev->dev(), _ref_clk, _ref_source); } std::string SoapyXTRX::getClockSource(void) const @@ -734,7 +773,7 @@ SoapySDR::ArgInfo SoapyXTRX::getSensorInfo(const std::string &name) const std::string SoapyXTRX::readSensor(const std::string &name) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); int res = 0; uint64_t val; if (name == "clock_locked") @@ -747,7 +786,7 @@ std::string SoapyXTRX::readSensor(const std::string &name) const } else if (name == "board_temp") { - res = xtrx_val_get(_dev, XTRX_TRX, XTRX_CH_AB, XTRX_BOARD_TEMP, &val); + res = xtrx_val_get(_dev->dev(), XTRX_TRX, XTRX_CH_AB, XTRX_BOARD_TEMP, &val); if (res) throw std::runtime_error("SoapyXTRX::readSensor("+name+") error: " + std::to_string(res)); @@ -780,7 +819,7 @@ SoapySDR::ArgInfo SoapyXTRX::getSensorInfo(const int /*direction*/, const size_t std::string SoapyXTRX::readSensor(const int /*direction*/, const size_t /*channel*/, const std::string &name) const { - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (name == "lo_locked") { @@ -840,7 +879,7 @@ void SoapyXTRX::writeSetting(const int direction, const size_t channel, (void)key; (void)value; - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); throw std::runtime_error("unknown setting key: "+key); } @@ -956,7 +995,7 @@ SoapySDR::Stream *SoapyXTRX::setupStream( const SoapySDR::Kwargs &args) { //TODO: multi stream - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); xtrx_run_stream_params_t *params; size_t num_channels = channels.size(); if (num_channels < 1) @@ -970,7 +1009,7 @@ SoapySDR::Stream *SoapyXTRX::setupStream( params = &_stream_params.rx; _rx_channels = num_channels; - xtrx_stop(_dev, XTRX_RX); + xtrx_stop(_dev->dev(), XTRX_RX); } else if (direction == SOAPY_SDR_TX) { if (_tx_stream != SS_NONE) { std::runtime_error("SoapyXTRX::setupStream(TX) stream is already allocated"); @@ -979,7 +1018,7 @@ SoapySDR::Stream *SoapyXTRX::setupStream( params = &_stream_params.tx; _tx_channels = num_channels; - xtrx_stop(_dev, XTRX_TX); + xtrx_stop(_dev->dev(), XTRX_TX); } else { throw std::runtime_error("SoapyXTRX::setupStream(?) unsupported direction"); } @@ -1066,7 +1105,7 @@ void SoapyXTRX::closeStream(SoapySDR::Stream *stream) { //TODO: multi stream (void)stream; - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); } size_t SoapyXTRX::getStreamMTU(SoapySDR::Stream *stream) const @@ -1087,7 +1126,7 @@ int SoapyXTRX::activateStream( throw std::runtime_error("SoapyXTRX::activateStream() - too much packet size"); } - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (stream == STREAM_RX) { if (_rx_stream != SS_ALOCATED) @@ -1101,7 +1140,7 @@ int SoapyXTRX::activateStream( if (flags & SOAPY_SDR_HAS_TIME) { _stream_params.rx_stream_start = (master_ts)SoapySDR::timeNsToTicks(timeNs, _actual_rx_rate); } else { - _stream_params.rx_stream_start = 4096; + _stream_params.rx_stream_start = 32768; } _stream_params.rx.paketsize = (uint16_t)numElems; _stream_params.dir = XTRX_RX; @@ -1119,14 +1158,14 @@ int SoapyXTRX::activateStream( if (flags & SOAPY_SDR_HAS_TIME) { _tx_internal = SoapySDR::timeNsToTicks(timeNs, _actual_tx_rate); } else { - _tx_internal = 4096*1024; + _tx_internal = 32768; } } else { throw std::runtime_error("SoapyXTRX::activateStream() - incorrect stream"); } _stream_params.nflags = 0; - int res = xtrx_run_ex(_dev, &_stream_params); + int res = xtrx_run_ex(_dev->dev(), &_stream_params); if (res == 0) { if (stream == STREAM_RX) { _rx_stream = SS_ACTIVATED; @@ -1150,13 +1189,13 @@ int SoapyXTRX::deactivateStream( (void)flags; (void)timeNs; - std::unique_lock lock(_accessMutex); + std::unique_lock lock(_dev->accessMutex); if (stream == STREAM_RX) { if (_rx_stream != SS_ACTIVATED) return SOAPY_SDR_STREAM_ERROR; - xtrx_stop(_dev, XTRX_RX); + xtrx_stop(_dev->dev(), XTRX_RX); _rx_stream = SS_ALOCATED; return 0; @@ -1164,7 +1203,7 @@ int SoapyXTRX::deactivateStream( if (_tx_stream != SS_ACTIVATED) return SOAPY_SDR_STREAM_ERROR; - xtrx_stop(_dev, XTRX_TX); + xtrx_stop(_dev->dev(), XTRX_TX); _tx_stream = SS_ALOCATED; return 0; @@ -1196,7 +1235,7 @@ int SoapyXTRX::readStream( rex.buffers = buffs; rex.flags = RCVEX_DONT_INSER_ZEROS; //RCVEX_EXTRA_LOG; - int res = xtrx_recv_sync_ex(_dev, &rex); + int res = xtrx_recv_sync_ex(_dev->dev(), &rex); if (res) { SoapySDR::logf(SOAPY_SDR_INFO, "SoapyXTRX::readStream(%d) res = %d", numElems, res); } @@ -1225,19 +1264,24 @@ int SoapyXTRX::writeStream( long long ts = (flags & SOAPY_SDR_HAS_TIME) ? SoapySDR::timeNsToTicks(timeNs, _actual_tx_rate) : _tx_internal; + unsigned toSend = numElems; + xtrx_send_ex_info_t nfo; nfo.buffer_count = _tx_channels; nfo.buffers = buffs; nfo.flags = 0; - nfo.samples = numElems; - nfo.ts = ts; - int res = xtrx_send_sync_ex(_dev, &nfo); + nfo.samples = toSend; + nfo.ts = 2*ts; + nfo.timeout = timeoutUs / 1000; + + //std::cerr << "SAMPLES: " << numElems << " TS:" << ts <dev(), &nfo); if (~(flags & SOAPY_SDR_HAS_TIME)) { - _tx_internal += numElems; + _tx_internal += toSend; } - return (res) ? SOAPY_SDR_TIMEOUT : numElems; + return (res) ? SOAPY_SDR_TIMEOUT : toSend; } int SoapyXTRX::readStreamStatus( diff --git a/soapy/SoapyXTRX.h b/soapy/SoapyXTRX.h index fa397a0..896e6ad 100644 --- a/soapy/SoapyXTRX.h +++ b/soapy/SoapyXTRX.h @@ -3,9 +3,34 @@ #include #include #include +#include #include "../xtrx_api.h" + +class XTRXHandle +{ +public: + mutable std::recursive_mutex accessMutex; + + struct xtrx_dev* dev() { return _dev; } + operator struct xtrx_dev* () { return _dev; } + + unsigned count() { return devcnt; } + + XTRXHandle() = delete; + XTRXHandle(const std::string& name); + ~XTRXHandle(); + + static std::shared_ptr get(const std::string& name); + +protected: + struct xtrx_dev* _dev = NULL; + unsigned devcnt; + + static std::map> s_created; +}; + class SoapyXTRX : public SoapySDR::Device { public: @@ -256,9 +281,7 @@ class SoapyXTRX : public SoapySDR::Device private: enum { MAX_CHANNELS = 2 }; - mutable std::recursive_mutex _accessMutex; - - xtrx_dev* _dev = NULL; + std::shared_ptr _dev; double _tmp_rx = 0; double _tmp_tx = 0; diff --git a/test_xtrx.c b/test_xtrx.c index e940fdc..842eea1 100644 --- a/test_xtrx.c +++ b/test_xtrx.c @@ -164,7 +164,7 @@ void* thread_rx_to_file(void* obj) sem_post(&rxd->sem_read_rx); rxd->buf_ptr = (rxd->buf_ptr + 1) % rxd->buf_max; - fprintf(stderr, "RX_TO_FILE: buffer %u written\n", rxd->get_cnt); + //fprintf(stderr, "RX_TO_FILE: buffer %u written\n", rxd->get_cnt); rxd->get_cnt++; } } @@ -282,7 +282,7 @@ int stream_rx(struct stream_data* sdata, unsigned buf_cnt = rxd->num_chans; uint64_t zero_inserted = 0; int overruns = 0; - uint64_t rx_processed; + uint64_t rx_processed = 0; uint64_t sp = grtime(); uint64_t st = sp; @@ -554,6 +554,8 @@ int main(int argc, char** argv) int rxgain_lna = 15; int rxgain_pga = 0; int rxgain_tia = 9; + int gtime = 0; + int gmode = 0; struct option long_options[] = { {"cycles", required_argument, 0, 'C' }, @@ -605,6 +607,8 @@ int main(int argc, char** argv) {"samples", required_argument, 0, 'u' }, {"rxgain", required_argument, 0, 'g' }, {"txgain", required_argument, 0, 'G' }, + {"gtime", required_argument, 0, 'j' }, + {"gmode", required_argument, 0, 'J' }, {0, 0, 0, 0 } }; @@ -702,6 +706,9 @@ int main(int argc, char** argv) case 'u': samples = atoll(optarg); break; case 'U': tx_nodiscard = 1; break; + + case 'j': gtime = parse_val(optarg); break; + case 'J': gmode = parse_val(optarg); break; default: /* '?' */ fprintf(stderr, "Usage: %s \n", argv[0]); generate_help(long_options); @@ -779,30 +786,22 @@ int main(int argc, char** argv) return 0; } - unsigned j; - char cpstr[1024]; - strncpy(cpstr, device, sizeof(cpstr)); - - char* str1; - char* saveptr1; - char* devices[MAX_DEVS]; - for (j = 0, str1 = cpstr; j < MAX_DEVS; j++, str1 = NULL) { - char* token = strtok_r(str1, ";", &saveptr1); - if (token == NULL) - break; - devices[j] = token; + res = xtrx_open_string(device, &dev); + if (res < 0) { + fprintf(stderr, "Failed xtrx_open: %d\n", res); + goto falied_open; } - fprintf(stderr, "Creating multidev for %d devices\n", j); - res = xtrx_open_multi(j, (const char**)devices, loglevel, &dev); - dev_count = j; + + dev_count = res; } else { res = xtrx_open(device, loglevel | XTRX_O_RESET, &dev); + if (res) { + fprintf(stderr, "Failed xtrx_open: %d\n", res); + goto falied_open; + } + dev_count = 1; } - if (res) { - fprintf(stderr, "Failed xtrx_open: %d\n", res); - goto falied_open; - } // // Initialize RX file output stream @@ -877,10 +876,12 @@ int main(int argc, char** argv) actual_txsample_rate / 1e6); if (vio) { - xtrx_val_set(dev, XTRX_TRX, XTRX_CH_AB, XTRX_LMS7_VIO, vio); + xtrx_val_set(dev, XTRX_TRX, XTRX_CH_ALL, XTRX_LMS7_VIO, vio); } if (dmarx) { + xtrx_set_antenna(dev, XTRX_RX_AUTO); + res = xtrx_tune(dev, XTRX_TUNE_RX_FDD, rxfreq, &rxactualfreq); if (res) { fprintf(stderr, "Failed xtrx_tune: %d\n", res); @@ -888,6 +889,7 @@ int main(int argc, char** argv) } fprintf(stderr, "RX tunned: %f\n", rxactualfreq); +#if 0 if (rxfreq < 900e6) { xtrx_set_antenna(dev, XTRX_RX_L); } else if (rxfreq > 2300e6) { @@ -895,6 +897,7 @@ int main(int argc, char** argv) } else { xtrx_set_antenna(dev, XTRX_RX_W); } +#endif res = xtrx_tune_rx_bandwidth(dev, ch, rxbandwidth, &actual_rxbandwidth); if (res) { @@ -935,8 +938,8 @@ int main(int argc, char** argv) } fprintf(stderr, "TX tunned: %f\n", txactualfreq); - if (txfreq < 1000e6) { - xtrx_set_antenna(dev, XTRX_TX_L); + if (txfreq > 2300e6) { + xtrx_set_antenna(dev, XTRX_TX_H); } else { xtrx_set_antenna(dev, XTRX_TX_W); } @@ -955,6 +958,39 @@ int main(int argc, char** argv) fprintf(stderr, "TX PAD gain: %f\n", actuallnagain); } + if (gmode > 0) { + gtime_data_t in = {0,0}; + gtime_data_t out; + res = xtrx_gtime_op(dev, -1, XTRX_GTIME_DISABLE, in, &out); + if (res) { + fprintf(stderr, "Failed xtrx_gtime_op(0): %d\n", res); + goto falied_tune; + } + + unsigned cmd = (gmode == 3) ? XTRX_GTIME_ENABLE_INT_WEXTE : + (gmode == 2) ? XTRX_GTIME_ENABLE_INT_WEXT : + XTRX_GTIME_ENABLE_INT; + + in.sec = 7; + res = xtrx_gtime_op(dev, -1, cmd, in, &out); + if (res) { + fprintf(stderr, "Failed xtrx_gtime_op(3): %d\n", res); + goto falied_tune; + } + + for (unsigned i = 0; i < 8; i++) { + usleep(450000); + for (unsigned j = 0; j < dev_count; j++) { + res = xtrx_gtime_op(dev, j, XTRX_GTIME_GET_CUR, in, &out); + if (res) { + fprintf(stderr, "Failed xtrx_gtime_op(4): %d\n", res); + goto falied_tune; + } + fprintf(stderr, "Current time[%d]: %08d.%09d\n", j, out.sec, out.nsec); + } + } + } + s_tx_start_ts = s_tx_skip / (tx_siso ? 1 : 2); s_tx_nodiscard = tx_nodiscard; @@ -994,6 +1030,11 @@ int main(int argc, char** argv) params.tx.paketsize = tx_packet_size / ((tx_siso) ? 1 : 2); params.rx_stream_start = rx_skip; params.tx_repeat_buf = NULL; //(tx_repeat_mode) ? BUF : NULL; + params.gtime.sec = 11; + params.gtime.nsec = 750000000; //250 ms delay + if (gmode > 0) { + params.nflags |= XTRX_RUN_GTIME; + } res = xtrx_run_ex(dev, ¶ms); if (res) { @@ -1001,6 +1042,17 @@ int main(int argc, char** argv) goto falied_tune; } + if (gmode > 0) { + gtime_data_t in = {0,0}; + gtime_data_t out; + res = xtrx_gtime_op(dev, -1, XTRX_GTIME_GET_CUR, in, &out); + if (res) { + fprintf(stderr, "Failed xtrx_gtime_op(4): %d\n", res); + goto falied_tune; + } + fprintf(stderr, "Current time: %08d.%09d\n", out.sec, out.nsec); + } + // // Streaming // diff --git a/xtrx.c b/xtrx.c index 82b2ee7..834aa2e 100644 --- a/xtrx.c +++ b/xtrx.c @@ -36,12 +36,10 @@ enum { MAX_LMS = 1, DEF_BUFSIZE = 32768, - DEF_RX_BUFSIZE = DEF_BUFSIZE, + DEF_TX_BUFSIZE = DEF_BUFSIZE, }; -typedef double clock_val_t; - struct xtrx_multill_stream { bool run; @@ -70,7 +68,7 @@ struct xtrx_dev { struct xtrx_fe_obj* fe; xtrx_debug_ctx_t* debugif; - clock_val_t refclock; + unsigned refclock; xtrx_clock_source_t clock_source; bool refclock_checked; @@ -119,6 +117,9 @@ struct xtrx_dev { xtrxdsp_filter_state_t tx_host_filter[2]; /* For A & B channels */ unsigned tx_host_inter; /* Interpolation on HOST */ unsigned rx_host_decim; /* Decimation on HOST */ + + unsigned gpio_cfg_funcs; + unsigned gpio_cfg_dir; }; static const char* _devname(struct xtrx_dev* dev) @@ -126,59 +127,160 @@ static const char* _devname(struct xtrx_dev* dev) return xtrxll_get_name(dev->lldev); } -static int _debug_param_io(void* obj, unsigned param, uint64_t val, uint64_t* oval) + +enum xtrx_reg_idxs { + OSCLATCH = 0, + RXTIME = 1, + TXTIME = 2, + GTIME = 3, + GT_OFF = 4, +}; + +static int _debug_param_io(void* obj, unsigned param, unsigned chno, uint64_t val, uint64_t* oval) { - int res; + int res = -EINVAL; int tmp; struct xtrx_dev* dev = (struct xtrx_dev*)obj; + xtrx_channel_t xch = (XTRX_CH_A << chno); + + unsigned devno = (chno >> 1); + if (devno >= dev->dev_max) { + XTRXLLS_LOG("XTRX", XTRXLL_WARNING, "Incorrect device channel: %d\n", chno); + return -EINVAL; + } + + XTRXLLS_LOG("XTRX", XTRXLL_WARNING, "%s: DEBUG: %x %x %lx\n", + _devname(&dev[devno]), param, chno, val); switch (param) { case DEBUG_RFIC_SPI_WR: - return xtrx_val_set(dev, XTRX_TRX, (xtrx_channel_t)(val >> 32), + return xtrx_val_set(dev, XTRX_TRX, xch, (xtrx_val_t)(XTRX_RFIC_REG_0 + ((val >> 16) & 0x7fff)), val & 0xffff); case DEBUG_RFIC_SPI_RD: - return xtrx_val_get(dev, XTRX_TRX, (xtrx_channel_t)(val >> 32), + return xtrx_val_get(dev, XTRX_TRX, xch, (xtrx_val_t)(XTRX_RFIC_REG_0 + ((val >> 16) & 0x7fff)), oval); + case DEBUG_GET_REFCLK: - *oval = (unsigned)dev->refclock; + *oval = (unsigned)dev[devno].refclock; return 0; case DEBUG_BOARD_TEMP: - res = xtrxll_get_sensor(dev->lldev, 0, &tmp); + res = xtrxll_get_sensor(dev[devno].lldev, 0, &tmp); XTRXLLS_LOG("XTRX", XTRXLL_INFO, "%s: Temp %.1f C\n", - _devname(dev), (double)tmp/256); + _devname(&dev[devno]), (double)tmp/256); *oval = (unsigned)tmp; return res; case DEBUG_BOARD_DAC: - res = -EINVAL; if (oval) { - res = xtrxll_get_sensor(dev->lldev, XTRXLL_DAC_REG, &tmp); + res = xtrxll_get_sensor(dev[devno].lldev, XTRXLL_DAC_REG, &tmp); if (res) return res; *oval = (unsigned)tmp; - XTRXLLS_LOG("XTRX", XTRXLL_INFO, "%s: DAC: %d\n", _devname(dev), tmp); + XTRXLLS_LOG("XTRX", XTRXLL_INFO, "%s: DAC: %d\n", + _devname(&dev[devno]), tmp); } if (val > 0) { - res = xtrxll_set_param(dev->lldev, XTRXLL_PARAM_REF_DAC, val); + res = xtrxll_set_param(dev[devno].lldev, XTRXLL_PARAM_REF_DAC, val); + } + return res; + + case DEBUG_ANT_RX: + if (val != UINT_MAX) { + return dev[devno].fe->ops->set_reg(dev[devno].fe, 0, XTRX_TRX, XTRX_FE_CUSTOM_0, val); + } else { + return dev[devno].fe->ops->get_reg(dev[devno].fe, 0, XTRX_TRX, XTRX_FE_CUSTOM_0, oval); } + case DEBUG_ANT_TX: + if (val != UINT_MAX) { + return dev[devno].fe->ops->set_reg(dev[devno].fe, 0, XTRX_TRX, XTRX_FE_CUSTOM_0 + 1, val); + } else { + return dev[devno].fe->ops->get_reg(dev[devno].fe, 0, XTRX_TRX, XTRX_FE_CUSTOM_0 + 1, oval); + } + case DEBUG_GET_DEVICES: + *oval = dev->dev_max; return 0; + + case DEBUG_GET_RXIQ_ODD: + res = xtrxll_get_sensor(dev[devno].lldev, XTRXLL_TEST_CNT_RXIQ_MALGN, &tmp); + *oval = tmp; return res; - case DEBUG_GET_ANT_RX: - return dev->fe->ops->get_reg(dev->fe, 0, XTRX_TRX, XTRX_FE_CUSTOM_0, oval); - case DEBUG_GET_ANT_TX: - return dev->fe->ops->get_reg(dev->fe, 0, XTRX_TRX, XTRX_FE_CUSTOM_0 + 1, oval); + case DEBUG_GET_RXIQ_MISS: + res = xtrxll_get_sensor(dev[devno].lldev, XTRXLL_TEST_CNT_RXIQ_MALGN, &tmp); + *oval = tmp; + return res; + + case DEBUG_VIO: + if (oval) { + res = xtrxll_get_sensor(dev[devno].lldev, XTRXLL_XTRX_VIO, &tmp); + if (res) + return res; + *oval = (unsigned)tmp; + } + if (val > 0) { + res = xtrxll_set_param(dev[devno].lldev, XTRXLL_PARAM_PWR_VIO, val); + } + return res; + + case DEBUG_V33: + if (oval) { + res = xtrxll_get_sensor(dev[devno].lldev, XTRXLL_XTRX_VGPIO, &tmp); + if (res) + return res; + *oval = (unsigned)tmp; + } + if (val > 0) { + res = xtrxll_set_param(dev[devno].lldev, XTRXLL_PARAM_PWR_VGPIO, val); + } + return res; - case DEBUG_SET_ANT_RX: - return dev->fe->ops->set_reg(dev->fe, 0, XTRX_TRX, XTRX_FE_CUSTOM_0, val); - case DEBUG_SET_ANT_TX: - return dev->fe->ops->set_reg(dev->fe, 0, XTRX_TRX, XTRX_FE_CUSTOM_0 + 1, val); + case DEBUG_FGP_CTRL: + if (oval) { + res = xtrxll_get_sensor(dev[devno].lldev, XTRXLL_EXT_CLK, &tmp); + if (res) + return res; + *oval = (unsigned)tmp; + } + if (val != UINT_MAX) { + res = xtrxll_set_param(dev[devno].lldev, XTRXLL_PARAM_EXT_CLK, val); + } + return res; + case DEBUG_XTRX_GET_REG: { + uint32_t d[2]; + unsigned llreg; + + switch (val) { + case OSCLATCH: llreg = XTRXLL_OSC_LATCHED; break; + case RXTIME: llreg = XTRXLL_RX_TIME; break; + case TXTIME: llreg = XTRXLL_TX_TIME; break; + case GTIME: llreg = XTRXLL_GTIME_SECFRAC; break; + case GT_OFF: llreg = XTRXLL_GTIME_OFF; break; + default: + return -EINVAL; + } + res = xtrxll_get_sensor(dev[devno].lldev, llreg, (int*)&d[0]); + if (res) + return res; + + if (val == GTIME) { + uint64_t v = (uint64_t)d[0] * 1000000000UL + (uint64_t)d[1]; + *oval = v; + } else { + *oval = d[0]; + } + return res; + } + default: + XTRXLLS_LOG("XTRX", XTRXLL_WARNING, "%s: Unknown CMDIDX:%x\n", + _devname(&dev[devno]), param); } + + return -EINVAL; } @@ -235,7 +337,7 @@ int xtrx_open(const char* device, unsigned flags, struct xtrx_dev** outdev) xtrxdsp_init(); - res = xtrx_fe_init(lldev, flags, &dev->fe); + res = xtrx_fe_init(dev, lldev, flags, NULL, &dev->fe); if (res) { XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "%s: Failed to initialize frontend: err=%d\n", _devname(dev), res); @@ -264,21 +366,23 @@ int xtrx_open(const char* device, unsigned flags, struct xtrx_dev** outdev) enum { XTRX_DEVS_MAX = 32 }; -int xtrx_open_multi(unsigned numdevs, const char** devices, unsigned flags, struct xtrx_dev** outdev) +int xtrx_open_multi(const xtrx_open_multi_info_t *dinfo, struct xtrx_dev** outdev) { int res; - int loglevel = flags & XTRX_O_LOGLVL_MASK; - xtrxll_set_loglevel(loglevel); + int loglevel = dinfo->loglevel; + if (loglevel >= 0) { + xtrxll_set_loglevel(loglevel); + } - if (numdevs > XTRX_DEVS_MAX || numdevs == 0) { + if (dinfo->devcount > XTRX_DEVS_MAX || dinfo->devcount == 0) { XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "Incorrect number of XTRXes in the multidevice: %d!\n", - numdevs); + dinfo->devcount); return -EINVAL; } struct xtrxll_dev* lldev[XTRX_DEVS_MAX]; - for (unsigned num = 0; num < numdevs; num++) { - res = xtrxll_open(devices[num], XTRXLL_FULL_DEV_MATCH, &lldev[num]); + for (unsigned num = 0; num < dinfo->devcount; num++) { + res = xtrxll_open(dinfo->devices[num], XTRXLL_FULL_DEV_MATCH, &lldev[num]); if (res) { for (; num > 0; num--) { xtrxll_close(lldev[num - 1]); @@ -290,24 +394,28 @@ int xtrx_open_multi(unsigned numdevs, const char** devices, unsigned flags, stru xtrxdsp_init(); //All devices are claimed - struct xtrx_dev* dev = (struct xtrx_dev*)malloc(sizeof(struct xtrx_dev) * numdevs); + struct xtrx_dev* dev = (struct xtrx_dev*)malloc(sizeof(struct xtrx_dev) * dinfo->devcount); if (dev == NULL) { res = -errno; goto failed_mem; } - memset(dev, 0, sizeof(struct xtrx_dev) * numdevs); + memset(dev, 0, sizeof(struct xtrx_dev) * dinfo->devcount); - for (unsigned num = 0; num < numdevs; num++) { + for (unsigned num = 0; num < dinfo->devcount; num++) { dev[num].dev_idx = num; - dev[num].dev_max = numdevs; + dev[num].dev_max = dinfo->devcount; dev[num].lldev = lldev[num]; dev[num].refclock = 0; dev[num].clock_source = XTRX_CLKSRC_INT; + dev[num].fe = dev[0].fe; - res = xtrx_fe_init(lldev[num], flags, &dev[num].fe); + res = xtrx_fe_init(&dev[num], lldev[num], + (num == 0 ? XTRX_FE_MASTER : 0) | num, + (dinfo->flags & XTRX_OMI_FE_SET) ? dinfo->frontend : NULL, + &dev[num].fe); if (res) { XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "%s: Failed to initialize frontend: err=%d on dev %d/%d\n", - _devname(dev), res, num, numdevs); + _devname(dev), res, num, dinfo->devcount); for (; num > 0; num--) { dev[num - 1].fe->ops->fe_deinit(dev[num - 1].fe); } @@ -315,10 +423,13 @@ int xtrx_open_multi(unsigned numdevs, const char** devices, unsigned flags, stru } } - res = xtrx_debug_init(NULL, &_debug_ops, dev, &dev->debugif); - if (res) { - XTRXLLS_LOG("XTRX", XTRXLL_WARNING, "%s: Failed to initialize debug service: err=%d\n", - _devname(dev), res); + if (dinfo->flags & XTRX_OMI_DEBUGIF) { + res = xtrx_debug_init(NULL, &_debug_ops, dev, &dev->debugif); + if (res) { + XTRXLLS_LOG("XTRX", XTRXLL_WARNING, "%s: Failed to initialize debug service: err=%d\n", + _devname(dev), res); + goto failed_fe; + } } *outdev = dev; @@ -327,12 +438,105 @@ int xtrx_open_multi(unsigned numdevs, const char** devices, unsigned flags, stru failed_fe: free(dev); failed_mem: - for (unsigned num = 0; num < numdevs; num++) { + for (unsigned num = 0; num < dinfo->devcount; num++) { xtrxll_close(lldev[num]); } return res; } +int xtrx_open_string(const char* paramstring, struct xtrx_dev** dev) +{ + int res; + char copypstr[4096]; + char* str; + char* saveptr; + char* ldevices[XTRX_DEVS_MAX] = {0}; + + char* devices = NULL; + char* flags = NULL; + + xtrxll_log_initialize(NULL); + + xtrx_open_multi_info_t params; + memset(¶ms, 0, sizeof(params)); + + params.loglevel = -1; + params.devcount = 1; + params.devices = (const char**)ldevices; + + if (paramstring) { + strncpy(copypstr, paramstring, sizeof(copypstr)); + devices = copypstr; + + char* separator = strstr(copypstr, ";;"); + if (devices == separator) { + devices = NULL; + } + if (separator) { + *separator = 0; + separator += 2; + if (*separator != 0) { + flags = separator; + } + } + } + + if (flags) { + for (str = flags; ; str = NULL) { + char* token = strtok_r(str, ";", &saveptr); + if (token == NULL) + break; + + char* eq = strchr(token, '='); + char* val = NULL; + if (eq) { + *eq = 0; + val = eq + 1; + if (*val == 0) + val = NULL; + } + if (strcmp(token, "loglevel") == 0) { + if (val != NULL) { + params.loglevel = atoi(val) & XTRX_O_LOGLVL_MASK; + + xtrxll_set_loglevel(params.loglevel); + } + } else if (strcmp(token, "fe") == 0) { + params.frontend = val; + params.flags |= XTRX_OMI_FE_SET; + } else if (strcmp(token, "debug") == 0) { + params.flags |= XTRX_OMI_DEBUGIF; + } else { + XTRXLLS_LOG("XTRX", XTRXLL_ERROR, + "xtrx_open(): unknown flag '%s' with value '%s'\n", + token, val); + } + } + } + + if (devices) { + int j; + for (j = 0, str = devices; j < XTRX_DEVS_MAX; j++, str = NULL) { + char* token = strtok_r(str, ";", &saveptr); + if (token == NULL) + break; + ldevices[j] = token; + XTRXLLS_LOG("XTRX", XTRXLL_INFO, "xtrx_open(): dev[%d]='%s'\n", + j, ldevices[j]); + } + if (j == 0) { + XTRXLLS_LOG("XTRX", XTRXLL_INFO, "xtrx_open(): no devices were found\n"); + return -ENOENT; + } + params.devcount = j; + } + + res = xtrx_open_multi(¶ms, dev); + if (res) + return res; + + return params.devcount; +} void xtrx_close(struct xtrx_dev* dev) { @@ -373,7 +577,7 @@ int xtrx_set_ref_clk(struct xtrx_dev* dev, unsigned refclkhz, xtrx_clock_source_ res = xtrxll_set_param(dev[devnum].lldev, XTRXLL_PARAM_EXT_CLK, - (dev[devnum].clock_source) ? 1 : 0); + (dev[devnum].clock_source) ? XTRXLL_CLK_EXT_NOPD : XTRXLL_CLK_INT); if (res) { XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "%s: Unable to set clock source\n", _devname(&dev[devnum])); @@ -397,8 +601,10 @@ int xtrx_set_ref_clk(struct xtrx_dev* dev, unsigned refclkhz, xtrx_clock_source_ if (abs(diff) * (int64_t)(1000000/PPM_LIMIT) / base_refclk[i] < 1) { dev->refclock = base_refclk[i]; dev->refclock_checked = true; - XTRXLLS_LOG("XTRX", XTRXLL_INFO, "%s: Set RefClk to %d based on %d measurement\n", - _devname(dev), (int)dev->refclock, osc); + XTRXLLS_LOG("XTRX", XTRXLL_INFO, "%s: Set %s RefClk to %d based on %d measurement\n", + _devname(dev), + (dev->clock_source) ? "EXT" : "INT", + (int)dev->refclock, osc); break; } } @@ -414,8 +620,11 @@ int xtrx_set_ref_clk(struct xtrx_dev* dev, unsigned refclkhz, xtrx_clock_source_ int osc; res = xtrxll_get_sensor(dev[devnum].lldev, XTRXLL_REFCLK_CLK, &osc); if (res) { + XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "%s: Unable to get OSC VAL (%d)\n", + _devname(&dev[devnum]), res); return res; } + if (abs((int)dev->refclock - osc) * (int64_t)(1000000/PPM_LIMIT) / dev->refclock > 1) { XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "%s: RefClk %d doesn't look like %d on master!\n", _devname(&dev[devnum]), osc, (int)dev->refclock); @@ -534,6 +743,9 @@ int xtrx_tune_ex(struct xtrx_dev* dev, xtrx_tune_t type, xtrx_channel_t ch, for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { unsigned fe_ch = (ch >> (2 * devnum)) & XTRX_CH_AB; + if (fe_ch == 0) + continue; + res = dev[devnum].fe->ops->fe_set_freq(dev[devnum].fe, fe_ch, type, freq, actualfreq); if (res) return res; @@ -544,6 +756,9 @@ int xtrx_tune_ex(struct xtrx_dev* dev, xtrx_tune_t type, xtrx_channel_t ch, case XTRX_TUNE_BB_TX: for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { unsigned fe_ch = (ch >> (2 * devnum)) & XTRX_CH_AB; + if (fe_ch == 0) + continue; + res = dev[devnum].fe->ops->bb_set_freq(dev[devnum].fe, fe_ch, type, freq, actualfreq); if (res) return res; @@ -561,6 +776,9 @@ static int xtrx_tune_bandwidth(struct xtrx_dev* dev, xtrx_channel_t xch, unsigned type = (rbb) ? XTRX_TUNE_BB_RX : XTRX_TUNE_BB_TX; for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { unsigned fe_ch = (xch >> (2 * devnum)) & XTRX_CH_AB; + if (fe_ch == 0) + continue; + res = dev[devnum].fe->ops->bb_set_badwidth(dev[devnum].fe, fe_ch, type, bw, actualbw); if (res) return res; @@ -596,6 +814,9 @@ int xtrx_set_gain(struct xtrx_dev* dev, xtrx_channel_t xch, xtrx_gain_type_t gt, int res; for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { unsigned fe_ch = (xch >> (2 * devnum)) & XTRX_CH_AB; + if (fe_ch == 0) + continue; + res = dev[devnum].fe->ops->bb_set_gain(dev[devnum].fe, fe_ch, gt, gain, actualgain); if (res) return res; @@ -605,16 +826,41 @@ int xtrx_set_gain(struct xtrx_dev* dev, xtrx_channel_t xch, xtrx_gain_type_t gt, int xtrx_set_antenna(struct xtrx_dev* dev, xtrx_antenna_t antenna) +{ + return xtrx_set_antenna_ex(dev, XTRX_CH_ALL, antenna); +} + +int xtrx_set_antenna_ex(struct xtrx_dev* dev, xtrx_channel_t ch, xtrx_antenna_t antenna) { int res; for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { - res = dev[devnum].fe->ops->fe_set_lna(dev[devnum].fe, XTRX_CH_AB, 0, antenna); + unsigned fe_ch = (ch >> (2 * devnum)) & XTRX_CH_AB; + if (fe_ch == 0) + continue; + + res = dev[devnum].fe->ops->fe_set_lna(dev[devnum].fe, fe_ch, 0, antenna); if (res) return res; } return 0; } +static unsigned _xtrx_ticks_to_ns(unsigned refclk, unsigned val) +{ + return ((uint64_t)1000000000U) * val / refclk; +} + +static unsigned _xtrx_ns_to_ticks(unsigned refclk, unsigned ns) +{ + return (unsigned)(((uint64_t)ns) * refclk / 1000000000); +} + +static int _xtrx_ns_to_ticks_i(int refclk, int ns) +{ + return (int)(((int64_t)ns) * refclk / 1000000000); +} + + /** * @brief xtrx_wire_format_get_iq_size Get size of symbol IQ pair on the wire * @param stream Stream @@ -663,7 +909,8 @@ static void xtrx_run_params_stream_init(xtrx_run_stream_params_t* stream) stream->wfmt = XTRX_WF_16; stream->hfmt = XTRX_IQ_FLOAT32; stream->chs = XTRX_CH_AB; - stream->paketsize = (DEF_BUFSIZE / 2) / 2; /* IQ and 16 bit each */ + stream->paketsize = 0; + stream->flags = 0; } void xtrx_run_params_init(xtrx_run_params_t* params) @@ -764,6 +1011,11 @@ int xtrx_run_ex(struct xtrx_dev* dev, const xtrx_run_params_t* params) for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { fe_params.rx.chs = (params->rx.chs >> (2 * devnum)) & XTRX_CH_AB; fe_params.tx.chs = (params->tx.chs >> (2 * devnum)) & XTRX_CH_AB; + if (fe_params.rx.chs == 0 && fe_params.tx.chs == 0) { + XTRXLLS_LOG("XTRX", XTRXLL_INFO, "%s: Skipping RX/TX configuration\n", + _devname(&dev[devnum])); + continue; + } res = dev[devnum].fe->ops->dd_set_modes(dev[devnum].fe, XTRX_FEDD_CONFIGURE, &fe_params); if (res) @@ -950,13 +1202,24 @@ int xtrx_run_ex(struct xtrx_dev* dev, const xtrx_run_params_t* params) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { - res = xtrxll_dma_start(dev[devnum].lldev, - chan, - rx_fe_fmt, - (xtrxll_mode_t)(rx_mode | rx_mode_flags), - rx_start_ts, - tx_fe_fmt, - tx_mode); + struct xtrxll_dmaop op; + op.rxfe = rx_fe_fmt; + op.rxmode = (xtrxll_mode_t)(rx_mode | rx_mode_flags); + op.txfe = tx_fe_fmt; + op.txmode = tx_mode; + op.rx_start_sample = rx_start_ts; + + if (params->nflags & XTRX_RUN_GTIME) { + op.gtime_sec = params->gtime.sec; + op.gtime_frac = _xtrx_ns_to_ticks(dev->refclock, params->gtime.nsec); + } else { + op.gtime_sec = 0; + op.gtime_frac = 0; + } + + op.gidx = 0; + + res = xtrxll_dma_start(dev[devnum].lldev, chan, &op); if (res) { XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "%s: Unable to start DMA err=%d\n", _devname(&dev[devnum]), res); @@ -1029,13 +1292,18 @@ int xtrx_stop(struct xtrx_dev* dev, xtrx_direction_t dir) for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { res = dev[devnum].fe->ops->dd_set_modes(dev[devnum].fe, XTRX_FEDD_RESET, &fe_params); - //if (res) - // return res; - res = xtrxll_dma_start(dev[devnum].lldev, chan, - (dir & XTRX_RX) ? XTRXLL_FE_STOP : XTRXLL_FE_DONTTOUCH, XTRXLL_FE_MODE_MIMO, - 0, - (dir & XTRX_TX) ? XTRXLL_FE_STOP : XTRXLL_FE_DONTTOUCH, XTRXLL_FE_MODE_MIMO); + struct xtrxll_dmaop op; + op.rxfe = (dir & XTRX_RX) ? XTRXLL_FE_STOP : XTRXLL_FE_DONTTOUCH; + op.rxmode = XTRXLL_FE_MODE_MIMO; + op.txfe = (dir & XTRX_TX) ? XTRXLL_FE_STOP : XTRXLL_FE_DONTTOUCH; + op.txmode = XTRXLL_FE_MODE_MIMO; + op.rx_start_sample = 0; + + op.gtime_sec = 0; + op.gtime_frac = 0; + + res = xtrxll_dma_start(dev[devnum].lldev, chan, &op); } return res; } @@ -1100,6 +1368,7 @@ int xtrx_send_sync_ex(struct xtrx_dev* mdev, xtrx_send_ex_info_t *info) abort(); case -EIO: case -EBUSY: + case -ETIMEDOUT: return res; case 0: dev->txbuf = wire_buffer_ptr; @@ -1345,6 +1614,10 @@ int xtrx_recv_sync_ex(struct xtrx_dev* mdev, xtrx_recv_ex_info_t* info) flags, info->timeout); switch (res) { + case -ETIMEDOUT: + XTRXLLS_LOG("XTRX", XTRXLL_WARNING, "%s: Buffer timed out!\n", _devname(dev)); + return res; + case -EOVERFLOW: info->out_events |= RCVEX_EVENT_OVERFLOW; info->out_resumed_at = dev->rxbuf_ts >> dev->rx_host_decim; @@ -1383,12 +1656,12 @@ int xtrx_recv_sync_ex(struct xtrx_dev* mdev, xtrx_recv_ex_info_t* info) if (user_processed == 0) { info->out_first_sample = dev->rx_samples >> dev->rx_host_decim; } - +/* XTRXLLS_LOG("XTRX", (dev->rxbuf_ts + dev->rxbuf_processed_ts != dev->rx_samples) ? XTRXLL_WARNING : XTRXLL_DEBUG, "%s: Total=%u Processed=%u UserTotal=%u UserProcessed=%u BUFTS=%" PRIu64 "+%" PRIu64 " OURTS=%" PRIu64 "\n", _devname(dev), dev->rxbuf_total, dev->rxbuf_processed, (unsigned)user_total, (unsigned)user_processed, dev->rxbuf_ts, dev->rxbuf_processed_ts, dev->rx_samples); - +*/ /* bytes need to fill in user */ size_t remaining = user_total - user_processed; unsigned bcnt = single_ch_streaming ? 1 : 2; @@ -1578,6 +1851,8 @@ static int _xtrx_val_set_int(struct xtrx_dev* dev, xtrx_direction_t dir, xtrx_channel_t chan, xtrx_val_t type, uint64_t val) { if (type >= XTRX_RFIC_REG_0 && type <= XTRX_RFIC_REG_0 + 65535) { + XTRXLLS_LOG("XTRX", XTRXLL_INFO, "%s: FE REG %x %x\n", + _devname(dev), type, val); return dev->fe->ops->set_reg(dev->fe, chan, dir, type, val); } @@ -1613,7 +1888,7 @@ XTRX_API int xtrx_val_set(struct xtrx_dev* dev, xtrx_direction_t dir, for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { unsigned fe_ch = (chan >> 2 * devnum) & XTRX_CH_AB; if (fe_ch) { - res = _xtrx_val_set_int(dev, dir, fe_ch, type, val); + res = _xtrx_val_set_int(&dev[devnum], dir, fe_ch, type, val); if (res) { break; } @@ -1653,7 +1928,14 @@ static int _xtrx_val_get_int(struct xtrx_dev* dev, xtrx_direction_t dir, *oval = val; } return res; - + case XTRX_REF_REFCLK: + if (!dev->refclock_checked) { + res = xtrx_set_ref_clk(dev, 0, dev->clock_source); + if (res) + return res; + } + *oval = dev->refclock; + return 0; case XTRX_LMS7_DATA_RATE: case XTRX_LMS7_RSSI: return dev->fe->ops->get_reg(dev->fe, chan, dir, type, oval); @@ -1669,6 +1951,12 @@ static int _xtrx_val_get_int(struct xtrx_dev* dev, xtrx_direction_t dir, *oval = val; } return res; + case XTRX_TX_TIME: + res = xtrxll_get_sensor(dev->lldev, XTRXLL_TX_TIME, &val); + if (!res) { + *oval = val; + } + return res; default: return -EINVAL; } @@ -1681,7 +1969,7 @@ XTRX_API int xtrx_val_get(struct xtrx_dev* dev, xtrx_direction_t dir, for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { unsigned fe_ch = (chan >> 2 * devnum) & XTRX_CH_AB; if (fe_ch) { - res = _xtrx_val_get_int(dev, dir, fe_ch, type, oval); + res = _xtrx_val_get_int(&dev[devnum], dir, fe_ch, type, oval); if (res) { break; } @@ -1690,6 +1978,311 @@ XTRX_API int xtrx_val_get(struct xtrx_dev* dev, xtrx_direction_t dir, return res; } +static int _xtrx_gpio_configure(struct xtrx_dev* dev, + int gpio_num, xtrx_gpio_func_t function) +{ + unsigned gfunc = 0; + unsigned dir = 0; + int res; + + switch (function) { + case XTRX_GPIO_FUNC_IN: gfunc = 0; break; + case XTRX_GPIO_FUNC_OUT: gfunc = 0; dir = 1; break; + case XTRX_GPIO_FUNC_ALT0: gfunc = 1; break; + case XTRX_GPIO_FUNC_ALT1: gfunc = 2; break; + case XTRX_GPIO_FUNC_ALT2: gfunc = 3; break; + default: + if (gpio_num == XTRX_GPIO_ALL) { + return -EINVAL; + } + if (gpio_num >= XTRX_GPIOS_TOTAL) { + return -EINVAL; + } + + if ((function == XTRX_GPIO_FUNC_PPS_O && (gpio_num == XTRX_GPIO_PPS_O || gpio_num == XTRX_GPIO_EPPS_O)) || + ((function == XTRX_GPIO_FUNC_PPS_I && gpio_num == XTRX_GPIO_PPS_I))) { + gfunc = 1; + break; + } + return -EINVAL; + } + + if (gpio_num == XTRX_GPIO_ALL) { + dev->gpio_cfg_funcs = 0; + dev->gpio_cfg_dir = 0; + for (unsigned i = 0; i < XTRX_GPIOS_TOTAL; i++) { + dev->gpio_cfg_funcs |= gfunc << (2 * i); + dev->gpio_cfg_dir |= dir << (2 * i); + } + } else { + unsigned msk = 0x3U << (2 * gpio_num); + + dev->gpio_cfg_funcs = (~msk & dev->gpio_cfg_funcs) | (gfunc << (2 * gpio_num)); + dev->gpio_cfg_dir = (~msk & dev->gpio_cfg_dir) | (dir << (2 * gpio_num)); + } + + res = xtrxll_set_param(dev->lldev, XTRXLL_PARAM_GPIO_DIR, dev->gpio_cfg_dir); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, XTRXLL_PARAM_GPIO_FUNC, dev->gpio_cfg_funcs); + if (res) + return res; + + return 0; +} + +XTRX_API int xtrx_gpio_configure(struct xtrx_dev* dev, int devno, + int gpio_num, xtrx_gpio_func_t function) +{ + if (devno >= (int)dev->dev_max) { + return -EINVAL; + } + if (devno >= 0) { + return _xtrx_gpio_configure(&dev[devno], gpio_num, function); + } + + for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { + int res = _xtrx_gpio_configure(&dev[devnum], gpio_num, function); + if (res) + return res; + } + + return 0; +} + +static int _xtrx_gpio_out(struct xtrx_dev* dev, unsigned out) +{ + return xtrxll_set_param(dev->lldev, XTRXLL_PARAM_GPIO_OUT, out); +} + +XTRX_API int xtrx_gpio_out(struct xtrx_dev* dev, int devno, unsigned out) +{ + if (devno >= (int)dev->dev_max) { + return -EINVAL; + } + if (devno >= 0) { + return _xtrx_gpio_out(&dev[devno], out); + } + + for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { + int res = _xtrx_gpio_out(&dev[devnum], out); + if (res) + return res; + } + + return 0; +} + +static int _xtrx_gpio_clear_set(struct xtrx_dev* dev, + unsigned clear_msk, unsigned set_msk) +{ + enum { + MAX_MSK = (1U << XTRX_GPIOS_TOTAL) - 1, + }; + if (clear_msk > MAX_MSK || set_msk > MAX_MSK) + return -EINVAL; + + unsigned cmd = (clear_msk << XTRX_GPIOS_TOTAL) | set_msk; + return xtrxll_set_param(dev->lldev, XTRXLL_PARAM_GPIO_CS, cmd); +} + +XTRX_API int xtrx_gpio_clear_set(struct xtrx_dev* dev, int devno, + unsigned clear_msk, unsigned set_msk) +{ + if (devno >= (int)dev->dev_max) { + return -EINVAL; + } + if (devno >= 0) { + return _xtrx_gpio_clear_set(&dev[devno], clear_msk, set_msk); + } + + for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { + int res = _xtrx_gpio_clear_set(&dev[devnum], clear_msk, set_msk); + if (res) + return res; + } + + return 0; +} + +XTRX_API int xtrx_gpio_in(struct xtrx_dev* dev, int devno, unsigned* in) +{ + if ((unsigned)devno >= dev->dev_max) { + return -EINVAL; + } + + return xtrxll_get_sensor(dev->lldev, XTRXLL_GPIO_IN, (int*)in); +} + +static int _xtrx_gtime_ctrl(struct xtrx_dev* dev, + bool external, unsigned isec) +{ + int res; + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_GTIME_RESET, 1); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_GTIME_SETCMP, + dev->refclock - 1); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_GTIME_CTRL, + (external) ? XTRXLL_GTIME_EXT_PPSFW : XTRXLL_GTIME_INT_ISO); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_ISOPPS_CTRL, + /*(external) ? XTRXLL_GISO_DISABLE :*/ XTRXLL_GISO_PPSFW); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_PPSDO_CTRL, + /*(external) ? XTRXLL_PPSDO_DISABLE :*/ XTRXLL_PPSDO_INT_GPS); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_GTIME_RESET, 0); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, XTRXLL_PARAM_ISOPPS_SETTIME, isec); + if (res) + return res; + + return res; +} + +static int _xtrx_gtime_op(struct xtrx_dev* dev, + struct xtrx_dev* mdev, + xtrx_gtime_cmd_t cmd, + gtime_data_t in, + gtime_data_t *out) +{ + int res = -EINVAL; + + switch (cmd) { + case XTRX_GTIME_ENABLE_INT: { + return _xtrx_gtime_ctrl(dev, false, in.sec); + } + case XTRX_GTIME_ENABLE_INT_WEXT: + case XTRX_GTIME_ENABLE_INT_WEXTE: { + res = _xtrx_gpio_configure(dev, + cmd == XTRX_GTIME_ENABLE_INT_WEXTE ? XTRX_GPIO_EPPS_O : XTRX_GPIO_PPS_O, + XTRX_GPIO_FUNC_PPS_O); + if (res) + return res; + + res = _xtrx_gpio_configure(dev, XTRX_GPIO_PPS_I, XTRX_GPIO_FUNC_PPS_I); + if (res) + return res; + + return _xtrx_gtime_ctrl(dev, true, in.sec); + } + case XTRX_GTIME_ENABLE_EXT: { + res = _xtrx_gpio_configure(dev, XTRX_GPIO_PPS_I, XTRX_GPIO_FUNC_PPS_I); + if (res) + return res; + + return _xtrx_gtime_ctrl(dev, true, in.sec); + } + case XTRX_GTIME_DISABLE: { + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_GTIME_RESET, 1); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_GTIME_CTRL, + XTRXLL_GTIME_DISABLE); + if (res) + return res; + + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_ISOPPS_CTRL, + XTRXLL_GISO_DISABLE); + if (res) + return res; + break; + } + case XTRX_GTIME_GET_RESOLUTION: { + out->nsec = _xtrx_ticks_to_ns(1, dev->refclock); + out->sec = 0; + break; + } + case XTRX_GTIME_SET_GENSEC: { + res = xtrxll_set_param(dev->lldev, XTRXLL_PARAM_ISOPPS_SETTIME, in.sec); + break; + } + case XTRX_GTIME_GET_CUR: { + uint32_t tm[2]; + res = xtrxll_get_sensor(dev->lldev, XTRXLL_GTIME_SECFRAC, (int*)&tm); + if (res) + return res; + + out->sec = tm[0]; + out->nsec = _xtrx_ticks_to_ns(dev->refclock, tm[1]); + break; + } + case XTRX_GTIME_APPLY_CORRECTION: { + if (in.sec > 0) + return -E2BIG; + + int corr = _xtrx_ns_to_ticks_i((int)dev->refclock, (int)in.nsec); + if (corr < -(int)dev->refclock/2 || corr > (int)dev->refclock/2) + return -E2BIG; + + res = xtrxll_set_param(dev->lldev, + XTRXLL_PARAM_GTIME_TRIMOFF, + (unsigned)corr); + break; + } + case XTRX_GTIME_GET_GPSPPS_DELTA: { + int data; + res = xtrxll_get_sensor(dev->lldev, XTRXLL_GTIME_OFF, &data); + if (res) + return res; + + out->sec = 0; + out->nsec = _xtrx_ticks_to_ns(dev->refclock, (unsigned)data); + break; + } + } + + return res; +} + +XTRX_API int xtrx_gtime_op(struct xtrx_dev* dev, int devno, + xtrx_gtime_cmd_t cmd, gtime_data_t in, + gtime_data_t *out) +{ + if (devno >= (int)dev->dev_max) { + return -EINVAL; + } + if (dev->refclock < 1) { + XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "%s: RefClock is not set!\n", + _devname(dev)); + return -EFAULT; + } + + if (devno >= 0) { + return _xtrx_gtime_op(&dev[devno], dev, cmd, in, out); + } + for (unsigned devnum = 0; devnum < dev->dev_max; devnum++) { + int res = _xtrx_gtime_op(&dev[devnum], dev, cmd, in, out); + if (res) + return res; + } + + return 0; +} void xtrx_log_setfunc(xtrx_logfunc_t func) diff --git a/xtrx_api.h b/xtrx_api.h index 5c8ea11..83edb7f 100644 --- a/xtrx_api.h +++ b/xtrx_api.h @@ -56,6 +56,12 @@ enum xtrx_flags { XTRX_O_RESET = 0x0100, }; +typedef struct gtime_data { + uint32_t sec; + uint32_t nsec; +} gtime_data_t; + + /** * @brief master_ts */ @@ -70,6 +76,24 @@ typedef uint64_t master_ts; */ XTRX_API int xtrx_open(const char* device, unsigned flags, struct xtrx_dev** dev); +enum { + XTRX_OMI_DEBUGIF = 1, + XTRX_OMI_FE_SET = 2, +}; + +typedef struct xtrx_open_multi_info { + uint32_t flags; + uint32_t flagsex; + + unsigned devcount; + int loglevel; + + const char** devices; + + const char* frontend; + void* reserved[32 - 1]; +} xtrx_open_multi_info_t; + /** Open XTRX composed of multiply devices * @brief xtrx_open_multi * @param numdevs @@ -78,7 +102,28 @@ XTRX_API int xtrx_open(const char* device, unsigned flags, struct xtrx_dev** dev * @param dev * @return */ -XTRX_API int xtrx_open_multi(unsigned numdevs, const char** devices, unsigned flags, struct xtrx_dev** dev); +XTRX_API int xtrx_open_multi(const xtrx_open_multi_info_t* dinfo, struct xtrx_dev** dev); + +/** Open XTRX device form semicolon separated device list + * @param paramstring Path to XTRX devices, semicolon separated followed by double semicolon and flags + * @param[out] dev XTRX device handle + * @return number of devices on success, errno on error + * + * String should not contain any whitespaces, all names should be in ASCII with + * ending 0 character + * + * Examples: + * NULL -- just first enumerated device and open with default parameters + * "usb3380" -- Open usb3380 XTRX + * ";;loglevel=7" -- Open first enumerated with specific arguments + * "/dev/xtrx0;/dex/xtrx1;;fe=octoRFX6;loglevel=4" + * + * When @ref devices is NULL only first enumerated device is created. + * Only 'loglevel' flag is parsed. + */ +XTRX_API int xtrx_open_string(const char* paramstring, struct xtrx_dev** dev); + + /** Close XTRX device * @param dev XTRX device handle @@ -220,7 +265,7 @@ typedef enum xtrx_antenna { XTRX_RX_H, XTRX_RX_W, - XTRX_TX_L, + XTRX_TX_H, XTRX_TX_W, XTRX_RX_L_LB, // loopback @@ -229,10 +274,13 @@ typedef enum xtrx_antenna { XTRX_RX_AUTO, // automatic selection XTRX_TX_AUTO, // automatic selection + XTRX_RX_ADC_EXT, // External ADC input } xtrx_antenna_t; XTRX_API int xtrx_set_antenna(struct xtrx_dev* dev, xtrx_antenna_t antenna); +XTRX_API int xtrx_set_antenna_ex(struct xtrx_dev* dev, xtrx_channel_t ch, xtrx_antenna_t antenna); + typedef enum xtrx_wire_format { XTRX_WF_8 = 1, XTRX_WF_12 = 2, @@ -255,6 +303,7 @@ typedef enum xtrx_host_format { typedef enum xtrx_run_params_flags { XTRX_RUN_DIGLOOPBACK = 1, XTRX_RUN_RXLFSR = 2, + XTRX_RUN_GTIME = 4, } xtrx_run_params_flags_t; typedef enum xtrx_run_sp_flags { @@ -271,6 +320,8 @@ typedef enum xtrx_run_sp_flags { XTRX_STREAMDSP_1 = 512, XTRX_STREAMDSP_2 = 1024, + + XTRX_RSP_SWAP_IQA = 2048, /* swap IQ only in one channel A */ } xtrx_run_sp_flags_t; typedef struct xtrx_run_stream_params { @@ -284,17 +335,17 @@ typedef struct xtrx_run_stream_params { xtrx_channel_t chs; /** Default packet size in samples (counted for the single channel) */ - uint16_t paketsize; + uint32_t paketsize; /** Flags of the stream, see xtrx_run_sp_flags_t*/ - uint16_t flags; + uint32_t flags; /** Optional scale value for XTRX_IQ_FLOAT32, it'll be [-scale, scale], * by default it's [-1;1] */ float scale; /** Reserved for future extension to keep ABI structure the same size */ - uint32_t reserved[12 - 5]; + uint32_t reserved[12 - 6]; } xtrx_run_stream_params_t; @@ -311,8 +362,102 @@ typedef struct xtrx_run_params { * any xtrx_send_burst_sync() call */ void* tx_repeat_buf; + + gtime_data_t gtime; + + /** Reserved for future extension to keep ABI structure the same size */ + uint32_t reserved[8]; } xtrx_run_params_t; + +typedef enum xtrx_gtime_cmd { + XTRX_GTIME_ENABLE_INT, /**< Time is ignored, applied immediate */ + XTRX_GTIME_ENABLE_INT_WEXT, /**< Internal with ext generation */ + XTRX_GTIME_ENABLE_INT_WEXTE, + XTRX_GTIME_ENABLE_EXT, + XTRX_GTIME_DISABLE, + XTRX_GTIME_GET_RESOLUTION, + XTRX_GTIME_SET_GENSEC, + XTRX_GTIME_GET_CUR, + XTRX_GTIME_APPLY_CORRECTION, + XTRX_GTIME_GET_GPSPPS_DELTA, + //XTRX_GTIME_ENABLE_AT_GPSPPS, +} xtrx_gtime_cmd_t; + +XTRX_API int xtrx_gtime_op(struct xtrx_dev* dev, int devno, + xtrx_gtime_cmd_t cmd, gtime_data_t in, + gtime_data_t *out); + +enum xtrx_gpios { + XTRX_GPIO_ALL = -1, + + XTRX_GPIO1 = 0, + XTRX_GPIO_PPS_I = XTRX_GPIO1, + + XTRX_GPIO2 = 1, + XTRX_GPIO_PPS_O = XTRX_GPIO2, + + XTRX_GPIO3 = 2, + XTRX_GPIO_TDD = XTRX_GPIO3, + + XTRX_GPIO4 = 3, + + XTRX_GPIO5 = 4, + XTRX_GPIO_LED_WWAN = XTRX_GPIO5, + + XTRX_GPIO6 = 5, + XTRX_GPIO_LED_WLAN = XTRX_GPIO6, + + XTRX_GPIO7 = 6, + XTRX_GPIO_LED_WPAN = XTRX_GPIO7, + + XTRX_GPIO8 = 7, + + XTRX_GPIO9 = 8, + XTRX_GPIO_EXT0 = XTRX_GPIO9, + + XTRX_GPIO10 = 9, + XTRX_GPIO_EXT1 = XTRX_GPIO10, + + XTRX_GPIO11 = 10, + XTRX_GPIO_EXT2 = XTRX_GPIO11, + + XTRX_GPIO12 = 11, + XTRX_GPIO_EXT3 = XTRX_GPIO12, + XTRX_GPIO_EPPS_O = XTRX_GPIO12, + + // Pseudo GPIOs + XTRX_LED = 12, + + XTRX_SAFE = 13, + + XTRX_GPIOS_TOTAL = 14, +}; + +typedef enum xtrx_gpio_func { + XTRX_GPIO_FUNC_IN, + XTRX_GPIO_FUNC_OUT, + + // special function + XTRX_GPIO_FUNC_PPS_O, + XTRX_GPIO_FUNC_PPS_I, + + // gpio specific funcs + XTRX_GPIO_FUNC_ALT0, + XTRX_GPIO_FUNC_ALT1, + XTRX_GPIO_FUNC_ALT2, +} xtrx_gpio_func_t; + +XTRX_API int xtrx_gpio_configure(struct xtrx_dev* dev, int devno, + int gpio_num, xtrx_gpio_func_t function); + +XTRX_API int xtrx_gpio_out(struct xtrx_dev* dev, int devno, unsigned out); + +XTRX_API int xtrx_gpio_clear_set(struct xtrx_dev* dev, int devno, + unsigned clear_msk, unsigned set_msk); + +XTRX_API int xtrx_gpio_in(struct xtrx_dev* dev, int devno, unsigned* in); + /** * @brief xtrx_run_params_init Initialize parameters with default values * @param params Parameters to initialize @@ -432,6 +577,8 @@ typedef enum xtrx_val { XTRX_LML_PHY_FBPHASE, XTRX_DSPFE_CMD, + XTRX_TX_TIME, + /* Performance counters */ XTRX_PERF_SAMPLES = 0x3000, XTRX_PERF_UNOVFLOW, @@ -441,6 +588,9 @@ typedef enum xtrx_val { * is used as index to RFIC onboard */ XTRX_RFIC_REG_0 = 0x10000000, XTRX_FE_CUSTOM_0 = 0x20000000, + + /* For internal use only */ + XTRX_DEBUG_0 = 0x30000000, } xtrx_val_t; XTRX_API int xtrx_val_set(struct xtrx_dev* dev, xtrx_direction_t dir, diff --git a/xtrx_debug.c b/xtrx_debug.c index a1f3790..8f3cb65 100644 --- a/xtrx_debug.c +++ b/xtrx_debug.c @@ -48,66 +48,93 @@ struct xtrx_debug_ctx int clifd; }; -int xtrx_debug_process_cmd(xtrx_debug_ctx_t* ctx, const char *cmd, unsigned len, +enum cmdvtype { + CMD_VT_NONE, + CMD_VT_DEC, +}; + +int xtrx_debug_process_cmd(xtrx_debug_ctx_t* ctx, char *cmd, unsigned len, char* reply, unsigned rlen) { uint64_t oval = 0; + uint64_t param = 0; + unsigned device = 0; int res = -EINVAL; - if (strncmp(cmd, "LMS", 3) == 0) { - unsigned reg = 0; - char chans = 0; - int cnt = sscanf(cmd + 4, "%c,%x", &chans, ®); - if (cnt == 2) { - uint64_t ch = (chans == 'A') ? 1 : - (chans == 'B') ? 2 : - (chans == 'C') ? 3 : 0; - XTRXLLS_LOG("DBGP", XTRXLL_INFO, "LMS write to 0x%08x (%c => %d)\n", - reg, chans, (int)ch); - uint64_t v = (ch << 32) | reg; - res = ctx->ops->param_io(ctx->obj, - (reg & 0x80000000) ? DEBUG_RFIC_SPI_WR : DEBUG_RFIC_SPI_RD, - v, - &oval); - } else { - XTRXLLS_LOG("DBGP", XTRXLL_ERROR, "LMS failed to parse! %d\n", cnt); + int j; + char *pptr[4] = {NULL,}; + char *str1 = NULL, *saveptr1 = NULL, *token = NULL; + enum dubug_cmd dcmd; + enum cmdvtype vt = CMD_VT_NONE; + + XTRXLLS_LOG("DBGP", XTRXLL_DEBUG, "got cmd: %s\n", cmd); + + for (j = 0, str1 = cmd; j < 4; j++, str1 = NULL) { + token = strtok_r(str1, ",", &saveptr1); + if (token == NULL) + break; + + pptr[j] = token; + } + + if (strcmp(cmd, "LMS") == 0) { + if (j < 3) { + goto incorrect_format; } - } else if(strncmp(cmd, "TMP", 3) == 0) { - res = ctx->ops->param_io(ctx->obj, DEBUG_BOARD_TEMP, 0, &oval); - } else if(strncmp(cmd, "DAC", 3) == 0) { - unsigned nval = 0; - sscanf(cmd + 4, "%d", &nval); - - res = ctx->ops->param_io(ctx->obj, DEBUG_BOARD_DAC, nval, &oval); - } else if(strncmp(cmd, "FREF", 4) == 0) { - res = ctx->ops->param_io(ctx->obj, DEBUG_GET_REFCLK, 0, &oval); - } else if(strncmp(cmd, "RDANT", 5) == 0) { - uint64_t arx, atx; - res = ctx->ops->param_io(ctx->obj, DEBUG_GET_ANT_RX, 0, &arx); - if (res) - goto fail; - res = ctx->ops->param_io(ctx->obj, DEBUG_GET_ANT_TX, 0, &atx); - if (res) - goto fail; - - oval = (atx << 2) | arx; - } else if(strncmp(cmd, "WRANT", 5) == 0) { - int nant = 0; - sscanf(cmd + 6, "%d", &nant); - unsigned arx = nant & 3, atx = (nant >> 2) & 1; - - res = ctx->ops->param_io(ctx->obj, DEBUG_SET_ANT_RX, arx, &oval); - if (res) - goto fail; - res = ctx->ops->param_io(ctx->obj, DEBUG_SET_ANT_TX, atx, &oval); - if (res) - goto fail; + param = strtol(pptr[2], NULL, 16); + dcmd = (param & 0x80000000) ? DEBUG_RFIC_SPI_WR : DEBUG_RFIC_SPI_RD; + } else if (strcmp(cmd, "TMP") == 0) { + dcmd = DEBUG_BOARD_TEMP; + } else if(strcmp(cmd, "DAC") == 0) { + dcmd = DEBUG_BOARD_DAC; + vt = CMD_VT_DEC; + } else if(strcmp(cmd, "FREF") == 0) { + dcmd = DEBUG_GET_REFCLK; + } else if(strcmp(cmd, "ANTRX") == 0) { + dcmd = DEBUG_ANT_RX; + vt = CMD_VT_DEC; + param = UINT_MAX; + } else if(strcmp(cmd, "ANTTX") == 0) { + dcmd = DEBUG_ANT_TX; + vt = CMD_VT_DEC; + param = UINT_MAX; + } else if (strcmp(cmd, "DEVS") == 0) { + dcmd = DEBUG_GET_DEVICES; + } else if (strcmp(cmd, "RXIQA") == 0) { + dcmd = DEBUG_GET_RXIQ_ODD; + } else if (strcmp(cmd, "RXIQB") == 0) { + dcmd = DEBUG_GET_RXIQ_MISS; + } else if (strcmp(cmd, "VIO") == 0) { + dcmd = DEBUG_VIO; + vt = CMD_VT_DEC; + } else if (strcmp(cmd, "VGP") == 0) { + dcmd = DEBUG_V33; + vt = CMD_VT_DEC; + } else if (strcmp(cmd, "FGP") == 0) { + dcmd = DEBUG_FGP_CTRL; + vt = CMD_VT_DEC; + param = UINT_MAX; + } else if (strcmp(cmd, "GETREG") == 0) { + dcmd = DEBUG_XTRX_GET_REG; + vt = CMD_VT_DEC; + param = UINT_MAX; } else { - XTRXLLS_LOG("DBGP", XTRXLL_ERROR, "unrecognised command! %c\n", cmd[0]); + XTRXLLS_LOG("DBGP", XTRXLL_ERROR, "unrecognized command! %s\n", cmd); + goto incorrect_format; } -fail: + if (j > 1) { + device = strtol(pptr[1], NULL, 16); + } + + if (vt != CMD_VT_NONE && j > 2) { + param = strtoll(pptr[2], NULL, 10); + } + + res = ctx->ops->param_io(ctx->obj, dcmd, device, param, &oval); + +incorrect_format: if (res == 0) { - return snprintf(reply, rlen, "OK,%016" PRId64 "\n", oval); + return snprintf(reply, rlen, "OK,%016" PRIx64 "\n", oval); } else { return snprintf(reply, rlen, "FAIL,%d\n", res); } @@ -184,6 +211,9 @@ static void* _xtrx_thread(void* param) continue; } + // Terminate string + *end = 0; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); replen = xtrx_debug_process_cmd(ctx, buffer, end - buffer, reply, sizeof(reply)); diff --git a/xtrx_debug.h b/xtrx_debug.h index ab31b97..8d14e17 100644 --- a/xtrx_debug.h +++ b/xtrx_debug.h @@ -30,15 +30,20 @@ enum dubug_cmd { DEBUG_RFIC_SPI_RD, DEBUG_BOARD_TEMP, DEBUG_GET_REFCLK, - DEBUG_GET_ANT_RX, - DEBUG_GET_ANT_TX, - DEBUG_SET_ANT_RX, - DEBUG_SET_ANT_TX, + DEBUG_ANT_RX, + DEBUG_ANT_TX, DEBUG_BOARD_DAC, + DEBUG_GET_DEVICES, + DEBUG_GET_RXIQ_ODD, + DEBUG_GET_RXIQ_MISS, + DEBUG_VIO, + DEBUG_V33, + DEBUG_FGP_CTRL, + DEBUG_XTRX_GET_REG, }; struct xtrx_debug_ops { - int (*param_io)(void* obj, unsigned param, uint64_t val, uint64_t* oval); + int (*param_io)(void* obj, unsigned param, unsigned chno, uint64_t val, uint64_t* oval); }; typedef struct xtrx_debug_ops xtrx_debug_ops_t; diff --git a/xtrx_fe.c b/xtrx_fe.c index a4485c0..10f6160 100644 --- a/xtrx_fe.c +++ b/xtrx_fe.c @@ -18,25 +18,62 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xtrx_fe.h" -#include "xtrxll_api.h" +#include +#include -#ifdef HAVE_LMS_NFE int lms7nfe_init(struct xtrxll_dev* lldev, - unsigned flags, - struct xtrx_fe_obj** obj); -#else -int lms7fe_init(struct xtrxll_dev* lldev, - unsigned flags, - struct xtrx_fe_obj** obj); + unsigned flags, + const char* fename, + struct xtrx_fe_obj** obj); + +int lms7octo_init(struct xtrxll_dev* lldev, + unsigned flags, + const char* fename, + struct xtrx_fe_obj** obj); + +int auto_init(struct xtrxll_dev* lldev, + unsigned flags, + const char* fename, + struct xtrx_fe_obj** obj) +{ +#if 0 + int res = lms7octo_init(lldev, flags, fename, obj); + if (res == 0 || (res && res != -ENODEV)) + return res; #endif + return lms7nfe_init(lldev, flags, fename, obj); +} + +typedef int (*fe_function_t)(struct xtrxll_dev* lldev, + unsigned flags, + const char* fename, + struct xtrx_fe_obj** obj); -int xtrx_fe_init(struct xtrxll_dev* lldev, +struct fe_dictionary { + const char* fename; + fe_function_t init; +}; + +int xtrx_fe_init(struct xtrx_dev* dev, + struct xtrxll_dev *lldev, unsigned flags, + const char* fename, struct xtrx_fe_obj** obj) { -#ifdef HAVE_LMS_NFE - return lms7nfe_init(lldev, flags, obj); -#else - return lms7fe_init(lldev, flags, obj); -#endif + const struct fe_dictionary fes[] = { + { "octoRFX6", lms7octo_init }, + { "lms7", lms7nfe_init }, + { "auto", auto_init } + }; + + if (fename == NULL) + return auto_init(lldev, flags, fename, obj); + + for (unsigned i = 0; i < sizeof(fes) / sizeof(fes[0]); i++) { + if (strncmp(fename, fes[i].fename, strlen(fes[i].fename)) == 0) + return fes[i].init(lldev, flags, fename, obj); + } + + // No frontend were found + return -EINVAL; } diff --git a/xtrx_fe.h b/xtrx_fe.h index 81dc46a..44fe8a9 100644 --- a/xtrx_fe.h +++ b/xtrx_fe.h @@ -21,6 +21,7 @@ #define XTRX_FE_H #include +#include "xtrx_api.h" // General abstraction layer of anlog to digital path and wise versa @@ -150,9 +151,15 @@ struct xtrx_fe_ops int (*fe_deinit)(struct xtrx_fe_obj* obj); }; -int xtrx_fe_init(struct xtrxll_dev* lldev, +#define GET_DEV_FROM_FLAGS(f) ((f) & 0xff) +enum { + XTRX_FE_MASTER = 0x1000, +}; + +int xtrx_fe_init(struct xtrx_dev* dev, + struct xtrxll_dev* lldev, unsigned flags, + const char* opts, struct xtrx_fe_obj** obj); - #endif //XTRX_FE_H diff --git a/xtrx_fe_nlms7.c b/xtrx_fe_nlms7.c index 4cff277..c5a5e86 100644 --- a/xtrx_fe_nlms7.c +++ b/xtrx_fe_nlms7.c @@ -17,7 +17,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "xtrx_fe.h" #include #include #include @@ -28,20 +27,14 @@ #include #include -#include "../liblms7002m/liblms7002m.h" - #include "xtrx_api.h" +#include "xtrx_fe_nlms7.h" + enum { MIN_TX_RATE = 2100000, /* 2.1e6 Minimum samplerate supported by the XTRX hardware */ }; -typedef struct xtrx_bparam -{ - bool set; - unsigned value; -} xtrx_bparam_t; - static void bparam_set_null(xtrx_bparam_t* p) { p->set = false; @@ -53,6 +46,7 @@ static void bparam_set_val(xtrx_bparam_t* p, unsigned val) p->value = val; } +#if 0 typedef struct xtrx_bparamu8 { bool set; @@ -69,68 +63,8 @@ static void bparamu8_set_val(xtrx_bparam_t* p, unsigned val) p->set = true; p->value = val; } +#endif -struct xtrx_nfe_lms7 -{ - struct xtrx_fe_obj base; - - struct xtrxll_dev* lldev; - struct lms7_state lms_state; - - double cgen_clk; - - unsigned lmsnum; - unsigned refclock; - unsigned refclk_source; - - bool rx_no_siso_map; - bool tx_no_siso_map; - - bool tx_run_a; - bool tx_run_b; - - bool rx_run_a; - bool rx_run_b; - - bool rx_port_1; - - uint8_t rx_mmcm_div; - uint8_t tx_mmcm_div; - uint8_t rx_port_cfg; - uint8_t tx_port_cfg; - - bool rx_lna_auto; - bool tx_lna_auto; - - unsigned rx_host_decim; - unsigned tx_host_inter; - - unsigned rxcgen_div; - unsigned txcgen_div; - unsigned rxtsp_div; /* Div ratio at LML */ - unsigned rxtsp_decim; /* Decimation in TSP */ - unsigned txtsp_div; - unsigned txtsp_interp; /* Interpolation in TSP */ - - unsigned txant; - unsigned rxant; - - double rx_lo; - double tx_lo; - - struct lml_map maprx; - struct lml_map maptx; - - enum lml_mode lml_mode; - unsigned lml_txdiv; - unsigned lml_rxdiv; - - xtrx_bparam_t tx_bw[2]; - xtrx_bparam_t rx_bw[2]; - - xtrx_bparam_t tx_dsp[2]; - xtrx_bparam_t rx_dsp[2]; -}; enum xtrxll_lms7_pwr { XTRXLL_LMS7_RESET_PIN = 1<<1, @@ -207,15 +141,15 @@ static int _xtrx_set_lna_rx(struct xtrx_nfe_lms7 *dev, int band) static int _xtrx_set_lna_tx(struct xtrx_nfe_lms7 *dev, int band) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "%s: Set TX band to %d\n", - xtrxll_get_name(dev->lldev), band); + XTRXLLS_LOG("LMSF", XTRXLL_INFO, "%s: Set TX band to %d (%c)\n", + xtrxll_get_name(dev->lldev), band, (band == 1) ? 'H' : 'W'); int res = lms7_trf_set_path(&dev->lms_state, band); if (res) return res; dev->txant = (band == 1) ? 1 : 0; - return xtrxll_set_param(dev->lldev, XTRXLL_PARAM_SWITCH_TX_ANT, dev->rxant); + return xtrxll_set_param(dev->lldev, XTRXLL_PARAM_SWITCH_TX_ANT, dev->txant); } @@ -251,6 +185,9 @@ static int _xtrx_signal_event(struct xtrx_nfe_lms7 *dev, enum sigtype t) (dev->rx_lo > 1500e6) ? RFE_LNAW : RFE_LNAL; XTRXLLS_LOG("LMSF", XTRXLL_INFO, "%s: Auto RX band selection: %s\n", xtrxll_get_name(dev->lldev), get_band_name(band)); + res = lms7_mac_set(&dev->lms_state, LMS7_CH_AB); + if (res) + return res; res = _xtrx_set_lna_rx(dev, band); } break; @@ -258,8 +195,12 @@ static int _xtrx_signal_event(struct xtrx_nfe_lms7 *dev, enum sigtype t) case XTRX_TX_LNA_CHANGED: if (dev->tx_lna_auto) { int band = (dev->tx_lo > 2200e6) ? 1 : 2; - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "%s: Auto TX band selection: %d\n", - xtrxll_get_name(dev->lldev), band); + XTRXLLS_LOG("LMSF", XTRXLL_INFO, "%s: Auto TX band selection: %s\n", + xtrxll_get_name(dev->lldev), + band == 1 ? "H (Band1)" : "W (Band2)"); + res = lms7_mac_set(&dev->lms_state, LMS7_CH_AB); + if (res) + return res; res = _xtrx_set_lna_tx(dev, band); } break; @@ -318,8 +259,9 @@ static int _xtrx_channel_to_lms7(unsigned xch, enum lms7_mac_mode* out) } int lms7nfe_init(struct xtrxll_dev* lldev, - unsigned flags, - struct xtrx_fe_obj** obj) + unsigned flags, + const char *fename, + struct xtrx_fe_obj** obj) { struct xtrx_nfe_lms7 *dev; int lmscnt = 0; @@ -346,7 +288,7 @@ int lms7nfe_init(struct xtrxll_dev* lldev, usleep(10000); - res = xtrxll_set_param(dev->lldev, XTRXLL_PARAM_FE_CTRL, PWR_CTRL_BUSONLY); + res = xtrxll_set_param(dev->lldev, XTRXLL_PARAM_PWR_CTRL, PWR_CTRL_BUSONLY); if (res) { goto failed_lms7; } @@ -828,10 +770,10 @@ static bool _xtrx_run_params_stream_is_mimo(const struct xtrx_dd_chpar* stream) !(stream->flags & XTRX_RSP_SISO_MODE)); } -static const struct lml_map _get_lml_portcfg(const struct xtrx_dd_chpar* par, +const struct lml_map lms7nfe_get_lml_portcfg(const struct xtrx_dd_chpar* par, bool no_siso_map) { - static const struct lml_map diqarray[12] = { + static const struct lml_map diqarray[16] = { // MIMO modes {{ LML_BI, LML_AI, LML_BQ, LML_AQ }}, {{ LML_BQ, LML_AQ, LML_BI, LML_AI }}, @@ -842,11 +784,16 @@ static const struct lml_map _get_lml_portcfg(const struct xtrx_dd_chpar* par, {{ LML_AQ, LML_AQ, LML_AI, LML_AI }}, {{ LML_BI, LML_BI, LML_BQ, LML_BQ }}, {{ LML_BQ, LML_BQ, LML_BI, LML_BI }}, - // MIMO test modes + // MIMO test modes (swap IQ_B) {{ LML_BQ, LML_AI, LML_BI, LML_AQ }}, {{ LML_BI, LML_AQ, LML_BQ, LML_AI }}, {{ LML_AQ, LML_BI, LML_AI, LML_BQ }}, {{ LML_AI, LML_BQ, LML_AQ, LML_BI }}, + // MIMO test modes (swap IQ_A) + {{ LML_BI, LML_AQ, LML_BQ, LML_AI }}, + {{ LML_BQ, LML_AI, LML_BI, LML_AQ }}, + {{ LML_AI, LML_BQ, LML_AQ, LML_BI }}, + {{ LML_AQ, LML_BI, LML_AI, LML_BQ }}, }; unsigned diqidx = 0; @@ -860,6 +807,8 @@ static const struct lml_map _get_lml_portcfg(const struct xtrx_dd_chpar* par, diqidx |= 4; else if (par->flags & XTRX_RSP_SWAP_IQB) diqidx |= 8; + else if (par->flags & XTRX_RSP_SWAP_IQA) + diqidx |= 12; assert(diqidx < (sizeof(diqarray)/sizeof(diqarray[0]))); return diqarray[diqidx]; @@ -910,7 +859,7 @@ int lms7nfe_dd_configure(struct xtrx_nfe_lms7* dev, return -EINVAL; } rx_lmschan = _corr_ch(rx_lmschan, params->rx.flags); - dev->maprx = _get_lml_portcfg(¶ms->rx, dev->rx_no_siso_map); + dev->maprx = lms7nfe_get_lml_portcfg(¶ms->rx, dev->rx_no_siso_map); rxafen_a = rx_lmschan != LMS7_CH_B; rxafen_b = rx_lmschan != LMS7_CH_A; @@ -920,7 +869,7 @@ int lms7nfe_dd_configure(struct xtrx_nfe_lms7* dev, return -EINVAL; } tx_lmschan = _corr_ch(tx_lmschan, params->tx.flags); - dev->maptx = _get_lml_portcfg(¶ms->tx, dev->tx_no_siso_map); + dev->maptx = lms7nfe_get_lml_portcfg(¶ms->tx, dev->tx_no_siso_map); txafen_a = tx_lmschan != LMS7_CH_B; txafen_b = tx_lmschan != LMS7_CH_A; @@ -982,6 +931,8 @@ int lms7nfe_dd_configure(struct xtrx_nfe_lms7* dev, unsigned tsf = (ich == 0) ? XTRX_RSP_TEST_SIGNAL_A : XTRX_RSP_TEST_SIGNAL_B; if (params->rx.flags & tsf) { + XTRXLLS_LOG("LMSF", XTRXLL_INFO,"%s: RBB Applying test tone on %c\n", + xtrxll_get_name(dev->lldev), (ich == 0) ? 'A' : 'B'); res = lms7_rxtsp_tsg_tone(&dev->lms_state, false, false); if (res) return res; @@ -1001,64 +952,7 @@ int lms7nfe_dd_configure(struct xtrx_nfe_lms7* dev, return res; } } -#if 0 - if ((params->rx.flags & XTRX_RSP_TEST_SIGNAL_A) && (rx_lmschan & LMS7_CH_A)) { - res = lms7_mac_set(&dev->lms_state, LMS7_CH_A); - if (res) - return res; - res = lms7_rxtsp_tsg_tone(&dev->lms_state, false, false); - if (res) - return res; - } - if ((params->rx.flags & XTRX_RSP_TEST_SIGNAL_B) && (rx_lmschan & LMS7_CH_B)) { - res = lms7_mac_set(&dev->lms_state, LMS7_CH_A); - if (res) - return res; - res = lms7_rxtsp_tsg_tone(&dev->lms_state, false, false); - if (res) - return res; - } - - // Restore requested BW - if (dev->rx_bw[0].set && (rx_lmschan & LMS7_CH_A)) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "RBB: Restore BW[A]=%d\n", dev->rx_bw[0].value); - res = lms7_mac_set(&dev->lms_state, LMS7_CH_A); - if (res) - return res; - res = lms7_rbb_set_bandwidth(&dev->lms_state, dev->rx_bw[0].value); - if (res) - return res; - } - if (dev->rx_bw[1].set && (rx_lmschan & LMS7_CH_B)) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "RBB: Restore BW[B]=%d\n", dev->rx_bw[1].value); - res = lms7_mac_set(&dev->lms_state, LMS7_CH_B); - if (res) - return res; - res = lms7_rbb_set_bandwidth(&dev->lms_state, dev->rx_bw[1].value); - if (res) - return res; - } - //Restore CMIX configuration - if (dev->rx_dsp[0].set && (rx_lmschan & LMS7_CH_A)) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "RBB: Restore DSP[A]=%d\n", dev->rx_dsp[0].value); - res = lms7_mac_set(&dev->lms_state, LMS7_CH_A); - if (res) - return res; - res = lms7_rxtsp_cmix(&dev->lms_state, dev->rx_dsp[0].value); - if (res) - return res; - } - if (dev->rx_dsp[1].set && (rx_lmschan & LMS7_CH_B)) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "RBB: Restore DSP[B]=%d\n", dev->rx_dsp[1].value); - res = lms7_mac_set(&dev->lms_state, LMS7_CH_B); - if (res) - return res; - res = lms7_rxtsp_cmix(&dev->lms_state, dev->rx_dsp[1].value); - if (res) - return res; - } -#endif dev->rx_run_a = rxafen_a; dev->rx_run_b = rxafen_b; } @@ -1075,7 +969,7 @@ int lms7nfe_dd_configure(struct xtrx_nfe_lms7* dev, res = lms7_trf_enable(&dev->lms_state, txafen_a, txafen_b); if (res) return res; -#if 1 + // Restore settings for (ich = 0; ich < 2; ich++) { enum lms7_mac_mode lch = (ich == 0) ? LMS7_CH_A : LMS7_CH_B; @@ -1088,6 +982,8 @@ int lms7nfe_dd_configure(struct xtrx_nfe_lms7* dev, unsigned tsf = (ich == 0) ? XTRX_RSP_TEST_SIGNAL_A : XTRX_RSP_TEST_SIGNAL_B; if (params->tx.flags & tsf) { + XTRXLLS_LOG("LMSF", XTRXLL_INFO,"%s: TBB Applying test tone on %c\n", + xtrxll_get_name(dev->lldev), (ich == 0) ? 'A' : 'B'); res = lms7_txtsp_tsg_tone(&dev->lms_state, false, false); if (res) return res; @@ -1107,78 +1003,11 @@ int lms7nfe_dd_configure(struct xtrx_nfe_lms7* dev, return res; } } -#endif -#if 0 - if ((params->tx.flags & XTRX_RSP_TEST_SIGNAL_A) && (tx_lmschan & LMS7_CH_A)) { - res = lms7_mac_set(&dev->lms_state, LMS7_CH_A); - if (res) - return res; - res = lms7_txtsp_tsg_tone(&dev->lms_state, false, false); - if (res) - return res; - } - if ((params->tx.flags & XTRX_RSP_TEST_SIGNAL_B) && (tx_lmschan & LMS7_CH_B)) { - res = lms7_mac_set(&dev->lms_state, LMS7_CH_A); - if (res) - return res; - res = lms7_txtsp_tsg_tone(&dev->lms_state, false, false); - if (res) - return res; - } - // Restore requested BW - if (dev->tx_bw[0].set && (tx_lmschan & LMS7_CH_A)) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "TBB: Restore BW[A]=%d\n", dev->tx_bw[0].value); - res = lms7_mac_set(&dev->lms_state, LMS7_CH_A); - if (res) - return res; - res = lms7_tbb_set_bandwidth(&dev->lms_state, dev->tx_bw[0].value); - if (res) - return res; - } - if (dev->tx_bw[1].set && (tx_lmschan & LMS7_CH_B)) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "TBB: Restore BW[B]=%d\n", dev->tx_bw[1].value); - res = lms7_mac_set(&dev->lms_state, LMS7_CH_B); - if (res) - return res; - res = lms7_tbb_set_bandwidth(&dev->lms_state, dev->tx_bw[1].value); - if (res) - return res; - } - //Restore CMIX configuration - if (dev->tx_dsp[0].set && (tx_lmschan & LMS7_CH_A)) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "TBB: Restore DSP[A]=%d\n", dev->tx_dsp[0].value); - res = lms7_mac_set(&dev->lms_state, LMS7_CH_A); - if (res) - return res; - res = lms7_txtsp_cmix(&dev->lms_state, dev->tx_dsp[0].value); - if (res) - return res; - } - if (dev->tx_dsp[1].set && (tx_lmschan & LMS7_CH_B)) { - XTRXLLS_LOG("LMSF", XTRXLL_INFO, "TBB: Restore DSP[B]=%d\n", dev->tx_dsp[1].value); - res = lms7_mac_set(&dev->lms_state, LMS7_CH_B); - if (res) - return res; - res = lms7_txtsp_cmix(&dev->lms_state, dev->tx_dsp[1].value); - if (res) - return res; - } -#endif dev->tx_run_a = txafen_a; dev->tx_run_b = txafen_b; } -#if 0 - // Move away - res = lms7_dc_start(&dev->lms_state, - dev->rx_run_a, dev->rx_run_b, - dev->tx_run_a, dev->tx_run_b); - if (res) - return res; -#endif - - unsigned nlml_mode = dev->lml_mode; if (params->nflags & XTRX_RUN_DIGLOOPBACK) { XTRXLLS_LOG("LMSF", XTRXLL_INFO, "%s: Enable digital loopback\n", @@ -1203,6 +1032,8 @@ int lms7nfe_dd_configure(struct xtrx_nfe_lms7* dev, dev->lml_mode = nlml_mode; } + XTRXLLS_LOG("LMSF", XTRXLL_INFO_LMS, "%s: configure done\n", + xtrxll_get_name(dev->lldev)); return 0; } @@ -1290,6 +1121,14 @@ int lms7nfe_bb_set_freq(struct xtrx_fe_obj* obj, bparam_set_val(&dev->tx_dsp[0], pfreq); if (ch & LMS7_CH_B) bparam_set_val(&dev->tx_dsp[1], pfreq); + + if (dev->tx_run_a || dev->tx_run_b) { + res = lms7_mac_set(&dev->lms_state, ch); + if (res) + return res; + res = lms7_txtsp_cmix(&dev->lms_state, + ch == LMS7_CH_B ? dev->tx_dsp[1].value : dev->tx_dsp[0].value); + } } else { double rx_dac_freq = dev->cgen_clk / dev->rxcgen_div; rel_freq = freq / rx_dac_freq; @@ -1305,23 +1144,14 @@ int lms7nfe_bb_set_freq(struct xtrx_fe_obj* obj, bparam_set_val(&dev->rx_dsp[0], pfreq); if (ch & LMS7_CH_B) bparam_set_val(&dev->rx_dsp[1], pfreq); - } - if (dev->tx_run_a || dev->tx_run_b) { - res = lms7_mac_set(&dev->lms_state, ch); - if (res) - return res; - } - if (type == XTRX_TUNE_BB_TX && (dev->tx_run_a || dev->tx_run_b)) { - if (ch & LMS7_CH_A) - res = lms7_txtsp_cmix(&dev->lms_state, dev->tx_dsp[0].value); - else if (ch & LMS7_CH_B) - res = lms7_txtsp_cmix(&dev->lms_state, dev->tx_dsp[1].value); - } else if (type == XTRX_TUNE_BB_RX && (dev->rx_run_a || dev->rx_run_b)) { - if (ch & LMS7_CH_A) - res = lms7_rxtsp_cmix(&dev->lms_state, dev->rx_dsp[0].value); - else if (ch & LMS7_CH_B) - res = lms7_rxtsp_cmix(&dev->lms_state, dev->rx_dsp[1].value); + if (dev->rx_run_a || dev->rx_run_b) { + res = lms7_mac_set(&dev->lms_state, ch); + if (res) + return res; + res = lms7_rxtsp_cmix(&dev->lms_state, + ch == LMS7_CH_B ? dev->rx_dsp[1].value : dev->rx_dsp[0].value); + } } if (res) return res; @@ -1361,6 +1191,7 @@ int lms7nfe_bb_set_badwidth(struct xtrx_fe_obj* obj, if (dir == XTRX_TUNE_BB_RX) { bparam_set_val(&dev->rx_bw[(j == LMS7_CH_A) ? 0 : 1], bw); + ///////////////// FIXMEEEEEEEEEE!!!!!!!!!!!!!!!!!!!!!!!! res = lms7_rbb_set_path(&dev->lms_state, RBB_LBF); if (res) return res; @@ -1482,53 +1313,38 @@ int lms7nfe_fe_set_freq(struct xtrx_fe_obj* obj, res = lms7_sxx_tune_sync(&dev->lms_state, rx, (unsigned)freq, type == XTRX_TUNE_TX_AND_RX_TDD); - res_freq = freq; - if (res == 0) { - if (actualfreq) - *actualfreq = res_freq; - - if (type == XTRX_TUNE_TX_AND_RX_TDD) { - dev->rx_lo = dev->tx_lo = res_freq; - } else { - if (!rx) { - dev->tx_lo = res_freq; - } else { - dev->rx_lo = res_freq; - } - } + res_freq = freq; //TODO !!!!! + if (res) { + return res; + } - if (type == XTRX_TUNE_TX_AND_RX_TDD || type == XTRX_TUNE_RX_FDD) { - enum lms7_mac_mode mode = (dev->rx_run_a && dev->rx_run_b) ? LMS7_CH_AB : - (dev->rx_run_a) ? LMS7_CH_A : - (dev->rx_run_b) ? LMS7_CH_B : LMS7_CH_NONE; - if (mode != LMS7_CH_NONE) { - res = lms7_mac_set(&dev->lms_state, mode); - if (res) - return res; + if (actualfreq) + *actualfreq = res_freq; - res = _xtrx_signal_event(dev, XTRX_RX_LO_CHANGED); - if (res) - return res; - } + if (type == XTRX_TUNE_TX_AND_RX_TDD) { + dev->rx_lo = dev->tx_lo = res_freq; + } else { + if (!rx) { + dev->tx_lo = res_freq; + } else { + dev->rx_lo = res_freq; } - if (type == XTRX_TUNE_TX_AND_RX_TDD || type == XTRX_TUNE_TX_FDD) { - enum lms7_mac_mode mode = (dev->tx_run_a && dev->tx_run_b) ? LMS7_CH_AB : - (dev->tx_run_a) ? LMS7_CH_A : - (dev->tx_run_b) ? LMS7_CH_B : LMS7_CH_NONE; - if (mode != LMS7_CH_NONE) { - res = lms7_mac_set(&dev->lms_state, mode); - if (res) - return res; + } - res = _xtrx_signal_event(dev, XTRX_TX_LO_CHANGED); - if (res) - return res; - } - } - return 0; + if ((type == XTRX_TUNE_TX_AND_RX_TDD || type == XTRX_TUNE_RX_FDD) && + (dev->rx_run_a || dev->rx_run_b)) { + res = _xtrx_signal_event(dev, XTRX_RX_LO_CHANGED); + if (res) + return res; + } + if ((type == XTRX_TUNE_TX_AND_RX_TDD || type == XTRX_TUNE_TX_FDD) && + (dev->tx_run_a || dev->tx_run_b)) { + res = _xtrx_signal_event(dev, XTRX_TX_LO_CHANGED); + if (res) + return res; } - return res; + return 0; } int lms7nfe_fe_set_lna(struct xtrx_fe_obj* obj, @@ -1553,15 +1369,18 @@ int lms7nfe_fe_set_lna(struct xtrx_fe_obj* obj, case XTRX_RX_L_LB: band = RFE_LBL; tx = 0; break; case XTRX_RX_W_LB: band = RFE_LBW; tx = 0; break; + case XTRX_TX_H: band = 1; tx = 1; break; + case XTRX_TX_W: band = 2; tx = 1; break; - case XTRX_TX_L: band = 1; tx = 1; break; // FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - case XTRX_TX_W: band = 2; tx = 1; break; // FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - case XTRX_RX_AUTO: dev->rx_lna_auto = false; return _xtrx_signal_event(dev, XTRX_RX_LNA_CHANGED); - case XTRX_TX_AUTO: dev->tx_lna_auto = false; return _xtrx_signal_event(dev, XTRX_TX_LNA_CHANGED); + case XTRX_RX_AUTO: dev->rx_lna_auto = true; return _xtrx_signal_event(dev, XTRX_RX_LNA_CHANGED); + case XTRX_TX_AUTO: dev->tx_lna_auto = true; return _xtrx_signal_event(dev, XTRX_TX_LNA_CHANGED); default: return -EINVAL; } + res = lms7_mac_set(&dev->lms_state, ch); + if (res) + return res; + if (tx) { dev->tx_lna_auto = false; return _xtrx_set_lna_tx(dev, band); diff --git a/xtrx_fe_nlms7.h b/xtrx_fe_nlms7.h new file mode 100644 index 0000000..7ede21f --- /dev/null +++ b/xtrx_fe_nlms7.h @@ -0,0 +1,138 @@ +#ifndef XTRX_FE_NLMS7_H +#define XTRX_FE_NLMS7_H + +#include +#include +#include +#include "xtrx_fe.h" + +typedef struct xtrx_bparam +{ + bool set; + unsigned value; +} xtrx_bparam_t; + + +struct xtrx_nfe_lms7 +{ + struct xtrx_fe_obj base; + + struct xtrxll_dev* lldev; + struct lms7_state lms_state; + + double cgen_clk; + + unsigned lmsnum; + unsigned refclock; + unsigned refclk_source; + + bool rx_no_siso_map; + bool tx_no_siso_map; + + bool tx_run_a; + bool tx_run_b; + + bool rx_run_a; + bool rx_run_b; + + bool rx_port_1; + + uint8_t rx_mmcm_div; + uint8_t tx_mmcm_div; + uint8_t rx_port_cfg; + uint8_t tx_port_cfg; + + bool rx_lna_auto; + bool tx_lna_auto; + + unsigned rx_host_decim; + unsigned tx_host_inter; + + unsigned rxcgen_div; + unsigned txcgen_div; + unsigned rxtsp_div; /* Div ratio at LML */ + unsigned rxtsp_decim; /* Decimation in TSP */ + unsigned txtsp_div; + unsigned txtsp_interp; /* Interpolation in TSP */ + + unsigned txant; + unsigned rxant; + + double rx_lo; + double tx_lo; + + struct lml_map maprx; + struct lml_map maptx; + + enum lml_mode lml_mode; + unsigned lml_txdiv; + unsigned lml_rxdiv; + + xtrx_bparam_t tx_bw[2]; + xtrx_bparam_t rx_bw[2]; + + xtrx_bparam_t tx_dsp[2]; + xtrx_bparam_t rx_dsp[2]; +}; + + +int lms7nfe_init(struct xtrxll_dev* lldev, + unsigned flags, + const char* fename, + struct xtrx_fe_obj** obj); + +int lms7nfe_dd_set_samplerate(struct xtrx_fe_obj* obj, + const struct xtrx_fe_samplerate* inrates, + struct xtrx_fe_samplerate* outrates); + +int lms7nfe_dd_set_modes(struct xtrx_fe_obj* obj, + unsigned op, + const struct xtrx_dd_params *params); + +int lms7nfe_bb_set_freq(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned type, + double freq, + double* actualfreq); + +int lms7nfe_bb_set_badwidth(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned dir, + double bw, + double* actualbw); + +int lms7nfe_set_gain(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned gain_type, + double gain, + double *actualgain); + +int lms7nfe_fe_set_freq(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned type, + double freq, + double *actualfreq); + +int lms7nfe_fe_set_lna(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned dir, + unsigned lna); + +int lms7nfe_get_reg(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned dir, + unsigned type, + uint64_t* outval); + +int lms7nfe_set_reg(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned dir, + unsigned type, + uint64_t val); + +int lms7nfe_deinit(struct xtrx_fe_obj* obj); + + +const struct lml_map lms7nfe_get_lml_portcfg(const struct xtrx_dd_chpar* par, + bool no_siso_map); +#endif diff --git a/xtrx_fe_octorx0.c b/xtrx_fe_octorx0.c new file mode 100644 index 0000000..9409fac --- /dev/null +++ b/xtrx_fe_octorx0.c @@ -0,0 +1,1148 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "../liblms7002m/liblms7002m.h" + +#include "xtrx_api.h" +#include "xtrx_fe_nlms7.h" + +enum octo_flags { + LO_SET = 1 << 0, + ADF_INIT = 1 << 1, + + RX_ACTIVE = 1 << 2, //Active streaming on channel + + BW_SET = 1 << 3, + LMS_TDD = 1 << 4, + + RX_DPATH_LMS = 1 << 7, + LO_PORTB_EN = 1 << 8, +}; + +#define MAX_FE_BOARDS 4 +struct xtrx_lms7octo +{ + struct xtrx_fe_obj base; + + struct xtrx_nfe_lms7 *lms; + struct xtrxll_dev *master; + struct xtrx_lms7octo *mdev; + + unsigned flags[2]; + + unsigned devno; + unsigned en_devs; // Valid only on master + + uint8_t trf37_bb_gain[2]; + uint8_t trf37_lpf[2]; + unsigned rx_path[2]; + + double lo; + unsigned gain[2]; + unsigned bw[2]; + + struct xtrx_dd_chpar run_rx; +}; + +#define IS_OCTO_PATH(p) (((p) == XTRX_RX_AUTO) || ((p) == XTRX_RX_ADC_EXT)) +static unsigned get_octo_chans(struct xtrx_lms7octo* dev) +{ + if (dev->flags[0] & RX_DPATH_LMS) { + return ((dev->rx_path[0] == XTRX_RX_ADC_EXT) ? XTRX_CH_A : 0) | + ((dev->rx_path[1] == XTRX_RX_ADC_EXT) ? XTRX_CH_B : 0); + } + + return ((IS_OCTO_PATH(dev->rx_path[0])) ? XTRX_CH_A : 0) | + ((IS_OCTO_PATH(dev->rx_path[1])) ? XTRX_CH_B : 0); +} + +enum { + REG_GPIO_G = 0, + REG_TMP_L = 1, + REG_ADI_H = 2, + REG_TRF_H = 3, + REG_I2C_H = 4, + + REG_RD_STAT = 8, + REG_RD_I2CL = 9, + REG_RD_I2CH = 10, +}; + +static void _lms7octo_init_base(struct xtrx_lms7octo *dev); + +#define MAKE_ADF4355_R0(a,p,n) \ + ((((a) & 1) << 21) | (((p) & 1) << 20) | (((n) & 0xffff) << 4) | 0x0) +#define MAKE_ADF4355_R1(f) \ + ((((f) & 0xffffff) << 4) | 0x1) +#define MAKE_ADF4355_R2(f, m) \ + ((((f) & 0x3fff) << 18) | (((m) & 0x3fff) << 4) | 0x2) +#define MAKE_ADF4355_R3(sdr, phr, pha, p) \ + ((((sdr) & 1) << 30) | (((phr) & 1) << 29) | (((pha) & 1) << 28) | (((p) & 0xffffff) << 4) | 0x3) + +#define MAKE_ADF4355_R4(mxo, rdbr, ddbr, r, dbuf, cs, refm, mux, pdp, pd, cps, cr) \ + ((((mxo) & 7) << 27) | \ + (((rdbr) & 1) << 26) | \ + (((ddbr) & 1) << 25) | \ + (((r) & 0x3ff) << 15) | \ + (((dbuf) & 1) << 14) | \ + (((cs) & 0xf) << 10) | \ + (((refm) & 1) << 9) | \ + (((mux) & 1) << 8) | \ + (((pdp) & 1) << 7) | \ + (((pd) & 1) << 6) | \ + (((cps) & 1) << 5) | \ + (((cr) & 1) << 4) | \ + 0x4) +#define MAKE_ADF4355_R5() 0x800025 +#define MAKE_ADF4355_R6(gb, nb, fs, rfds, cpbc, mtld, auxen, auxop, rfen, rfop) \ + ((((gb) & 1) << 30) | \ + (((nb) & 1) << 29) | \ + (((0xc) & 0xf) << 25) | \ + (((fs) & 1) << 24) | \ + (((rfds) & 0x7) << 21) | \ + (((cpbc) & 0xff) << 13) | \ + (((mtld) & 1) << 11) | \ + (((auxen) & 1) << 9) | \ + (((auxop) & 3) << 7) | \ + (((rfen) & 1) << 6) | \ + (((rfop) & 3) << 4) | \ + 0x6) +#define MAKE_ADF4355_R7(les, ldcc, lolm, fracnld, lodm) \ + ((((0x4) & 0x3f) << 26) | \ + (((les) & 1) << 25) | \ + (((ldcc) & 0x3) << 8) | \ + (((lolm) & 1) << 7) | \ + (((fracnld) & 0x3) << 5) | \ + (((lodm) & 1) << 4) | \ + 0x7) + +#define MAKE_ADF4355_R8() 0x1A69A6B8 +#define MAKE_ADF4355_R9(vcob, to, slt) \ + ((((vcob) & 0xff) << 24) | \ + (((to) & 0x3ff) << 14) | \ + (((0x1f) & 0x1f) << 9) | \ + (((slt) & 0x1f) << 4) | \ + 0x9) +#define MAKE_ADF4355_R10(adccd, adcc, adce) \ + ((((0x3) & 0x3) << 29) | \ + (((adccd) & 0xff) << 6) | \ + (((adcc) & 0x1) << 5) | \ + (((adce) & 0x1) << 4) | \ + 0xa) +#define MAKE_ADF4355_R11() 0x0081200B + +#define MAKE_ADF4355_R12(rc) \ + ((((rc) & 0xffff) << 16) | \ + 0x50c) + + +#define MAKE_OCTO_SPI(cmd, data) \ + ((((cmd) & 0xf) << 28) | ((data) & 0x0fffffff)) + +static int adf4355_spi(struct xtrxll_dev *dev, uint32_t out) +{ + int res; + res = xtrxll_set_param(dev, XTRXLL_PARAM_EXT_SPI, MAKE_OCTO_SPI(REG_TMP_L, out)); + if (res) + return res; + + res = xtrxll_set_param(dev, XTRXLL_PARAM_EXT_SPI, MAKE_OCTO_SPI(REG_ADI_H, out >> 28)); + if (res) + return res; + + usleep(2000); + return 0; +} + + +#define MAKE_TRF37_DEV_SETUP(dcoffcalpd, bbg, lpf, fltb, fg, x2fg, odb3) \ + ((((odb3) & 1) << 31) | \ + (((x2fg) & 1) << 28) | \ + (((fg) & 1) << 27) | \ + (((fltb) & 3) << 25) | \ + (((lpf) & 0xff) << 17) | \ + (((bbg) & 0x1f) << 12) | \ + (((dcoffcalpd) & 1) << 10) | \ + (((1) & 1) << 7) | \ + 9) + +#define MAKE_TRF37_DEV_SETUP2(osctrim, cclk, clkdiv, calsel, idet, qdac, idac, ena) \ + ((((osctrim) & 7) << 29) | \ + (((cclk) & 1) << 28) | \ + (((clkdiv) & 7) << 25) | \ + (((calsel) & 1) << 24) | \ + (((idet) & 3) << 22) | \ + (((qdac) & 0xff) << 14) | \ + (((idac) & 0xff) << 6) | \ + (((ena) & 1) << 5) | \ + 12) + +#define MAKE_TRF37_DEV_SETUP3(b, fc) \ + ((((fc) & 3) << 30) | \ + (((b) & 1) << 29) | \ + 13) + +#define ROTATE2(x) \ + ((((x) & 0x1) << 1) | \ + (((x) & 0x2) >> 1)) +#define XTRX_CH_TO_OCTO(devno, ch) \ + (ROTATE2(ch) << (2*(devno))) + +static int trf37_spi(struct xtrxll_dev *dev, unsigned mask, uint32_t out) +{ + unsigned rotate = 0; + for (unsigned i = 0; i < 32; i++) { + if (out & (1<> 28)); + if (res) + return res; + + usleep(2000); + return 0; +} + +static int octo_read_spi(struct xtrxll_dev *dev, unsigned regno, unsigned* out) +{ + int res = xtrxll_set_param(dev, XTRXLL_PARAM_EXT_SPI, MAKE_OCTO_SPI(regno, 0)); + if (res) + return res; + + usleep(1000); + + return xtrxll_get_sensor(dev, XTRXLL_EXT_SPI_RB, (int*)out); +} + +enum octo_i2c_lut { + OCTO_I2C_TMP = 0, + OCTO_I2C_DAC_TCXO = 1, + OCTO_I2C_DAC_VCOM = 2, +}; +#define MAKE_I2C_CMD(RD, RDZSZ, WRSZ, DEVNO, DATA) (\ + (((RD) & 1U) << 31) | \ + (((RDZSZ) & 7U) << 28) | \ + (((WRSZ) & 3U) << 26) | \ + (((DEVNO) & 3U) << 24) | \ + (((DATA) & 0xffffffu) << 0)) + +static int i2c_cmdwr(struct xtrxll_dev *dev, uint32_t cmd) +{ + int res; + res = xtrxll_set_param(dev, XTRXLL_PARAM_EXT_SPI, + MAKE_OCTO_SPI(REG_TMP_L, cmd >> 4)); + if (res) + return res; + + res = xtrxll_set_param(dev, XTRXLL_PARAM_EXT_SPI, + MAKE_OCTO_SPI(REG_I2C_H, cmd & 0xf)); + if (res) + return res; + + usleep(2000); + return 0; +} + +static int i2c_wait_rb(struct xtrxll_dev *dev) +{ + unsigned oval; + for (int i = 0; i < 200; i++) { + int res = octo_read_spi(dev, REG_RD_STAT, &oval); + if (res) + return res; + + if (oval & (1 << 5)) + return 0; + } + + XTRXLLS_LOG("OCTO", XTRXLL_ERROR, "Failed I2C transaction, status: %08x\n", + oval); + return -EIO; +} + +static int tmp108_get(struct xtrxll_dev* dev, unsigned reg, int *outval) +{ + unsigned i2c_rb; + int res = i2c_cmdwr(dev, MAKE_I2C_CMD(1, 1, 1, OCTO_I2C_TMP, reg)); + if (res) + return res; + + res = i2c_wait_rb(dev); + if (res) + return res; + + res = octo_read_spi(dev, REG_RD_I2CL, &i2c_rb); + if (res) + return res; + + *outval = (int16_t)(htole16(i2c_rb & 0xffff)); + return 0; +} + + +static int dac_set(struct xtrxll_dev* dev, unsigned dno, unsigned val) +{ + uint32_t cmd = (((val >> 8) & 0x0f)) | (((val) & 0xff) << 8); + return i2c_cmdwr(dev, MAKE_I2C_CMD(0, 0, 2, dno, cmd)); +} + +static int dac_get(struct xtrxll_dev* dev, unsigned dno, unsigned* oval) +{ + int res = i2c_cmdwr(dev, MAKE_I2C_CMD(1, 3, 0, dno, 0)); + if (res) + return res; + + res = i2c_wait_rb(dev); + if (res) + return res; + + res = octo_read_spi(dev, REG_RD_I2CL, oval); + return res; +} + + +enum adf4355_flags { + ADF4355_EN_INIT = 1, + ADF4355_EN_A = 2, + ADF4355_EN_B = 4, +}; + +static int adf4355_powerdown(struct xtrxll_dev* dev) +{ + uint32_t adf4 = MAKE_ADF4355_R4(6, 0, 0, 1, 0, 2, 0, 1, 1, 1, 0, 0); + return adf4355_spi(dev, adf4); +} + +static int adf4355_tune(struct xtrxll_dev* dev, uint64_t outfreq, unsigned fref, unsigned flags) +{ + int res; + + static const uint16_t icp_ua[16] = { + 310, + 630, + 940, + 1250, + 1560, + 1880, + 2190, + 2500, + 2810, + 3130, + 3440, + 3750, + 4060, + 4380, + 4690, + 5000, + }; +#define VCO_MIN 3300e6 +#define VCO_MAX 6600e6 + + unsigned ref_doubler = 1; + unsigned fpd = fref << ref_doubler; + unsigned frac; + unsigned intn; + unsigned div; + + unsigned div_val; + uint64_t vco_freq; + for (div = 0, div_val = 1; div_val < 128; div++, div_val <<= 1) { + vco_freq = ((uint64_t)outfreq) * div_val; + if (vco_freq > VCO_MAX) + return -EINVAL; + if (vco_freq >= VCO_MIN) + break; + } + if (div_val > 64) { + return -EINVAL; + } + intn = vco_freq / fpd; + frac = (vco_freq - ((uint64_t)fpd) * intn) * ((uint64_t)1 << 24) / fpd; + + unsigned icp_idx = 2; // Default for the best spurs + if (flags & ADF4355_EN_INIT) { + //if (1) { + unsigned cp_bleed_c = (39 * fpd / 61440000) * icp_ua[icp_idx] / 900; + if (cp_bleed_c > 255) + cp_bleed_c = 255; + unsigned a_en = (flags & ADF4355_EN_A) ? 1 : 0; + unsigned b_en = (flags & ADF4355_EN_B) ? 1 : 0; + unsigned rf_pwr = 2; // -4; -1; +2; +5 dBm pwr level + + unsigned vco_band = (fpd + 2400000 - 1) / 2400000; + if (vco_band > 255) + vco_band = 255; + + unsigned adc_div = ((((fpd + 100000 - 1) / 100000) - 2) + 3) / 4; + if (adc_div < 0) + adc_div = 1; + else if (adc_div > 255) + adc_div = 255; + + uint32_t adfregs[12] = { + MAKE_ADF4355_R12(1), + MAKE_ADF4355_R11(), + MAKE_ADF4355_R10(adc_div, 1, 1), + MAKE_ADF4355_R9(vco_band, 0x0ff, 0x14), + MAKE_ADF4355_R8(), + MAKE_ADF4355_R7(0, 3, 0, 0, 0), + MAKE_ADF4355_R6(0, 0, 1, div, cp_bleed_c, 0, b_en, rf_pwr, a_en, rf_pwr), + MAKE_ADF4355_R5(), + MAKE_ADF4355_R4(6, ref_doubler, 0, 1/*R*/, 0 /* db rfdiv*/, icp_idx, 0, 1, 1, 0, 0, 0), + MAKE_ADF4355_R3(0, 0, 0, 0), + MAKE_ADF4355_R2(0, 130), + MAKE_ADF4355_R1(frac), + }; + for (unsigned i = 0; i < 12; i++) { + XTRXLLS_LOG("OCTO", XTRXLL_INFO_LMS, "ADF OUT %08x\n", adfregs[i]); + int res = adf4355_spi(dev, adfregs[i]); + if (res) { + return res; + } + } + + usleep(1000); + + uint32_t adf0 = MAKE_ADF4355_R0(0, 0, intn); + res = adf4355_spi(dev, adf0); + if (res) { + return res; + } + + usleep(1000); + } + + uint32_t adfregs2[] = { + //MAKE_ADF4355_R10(adc_div, 1, 1), + MAKE_ADF4355_R4(6, ref_doubler, 0, 1/*R*/, 0 /* db rfdiv*/, icp_idx, 0, 1, 1, 0, 0, 1), + MAKE_ADF4355_R2(0, 130), + MAKE_ADF4355_R1(frac), + MAKE_ADF4355_R0(0, 0, intn), + MAKE_ADF4355_R4(6, ref_doubler, 0, 1/*R*/, 0 /* db rfdiv*/, icp_idx, 0, 1, 1, 0, 0, 0), + }; + for (unsigned i = 0; i < sizeof(adfregs2)/sizeof(adfregs2[0]); i++) { + int res = adf4355_spi(dev, adfregs2[i]); + if (res) { + return res; + } + } + + usleep(1000); + + uint32_t adf1 = MAKE_ADF4355_R0(1, 0, intn); + res = adf4355_spi(dev, adf1); + if (res) { + return res; + } + + return 0; +} + +static int lms7octo_update_trf(struct xtrx_lms7octo *dev, unsigned channel) +{ + int res; + unsigned devmask = XTRX_CH_TO_OCTO(dev->devno, channel); + unsigned octo_chans = get_octo_chans(dev); + + uint32_t trf_ref0 = MAKE_TRF37_DEV_SETUP(0, dev->trf37_bb_gain[0], + dev->trf37_lpf[0], 2, 0, 0, 0); + uint32_t trf_ref1 = MAKE_TRF37_DEV_SETUP(0, dev->trf37_bb_gain[1], + dev->trf37_lpf[1], 2, 0, 0, 0); + if (!(octo_chans & XTRX_CH_A) || !(dev->flags[0] & RX_ACTIVE)) { + trf_ref0 |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 10); + } + if (!(octo_chans & XTRX_CH_B) || !(dev->flags[1] & RX_ACTIVE)) { + trf_ref1 |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 10); + } + + XTRXLLS_LOG("OCTO", XTRXLL_INFO_LMS, "OCTO Update TRF37: %02x:%08x:%08x:%02x:%02x\n", + devmask, trf_ref0, trf_ref1, channel, octo_chans); + + if ((channel == XTRX_CH_AB) && (trf_ref0 == trf_ref1)) { + return trf37_spi(dev->master, devmask, trf_ref0); + } + if (channel & XTRX_CH_A) { + res = trf37_spi(dev->master, XTRX_CH_TO_OCTO(dev->devno, XTRX_CH_A), trf_ref0); + if (res) + return res; + } + if (channel & XTRX_CH_B) { + res = trf37_spi(dev->master, XTRX_CH_TO_OCTO(dev->devno, XTRX_CH_B), trf_ref1); + if (res) + return res; + } + + return 0; +} + +static int trf_gain_to(unsigned gain, double *outgain, uint8_t *v) +{ + int ig = (int)gain + 12.5; + if (ig < 0) + ig = 0; + else if (ig > 31) + ig = 31; + + *outgain = ig - 12.5; + *v = ig; + return 0; +} + +static int trf_bw_to(unsigned bw, double *outbw, uint8_t *ov) +{ + if (bw > 30e6) + bw = 30e6; + else if (bw < 1e6) + bw = 1e6; + *outbw = bw; + + // TODO + int v = 255 * (30.000001e6 - bw) / (29e6); + if (v < 0) + v = 0; + else if (v > 255) + v = 255; + + *ov = v; + return 0; +} + +static int lms7octo_lo_tune(struct xtrx_lms7octo *dev) +{ + unsigned fref = 19200000; //TODO + int res = adf4355_tune(dev->master, dev->mdev->lo, fref, + ((dev->mdev->flags[0] & ADF_INIT) ? 0 : ADF4355_EN_INIT) | + ((dev->mdev->flags[0] & LO_PORTB_EN) ? ADF4355_EN_B : 0) | + ADF4355_EN_A); + if (res) + return res; + + XTRXLLS_LOG("OCTO", XTRXLL_INFO, "LO tuned to %.3fMhz", + dev->mdev->lo / 1.0e6); + dev->mdev->flags[0] |= ADF_INIT; + return 0; +} + +static int lms7octo_lo_pd(struct xtrx_lms7octo *dev) +{ + XTRXLLS_LOG("OCTO", XTRXLL_INFO, "LO power down"); + + dev->mdev->flags[0] &= ~ADF_INIT; + return adf4355_powerdown(dev->master); +} + + +enum { + GPIO_DEF_FUNC = + (0 << 26) | (1 << 24) | + (0 << 22) | (0 << 20) | (0 << 18) | (0 << 16) | + (0 << 14) | (1 << 12) | (1 << 10) | (1 << 8) | + (0 << 6) | (0 << 4) | (0 << 2) | (0 << 0), + + GPIO_EXSPI_FUNC = + (1 << 20) | (0 << 18) | (1 << 16), +}; + +int lms7octo_init(struct xtrxll_dev* lldev, + unsigned flags, + const char* fename, + struct xtrx_fe_obj** obj) +{ + struct xtrx_lms7octo *dev; + int res; + int oval; + unsigned devno = GET_DEV_FROM_FLAGS(flags); + + if (*obj != NULL) { + // Check if we at the same FE on non master FE + struct xtrx_lms7octo tmp_dev; + _lms7octo_init_base(&tmp_dev); + + struct xtrx_lms7octo* master = *(struct xtrx_lms7octo**)obj; + if (master->base.ops != tmp_dev.base.ops) { + return -ENODEV; + } + + if (devno >= MAX_FE_BOARDS) { + //return -ENODEV; + return lms7nfe_init(lldev, flags, fename, obj); + } + } + + dev = (struct xtrx_lms7octo*)malloc(sizeof(struct xtrx_lms7octo)); + if (dev == NULL) { + res = -errno; + goto failed_mem; + } + memset(dev, 0, sizeof(struct xtrx_lms7octo)); + if (*obj != NULL) { + struct xtrx_lms7octo* master = *(struct xtrx_lms7octo**)obj; + dev->mdev = master; + dev->master = master->lms->lldev; + } + + dev->flags[0] = dev->flags[1] = 0; + dev->devno = devno; + dev->en_devs = 0; + + res = lms7nfe_init(lldev, flags, fename, (struct xtrx_fe_obj**)&dev->lms); + if (res) + goto failed_lms7; + + if (dev->master == NULL) { + unsigned stat; + + dev->mdev = dev; + dev->master = lldev; + + res = xtrxll_set_param(lldev, XTRXLL_PARAM_GPIO_FUNC, + GPIO_DEF_FUNC | GPIO_EXSPI_FUNC); + if (res) + return res; + + // Init and check + res = octo_read_spi(lldev, REG_RD_STAT, &stat); + if (res) + goto failed_check; + + if ((stat >> 16) != 0xf500) { + bool automode = (fename == NULL) || (strcmp(fename, "auto") == 0); + if (!automode || (stat != 0xffffffff && stat != 0x0)) { + XTRXLLS_LOG("OCTO", + (automode) ? XTRXLL_WARNING : XTRXLL_ERROR, + "FE board reports STAT:%08x\n", + stat); + } + res = -ENODEV; + goto failed_check; + } + XTRXLLS_LOG("OCTO", XTRXLL_INFO_LMS, "OCTO FE STAT:%08x, Master IDX:%d\n", + stat, dev->devno); + + res = xtrxll_set_param(lldev, XTRXLL_PARAM_EXT_SPI, + MAKE_OCTO_SPI(REG_GPIO_G, 0xff)); + if (res) + goto failed_check; + + res = tmp108_get(dev->master, 0, &oval); + XTRXLLS_LOG("OCTO", XTRXLL_INFO, "Board Temperature: %.2fC\n", + oval / 256.0); + if (res) + goto failed_check; + + // Wait for power up + usleep(10000); + + for (unsigned k = 0; k < 10; k++) { + // Set VCM to 0.775V + unsigned val = 775*4095/3300; + res = dac_set(lldev, OCTO_I2C_DAC_VCOM, val); + if (res) + goto failed_check; + + usleep(5000); + res = dac_get(lldev, OCTO_I2C_DAC_VCOM, (unsigned*)&oval); + if (res) + goto failed_check; + + unsigned z = (oval >> 12) & 0xfff; + XTRXLLS_LOG("OCTO", XTRXLL_INFO_LMS, "Vcom = %08x (%d)\n", oval, z); + if (z == val) + break; + } + } + + _lms7octo_init_base(dev); + dev->trf37_bb_gain[0] = dev->trf37_bb_gain[1] = 13; + dev->trf37_lpf[0] = dev->trf37_lpf[1] = 128; + dev->rx_path[0] = dev->rx_path[1] = XTRX_RX_ADC_EXT; + + if (fename && (strcmp(fename, "octoRFX6:clk") == 0)) { + XTRXLLS_LOG("OCTO", XTRXLL_INFO, "AUTO defaults to LMS RX port, use ADC to enable OCTO frontend\n"); + dev->flags[0] |= RX_DPATH_LMS; + } + + *obj = (struct xtrx_fe_obj*)dev; + return 0; + +failed_check: + // Get GPIO state to default + xtrxll_set_param(lldev, XTRXLL_PARAM_GPIO_FUNC, GPIO_DEF_FUNC); + lms7nfe_deinit((struct xtrx_fe_obj*)dev->lms); +failed_lms7: + free(dev); +failed_mem: + return res; +} + +int lms7octo_deinit(struct xtrx_fe_obj* obj) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + int res; + + //Turn off sync board + if (dev->master == dev->lms->lldev) { + res = xtrxll_set_param(dev->master, XTRXLL_PARAM_EXT_SPI, + MAKE_OCTO_SPI(REG_GPIO_G, 0)); + XTRXLLS_LOG("OCTO", XTRXLL_INFO_LMS, "Turn off"); + } + res = lms7nfe_deinit((struct xtrx_fe_obj*)dev->lms); + free(dev); + + return res; +} + +// Switch RX path in BB between internal and extarnal path +static int octo_switch_fe(struct xtrx_lms7octo* dev, + unsigned mod_channel) +{ + int r = 0; + unsigned channel; + unsigned octo_chans = get_octo_chans(dev); + XTRXLLS_LOG("OCTO", XTRXLL_INFO_LMS, "Dev %d: Reconfigure mod=%x octo=%x\n", + dev->devno, mod_channel, octo_chans); + + if ((channel = (mod_channel & octo_chans))) { + if (!(dev->mdev->en_devs & (1 << dev->devno))) { + // Restore saved data + if ((dev->mdev->en_devs == 0) && (dev->mdev->flags[0] & LO_SET)) { + XTRXLLS_LOG("OCTO", XTRXLL_INFO, "Restore LO to %.3f\n", dev->mdev->lo / 1.0e6); + r = (r) ? r : lms7octo_lo_tune(dev); + } + + dev->mdev->en_devs |= (1 << dev->devno); + r = (r) ? r : xtrxll_set_param(dev->master, XTRXLL_PARAM_EXT_SPI, + MAKE_OCTO_SPI(REG_GPIO_G, 0x000000ff | (dev->mdev->en_devs << 8))); + } + + // Reconfigure for external ADC input + r = (r) ? r : lms7_mac_set(&dev->lms->lms_state, channel); + r = (r) ? r : lms7_rfe_disable(&dev->lms->lms_state); + r = (r) ? r : lms7_rbb_set_ext(&dev->lms->lms_state); + + r = (r) ? r : lms7octo_update_trf(dev, channel); + + XTRXLLS_LOG("OCTO", XTRXLL_INFO, "Board:%d LMS7 ch:%d configured for extrnal ADC input; OctoEn: %08x\n", + dev->devno, channel, dev->mdev->en_devs); + } + if ((channel = (mod_channel & ~octo_chans))) { + // Reconfigure for internal ADC input + r = (r) ? r : lms7nfe_set_gain((struct xtrx_fe_obj* )dev->lms, + channel, XTRX_RX_PGA_GAIN, + (channel == XTRX_CH_B) ? dev->gain[1] : dev->gain[0], + NULL); + + r = (r) ? r : lms7nfe_fe_set_lna((struct xtrx_fe_obj* )dev->lms, channel, 0, + (channel == XTRX_CH_B) ? dev->rx_path[1] : dev->rx_path[0]); + + if (dev->flags[0] & BW_SET) { + r = (r) ? r : lms7nfe_bb_set_badwidth((struct xtrx_fe_obj* )dev->lms, + channel, XTRX_TUNE_BB_RX, + (channel == XTRX_CH_B) ? dev->bw[1] : dev->bw[0], + NULL); + } + if (dev->flags[0] & LO_SET) { + r = (r) ? r : lms7nfe_fe_set_freq((struct xtrx_fe_obj* )dev->lms, channel, + (dev->flags[0] & LMS_TDD) ? XTRX_TUNE_TX_AND_RX_TDD : XTRX_TUNE_RX_FDD, + dev->lo, NULL); + } + + if (octo_chans == 0) { + dev->mdev->en_devs &= ~(1 << dev->devno); + r = (r) ? r : xtrxll_set_param(dev->master, XTRXLL_PARAM_EXT_SPI, + MAKE_OCTO_SPI(REG_GPIO_G, 0x000000ff | (dev->mdev->en_devs << 8))); + + if (dev->mdev->en_devs == 0) { + r = (r) ? r : lms7octo_lo_pd(dev->mdev); + } + } + + XTRXLLS_LOG("OCTO", XTRXLL_INFO, "Board:%d LMS7 ch:%d configured for internal path; OctoEn: %08x\n", + dev->devno, channel, dev->mdev->en_devs); + } + + if (r) + return r; + + // Reconfigure LML port to fix I/Q mirror issue + struct xtrx_dd_chpar crx = dev->run_rx; + if ((octo_chans != 0 && octo_chans != XTRX_CH_AB) && + crx.chs == XTRX_CH_AB && !(crx.flags & XTRX_RSP_SISO_MODE)) { + // Both LMS & OctoPath active on MIMO + + if (octo_chans & XTRX_CH_A) { + crx.flags |= XTRX_RSP_SWAP_IQA; + } else { + crx.flags |= XTRX_RSP_SWAP_IQB; + } + } else if ((mod_channel & octo_chans)) { + crx.flags ^= XTRX_RSP_SWAP_IQ; + } + + dev->lms->maprx = lms7nfe_get_lml_portcfg(&crx, dev->lms->rx_no_siso_map); + r = lms7_lml_set_map(&dev->lms->lms_state, + dev->lms->rx_port_1 ? dev->lms->maprx : dev->lms->maptx, + dev->lms->rx_port_1 ? dev->lms->maptx : dev->lms->maprx); + + return r; +} + +int lms7octo_dd_set_samplerate(struct xtrx_fe_obj* obj, + const struct xtrx_fe_samplerate* inrates, + struct xtrx_fe_samplerate* outrates) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + return lms7nfe_dd_set_samplerate((struct xtrx_fe_obj*)dev->lms, + inrates, + outrates); +} + +int lms7octo_dd_set_modes(struct xtrx_fe_obj* obj, + unsigned op, + const struct xtrx_dd_params *params) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + int res; + + switch (op) { + case XTRX_FEDD_CONFIGURE: + if (params->dir & XTRX_RX) { + dev->run_rx = params->rx; + res = lms7nfe_dd_set_modes((struct xtrx_fe_obj*)dev->lms, + op, params); + if (res) + return res; + + // TODO: FIXUP for old software + unsigned enchans = params->rx.chs; + if (enchans == LMS7_CH_AB && (params->rx.flags & XTRX_RSP_SISO_MODE)) { + if (params->rx.flags & XTRX_RSP_SWAP_AB) { + enchans = LMS7_CH_B; + } else { + enchans = LMS7_CH_A; + } + } + if (enchans & LMS7_CH_A) { + dev->flags[0] |= RX_ACTIVE; + } + if (enchans & LMS7_CH_B) { + dev->flags[1] |= RX_ACTIVE; + } + + //XTRXLLS_LOG("OCTO", XTRXLL_INFO_LMS, "ENCH %d\n", enchans); + + res = octo_switch_fe(dev, enchans); + if (res) + return res; + + // Update both TRF, PD one if not using + res = lms7octo_update_trf(dev, XTRX_CH_AB); + if (res) + return res; + } else { + res = lms7nfe_dd_set_modes((struct xtrx_fe_obj*)dev->lms, + op, params); + } + return res; + case XTRX_FEDD_RESET: + if (params->dir & XTRX_RX) { + if (dev->master == dev->lms->lldev) { + xtrxll_set_param(dev->master, XTRXLL_PARAM_EXT_SPI, + MAKE_OCTO_SPI(REG_GPIO_G, 0x000000ff)); + + lms7octo_lo_pd(dev->mdev); + } + dev->flags[0] &= ~RX_ACTIVE; + dev->flags[1] &= ~RX_ACTIVE; + } + return lms7nfe_dd_set_modes((struct xtrx_fe_obj*)dev->lms, + op, params); + } + + return -EINVAL; +} + +int lms7octo_bb_set_freq(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned type, + double freq, + double* actualfreq) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + return lms7nfe_bb_set_freq((struct xtrx_fe_obj*)dev->lms, + channel, + type, + freq, + actualfreq); +} + +int lms7octo_bb_set_badwidth(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned type, + double bw, + double* actualbw) +{ + int res; + unsigned ochmsk = 0; + + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + if (type == XTRX_TUNE_BB_RX) { + if (channel & XTRX_CH_A) { + dev->bw[0] = bw; + dev->flags[0] |= BW_SET; + } + if (channel & XTRX_CH_B) { + dev->bw[1] = bw; + dev->flags[1] |= BW_SET; + } + + /* + bool bypass = (bw > 60e6); + if (bypass) { + uint32_t td3 = MAKE_TRF37_DEV_SETUP3(1, 0); + return trf37_spi(dev->master, + XTRX_CH_TO_OCTO(dev->devno, channel), + td3); + } + */ + uint8_t v; + if (channel & XTRX_CH_A) { + trf_bw_to(dev->bw[0], actualbw, &v); + dev->trf37_lpf[0] = v; + } + if (channel & XTRX_CH_B) { + trf_bw_to(dev->bw[1], actualbw, &v); + dev->trf37_lpf[1] = v; + } + + ochmsk = get_octo_chans(dev); + } + + if (ochmsk & channel) { + res = lms7octo_update_trf(dev, channel); + if (res) + return res; + } + if (~ochmsk & channel) { + res = lms7nfe_bb_set_badwidth((struct xtrx_fe_obj*)dev->lms, + ~ochmsk & channel, + type, + bw, + actualbw); + if (res) + return res; + } + + return 0; +} + +int lms7octo_set_gain(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned gain_type, + double gain, + double *actualgain) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + int res; + unsigned ochmsk = 0; + + if (gain_type == XTRX_RX_PGA_GAIN) { + if (channel & XTRX_CH_A) { + dev->gain[0] = gain; + } + if (channel & XTRX_CH_B) { + dev->gain[1] = gain; + } + + uint8_t bbg; + if (channel & XTRX_CH_A) { + trf_gain_to(gain, actualgain, &bbg); + dev->trf37_bb_gain[0] = bbg; + } + if (channel & XTRX_CH_B) { + trf_gain_to(gain, actualgain, &bbg); + dev->trf37_bb_gain[1] = bbg; + } + + ochmsk = get_octo_chans(dev); + } + + if (ochmsk & channel) { + res = lms7octo_update_trf(dev, channel); + if (res) + return res; + } + if (~ochmsk & channel) { + // Gain settings will override ADC path, filter out not our channels + res = lms7nfe_set_gain((struct xtrx_fe_obj*)dev->lms, + ~ochmsk & channel, + gain_type, + gain, + actualgain); + if (res) + return res; + } + return 0; +} + +#define ABSF(x) (((x) < 0) ? -(x) : (x)) + +int lms7octo_fe_set_freq(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned type, + double freq, + double *actualfreq) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + int res; + unsigned ochmsk = 0; + + if (type == XTRX_TUNE_RX_FDD || type == XTRX_TUNE_TX_AND_RX_TDD) { + ochmsk = get_octo_chans(dev); + } + + XTRXLLS_LOG("OCTO", XTRXLL_INFO_LMS, "LO CH:%d:%d %02x -> %.3f\n", + channel, ochmsk, dev->mdev->flags[0], freq / 1e6); + + if (ochmsk & channel) { + if (!((dev->mdev->flags[0] & LO_SET) && (ABSF(dev->mdev->lo - freq) < 1))) { + dev->mdev->lo = freq; + dev->mdev->flags[0] |= LO_SET; + + //XTRXLLS_LOG("OCTO", XTRXLL_INFO, "Update LO to %.3f Mhz\n", freq / 1.0e6); + res = lms7octo_lo_tune(dev); + if (res) + return res; + } + *actualfreq = freq; + } + if (~ochmsk & channel) { + res = lms7nfe_fe_set_freq((struct xtrx_fe_obj*)dev->lms, + ~ochmsk & channel, + type, + freq, + actualfreq); + if (res) + return res; + } + + if (type == XTRX_TUNE_RX_FDD || type == XTRX_TUNE_TX_AND_RX_TDD) { + dev->lo = freq; + dev->flags[0] |= LO_SET; + if (type == XTRX_TUNE_TX_AND_RX_TDD) { + dev->flags[0] |= LMS_TDD; + } else { + dev->flags[0] &= ~LMS_TDD; + } + } + return 0; +} + +static int is_rx_path(unsigned lna) +{ + switch (lna) { + case XTRX_RX_L: + case XTRX_RX_H: + case XTRX_RX_W: + case XTRX_RX_L_LB: + case XTRX_RX_W_LB: + case XTRX_RX_AUTO: + case XTRX_RX_ADC_EXT: + return true; + } + + return false; +} + +int lms7octo_fe_set_lna(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned dir, + unsigned lna) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + if (!is_rx_path(lna)) { + return lms7nfe_fe_set_lna((struct xtrx_fe_obj*)dev->lms, + channel, + dir, + lna); + } + + unsigned bocto_chans = get_octo_chans(dev); + if (channel & XTRX_CH_A) { + dev->rx_path[0] = lna; + } + if (channel & XTRX_CH_B) { + dev->rx_path[1] = lna; + } + unsigned aocto_chans = get_octo_chans(dev); + + if (bocto_chans != aocto_chans) { + return octo_switch_fe(dev, channel); + } + return 0; +} + +int lms7octo_get_reg(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned dir, + unsigned type, + uint64_t* outval) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + return lms7nfe_get_reg((struct xtrx_fe_obj*)dev->lms, + channel, + dir, + type, + outval); +} + +int lms7octo_set_reg(struct xtrx_fe_obj* obj, + unsigned channel, + unsigned dir, + unsigned type, + uint64_t val) +{ + struct xtrx_lms7octo *dev = (struct xtrx_lms7octo *)obj; + return lms7nfe_set_reg((struct xtrx_fe_obj*)dev->lms, + channel, + dir, + type, + val); +} + +static const struct xtrx_fe_ops _lms7octo_ops = { + lms7octo_dd_set_modes, + lms7octo_dd_set_samplerate, + + lms7octo_bb_set_freq, + lms7octo_bb_set_badwidth, + lms7octo_set_gain, + + lms7octo_fe_set_freq, + lms7octo_fe_set_lna, + lms7octo_set_gain, + + lms7octo_get_reg, + lms7octo_set_reg, + + lms7octo_deinit, +}; + +void _lms7octo_init_base(struct xtrx_lms7octo *dev) +{ + dev->base.ops = &_lms7octo_ops; +}