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/soapy/SoapyXTRX.cpp b/soapy/SoapyXTRX.cpp index fc43221..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 @@ -106,7 +139,7 @@ std::vector SoapyXTRX::listAntennas(const int direction, const size 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; @@ -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) @@ -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 a7a4da4..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++; } } @@ -786,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 @@ -888,6 +880,8 @@ int main(int argc, char** argv) } 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); @@ -895,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) { @@ -902,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) { diff --git a/xtrx.c b/xtrx.c index ad0ef4f..834aa2e 100644 --- a/xtrx.c +++ b/xtrx.c @@ -149,6 +149,9 @@ static int _debug_param_io(void* obj, unsigned param, unsigned chno, uint64_t va 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, xch, @@ -334,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); @@ -363,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]); @@ -389,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); } @@ -414,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; @@ -426,25 +438,51 @@ 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_list(const char* devices, const char *flags, struct xtrx_dev** dev) +int xtrx_open_string(const char* paramstring, struct xtrx_dev** dev) { - int res, j; - char cpstr[4096]; + int res; + char copypstr[4096]; char* str; char* saveptr; - char* ldevices[XTRX_DEVS_MAX]; + 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; - unsigned loglevel = 0; + 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) { - strncpy(cpstr, flags, sizeof(cpstr)); - for (str = cpstr; ; str = NULL) { + for (str = flags; ; str = NULL) { char* token = strtok_r(str, ";", &saveptr); if (token == NULL) break; @@ -459,45 +497,45 @@ int xtrx_open_list(const char* devices, const char *flags, struct xtrx_dev** dev } if (strcmp(token, "loglevel") == 0) { if (val != NULL) { - loglevel = atoi(val) & XTRX_O_LOGLVL_MASK; + params.loglevel = atoi(val) & XTRX_O_LOGLVL_MASK; - xtrxll_set_loglevel(loglevel); + 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_INFO, + XTRXLLS_LOG("XTRX", XTRXLL_ERROR, "xtrx_open(): unknown flag '%s' with value '%s'\n", token, val); } } } - if (!devices || *devices == 0) { - res = xtrx_open(NULL, loglevel, dev); - if (res) - return res; - - return 1; - } - - strncpy(cpstr, devices, sizeof(cpstr)); - for (j = 0, str = cpstr; 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; + 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(j, (const char**)ldevices, loglevel, dev); + res = xtrx_open_multi(¶ms, dev); if (res) return res; - return j; + return params.devcount; } void xtrx_close(struct xtrx_dev* dev) @@ -1618,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; @@ -1813,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); } diff --git a/xtrx_api.h b/xtrx_api.h index a78cf8a..83edb7f 100644 --- a/xtrx_api.h +++ b/xtrx_api.h @@ -76,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 @@ -84,18 +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 devices Path to XTRX devices, semicolon separated (returned from xtrx_discovery) - * @param flags Semicolon separated flags. Can be NULL. + * @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_list(const char* devices, const char* flags, struct xtrx_dev** dev); +XTRX_API int xtrx_open_string(const char* paramstring, struct xtrx_dev** dev); + + /** Close XTRX device * @param dev XTRX device handle @@ -246,6 +274,7 @@ 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); @@ -291,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 { 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 2a71dd0..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; @@ -72,67 +65,6 @@ static void bparamu8_set_val(xtrx_bparam_t* p, unsigned 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, @@ -327,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; @@ -837,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 }}, @@ -851,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; @@ -869,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]; @@ -919,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; @@ -929,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; @@ -1092,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; } 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; +}