yate/modules/radio/ybladerf.cpp

7355 lines
229 KiB
C++

/**
* ybladerf.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* BladeRF radio interface
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2015 Null Team
* Copyright (C) 2015 LEGBA Inc
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <yatephone.h>
#include <yateradio.h>
#include <libusb-1.0/libusb.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
//#define DEBUG_LUSB_TRANSFER_CALLBACK // Debug libusb transfer callback
//#define DEBUGGER_DEVICE_METH // Instantiate a Debugger to trace some device methods
//#define DEBUG_DEVICE_TX // Special (verbose) TX debug
//#define DEBUG_DEVICE_RX // Special (verbose) RX debug
using namespace TelEngine;
namespace { // anonymous
class BrfLibUsbDevice; // A bladeRF device using libusb
class BrfInterface; // A bladeRF radio interface
class BrfModule; // The module
// SPI flash page size, in bytes
#define BRF_FLASH_PAGE_SIZE 256
#define SI5338_F_VCO (38400000UL * 66UL)
// Vendor commands
#define BRF_USB_CMD_QUERY_FPGA_STATUS 1
#define BRF_USB_CMD_BEGIN_PROG 2
#define BRF_USB_CMD_RF_RX 4
#define BRF_USB_CMD_RF_TX 5
#define BRF_USB_CMD_READ_CAL_CACHE 110
#define BRF_SAMPLERATE_MIN 80000u
// Frequency bounds
#define BRF_FREQUENCY_MIN 232500000u
#define BRF_FREQUENCY_MAX 3800000000u
// Frequency offset interval
#define BRF_FREQ_OFFS_DEF 128
#define BRF_FREQ_OFFS_MIN 64
#define BRF_FREQ_OFFS_MAX 192
#define BRF_RXVGA1_GAIN_MIN 5
#define BRF_RXVGA1_GAIN_MAX 30
#define BRF_RXVGA2_GAIN_MIN 0
#define BRF_RXVGA2_GAIN_MAX 30
#define BRF_TXVGA1_GAIN_MIN -35
#define BRF_TXVGA1_GAIN_MAX -4
#define BRF_TXVGA1_GAIN_DEF -16
#define BRF_TXVGA2_GAIN_MIN 0
#define BRF_TXVGA2_GAIN_MAX 25
#define VCO_HIGH 0x02
#define VCO_NORM 0x00
#define VCO_LOW 0x01
#define MAKE_TOKEN_NAME_PREFIX(PREFIX,x) {#x, PREFIX##x}
static inline bool brfIsLowBand(unsigned int hz)
{
return hz < 1500000000u;
}
#define BRF_ALTSET_INVALID -1 // Invalid value
#define BRF_ALTSET_IDLE 0 // Used for idle mode
#define BRF_ALTSET_RF_LINK 1 // Send and receive samples. Also used for
// sending commands to peripherals (VCTCXO, Si5338, LMS6002D)
#define BRF_ALTSET_SPI_FLASH 2 // Update the firmware on the board
#define BRF_ALTSET_FPGA 3 // FPGA operations
#define BRF_ALTSET_MIN BRF_ALTSET_IDLE
#define BRF_ALTSET_MAX BRF_ALTSET_FPGA
static const TokenDict s_altSetDict[] = {
MAKE_TOKEN_NAME_PREFIX(BRF_ALTSET_,INVALID),
MAKE_TOKEN_NAME_PREFIX(BRF_ALTSET_,IDLE),
MAKE_TOKEN_NAME_PREFIX(BRF_ALTSET_,RF_LINK),
MAKE_TOKEN_NAME_PREFIX(BRF_ALTSET_,SPI_FLASH),
MAKE_TOKEN_NAME_PREFIX(BRF_ALTSET_,FPGA),
{0,0}
};
static inline const char* altSetName(int val)
{
return lookup(val,s_altSetDict);
}
// USB endpoints
#define BRF_ENDP_TX_SAMPLES 0x01 // Endpoint for TX data samples
#define BRF_ENDP_TX_CTRL 0x02 // Endpoint for ctrl RF
#define BRF_ENDP_RX_SAMPLES 0x81 // Endpoint for RX data samples
#define BRF_ENDP_RX_CTRL 0x82 // Endpoint for ctrl RF
// DC calibrate modules
// Auto calibration order is given in LMS6002D calibration guide, Section 4.7
#define BRF_CALIBRATE_LPF_TUNING 0 // DC offset cancellation of LPF
#define BRF_CALIBRATE_LPF_BANDWIDTH 1 // LPF bandwidth tunning
#define BRF_CALIBRATE_TX_LPF 2 // DC offset cancellation of TX LPF
#define BRF_CALIBRATE_RX_LPF 3 // DC offset cancellation of RX LPF
#define BRF_CALIBRATE_RX_VGA2 4 // DC offset cancellation of RX VGA2
#define BRF_CALIBRATE_FIRST BRF_CALIBRATE_LPF_TUNING
#define BRF_CALIBRATE_LAST BRF_CALIBRATE_RX_VGA2
#define BRF_CALIBRATE_MAX_SUBMODULES 5
static const TokenDict s_calModuleDict[] = {
MAKE_TOKEN_NAME_PREFIX(BRF_CALIBRATE_,LPF_TUNING),
MAKE_TOKEN_NAME_PREFIX(BRF_CALIBRATE_,LPF_BANDWIDTH),
MAKE_TOKEN_NAME_PREFIX(BRF_CALIBRATE_,TX_LPF),
MAKE_TOKEN_NAME_PREFIX(BRF_CALIBRATE_,RX_LPF),
MAKE_TOKEN_NAME_PREFIX(BRF_CALIBRATE_,RX_VGA2),
{0,0}
};
static inline const char* calModName(int val)
{
return lookup(val,s_calModuleDict);
}
static const char* s_calRxTxLpfNames[] = {"DC_I","DC_Q"};
static const char* s_calRxVga2Names[] = {"VGA2_DC_REF","VGA2A_DC_I","VGA2A_DC_Q","VGA2B_DC_I","VGA2B_DC_Q"};
struct BrfCalDesc
{
uint8_t clkEnMask;
uint8_t addr;
uint8_t subModules;
const char** subModName;
};
static const BrfCalDesc s_calModuleDesc[] = {
// BRF_CALIBRATE_LPF_TUNING
{0x20,0x00,1,0},
// BRF_CALIBRATE_LPF_BANDWIDTH
{0,0,0,0},
// BRF_CALIBRATE_TX_LPF
{0x02,0x30,2,s_calRxTxLpfNames},
// BRF_CALIBRATE_RX_LPF
{0x08,0x50,2,s_calRxTxLpfNames},
// BRF_CALIBRATE_RX_VGA2
{0x10,0x60,5,s_calRxVga2Names}
};
// Maximum values for Rx/Tx DC offset I and Q
#define BRF_RX_DC_OFFSET_MAX 63
#define BRF_TX_DC_OFFSET_MIN -128
#define BRF_TX_DC_OFFSET_MAX 127
static inline int16_t decodeDCOffs(bool tx, uint8_t val)
{
if (tx) {
bool negative = ((val & 0x80) == 0);
return negative ? ((int16_t)val - 128) : (int16_t)(val & 0x7f);
}
bool negative = ((val & 0x40) != 0);
return negative ? -(int16_t)(val & 0x3f) : (int16_t)(val & 0x3f);
}
// Calculate Rx DC offset correction
#define BRF_RX_DC_OFFSET_ERROR 10
#define BRF_RX_DC_OFFSET_COEF 1.5
#define BRF_RX_DC_OFFSET_AVG_DAMPING 1024
#define BRF_RX_DC_OFFSET_DEF (BRF_RX_DC_OFFSET_ERROR * BRF_RX_DC_OFFSET_AVG_DAMPING)
static inline double brfRxDcOffset(double val)
{
return (val * BRF_RX_DC_OFFSET_COEF + BRF_RX_DC_OFFSET_ERROR) *
BRF_RX_DC_OFFSET_AVG_DAMPING;
}
// FPGA correction
#define BRF_FPGA_CORR_MAX 4096
// libusb defaults
#define LUSB_CTRL_TIMEOUT 500 // Control transfer timeout def val (in milliseconds)
#define LUSB_BULK_TIMEOUT 500 // Bulk transfer timeout def val (in milliseconds)
// libusb control transfer
#define LUSB_CTRLTRANS_IFACE_VENDOR (LIBUSB_RECIPIENT_INTERFACE | LIBUSB_REQUEST_TYPE_VENDOR)
#define LUSB_CTRLTRANS_IFACE_VENDOR_IN (LUSB_CTRLTRANS_IFACE_VENDOR | LIBUSB_ENDPOINT_IN)
#define LUSB_CTRLTRANS_DEV_VENDOR (LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR)
#define LUSB_CTRLTRANS_DEV_VENDOR_IN (LUSB_CTRLTRANS_DEV_VENDOR | LIBUSB_ENDPOINT_IN)
#define LUSB_CTRLTRANS_DEV_VENDOR_OUT (LUSB_CTRLTRANS_DEV_VENDOR | LIBUSB_ENDPOINT_OUT)
static const uint64_t s_freqRefClock = 38400000;
static inline unsigned int bytes2samplesf(unsigned int bytes)
{
return bytes / (2 * sizeof(float));
}
static inline unsigned int samplesf2bytes(unsigned int samples)
{
return samples * 2 * sizeof(float);
}
static inline unsigned int samplesi2bytes(unsigned int samples)
{
return samples * 2 * sizeof(int16_t);
}
// Utility: check timeout or cancelled
static inline unsigned int checkCancelled(String* error = 0)
{
if (!Thread::check(false))
return 0;
if (error)
*error = "Cancelled";
return RadioInterface::Cancelled;
}
static String& appendComplex(String& s, float* f, unsigned int n)
{
char c[320];
n /= 2;
if (!(f && n))
return s;
String tmp;
unsigned int a = n / 4;
while (a--) {
::sprintf(c,"(%.3f,%.3f) (%.3f,%.3f) (%.3f,%.3f) (%.3f,%.3f)",f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7]);
f += 8;
tmp.append(c," ");
}
a = n % 4;
while (a--) {
::sprintf(c,"(%.3f,%.3f)",f[0],f[1]);
f += 2;
tmp.append(c," ");
}
return s.append(tmp);
}
static inline String& appendComplex(String& s, DataBlock& d)
{
return appendComplex(s,(float*)d.data(0),d.length() / sizeof(float));
}
static inline const char* onStr(bool on)
{
return on ? "on" : "off";
}
static inline const char* enableStr(bool on)
{
return on ? "enable" : "disable";
}
static inline const char* enabledStr(bool on)
{
return on ? "Enabled" : "Disabled";
}
static inline const char* brfDir(bool tx)
{
return tx ? "TX" : "RX";
}
static inline char mixer(bool pre)
{
return pre ? '1' : '2';
}
static inline char brfIQ(bool i)
{
return i ? 'I' : 'Q';
}
static inline String& dumpFloatG(String& buf, double val, const char* prefix = 0,
const char* suffix = 0)
{
return buf.printf("%s%g%s",TelEngine::c_safe(prefix),val,TelEngine::c_safe(suffix));
}
inline String& addIntervalInt(String& s, int minVal, int maxVal, const char* sep = " ")
{
String tmp;
return s.append(tmp.printf("[%d..%d]",minVal,maxVal),sep);
}
static inline bool retMsgError(NamedList& list, const char* what, const char* param = 0)
{
NamedString* ns = new NamedString("error",what);
if (!TelEngine::null(param))
*ns << " '" << param << "'";
list.setParam(ns);
return false;
}
static inline bool retParamError(NamedList& list, const char* param)
{
if (list.getParam(param))
return retMsgError(list,"Missing parameter",param);
return retMsgError(list,"Invalid parameter",param);
}
static inline bool retValFailure(NamedList& list, unsigned int code)
{
return retMsgError(list,String(code) + " " + RadioInterface::errorName(code));
}
static inline bool getFirstStr(String& dest, String& line)
{
int pos = line.find(' ');
if (pos >= 0) {
dest = line.substr(0,pos);
line = line.substr(pos + 1);
}
else {
dest = line;
line.clear();
}
return !dest.null();
}
// Convert 4 bytes to version string (MSB -> LSB: patch.minor.major)
static inline void ver2str(String& dest, uint32_t ver)
{
dest << (uint8_t)ver << ".";
dest << (uint8_t)(ver >> 8) << ".";
uint16_t patch = (uint8_t)(ver >> 16) | (uint8_t)(ver >> 24);
dest << patch;
}
// Code is expecting this array to have 15 elements
static uint32_t s_bandSet[] = {
1500000u, 1750000u, 2500000u, 2750000u, 3000000u,
3840000u, 5000000u, 5500000u, 6000000u, 7000000u,
8750000u, 10000000u,12000000u,14000000u,20000000u
};
#if 0
static uint32_t s_freqLimits[] = {
BRF_FREQUENCY_MIN, 285625000, 0x27,
285625000, 336875000, 0x2f,
336875000, 405000000, 0x37,
405000000, 465000000, 0x3f,
465000000, 571250000, 0x26,
571250000, 673750000, 0x2e,
673750000, 810000000, 0x36,
810000000, 930000000, 0x3e,
930000000, 1142500000, 0x25,
1142500000, 1347500000, 0x2d,
1347500000, 1620000000, 0x35,
1620000000, 1860000000, 0x3d,
1860000000u, 2285000000u, 0x24,
2285000000u, 2695000000u, 0x2c,
2695000000u, 3240000000u, 0x34,
3240000000u, BRF_FREQUENCY_MAX, 0x3c,
0,0,0
};
#else
static uint32_t s_freqLimits[] = {
BRF_FREQUENCY_MIN, 285625000, 0x27,
285625000, 336875000, 0x2f,
336875000, 405000000, 0x37,
405000000, 475000000, 0x3f,
475000000, 571250000, 0x26,
571250000, 673750000, 0x2e,
673750000, 810000000, 0x36,
810000000, 945000000, 0x3e,
945000000, 1142500000, 0x25,
1142500000, 1350000000, 0x2d,
1350000000, 1620000000, 0x35,
1620000000, 1890000000, 0x3d,
1890000000u, 2285000000u, 0x24,
2285000000u, 2695000000u, 0x2c,
2695000000u, 3240000000u, 0x34,
3240000000u, BRF_FREQUENCY_MAX, 0x3c,
0,0,0
};
#endif
struct BrfRationalRate
{
uint64_t integer;
uint64_t numerator;
uint64_t denominator;
};
struct Si5338MultiSynth
{
uint8_t index; // Multisynth to program (0-3)
uint16_t base; // Base address of the multisynth
BrfRationalRate requested; // Requested sample rates
BrfRationalRate actual; // Actual sample rates
uint8_t enable; // Enables for A and/or B outputs
uint32_t a, b, c, r; // f_out = fvco / (a + b/c) / r
uint32_t p1, p2, p3; // (a, b, c) in multisynth (p1, p2, p3) form
uint8_t regs[10]; // p1, p2, p3) in register form
};
static const uint8_t s_rxvga1_set[BRF_RXVGA1_GAIN_MAX + 1] = {
2, 2, 2, 2, 2, 2, 14, 26, 37, 47, 56, 63, 70, 76, 82, 87,
91, 95, 99, 102, 104, 107, 109, 111, 113, 114, 116, 117, 118, 119, 120,
};
static const uint8_t s_rxvga1_get[121] = {
5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13, 13,
13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 17,
17, 17, 18, 18, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, 22, 22, 23, 24, 24,
25, 25, 26, 27, 28, 29, 30
};
// Utility macros (call methods)
#define BRF_FUNC_CALL_(result,func,cond,instr) { \
result = func; \
if (cond) { instr; } \
}
// Call a method, remember the first error
#define BRF_FUNC_CALL(func) BRF_FUNC_CALL_(unsigned int tmp,func,!status && tmp,status = tmp; error = 0)
#define BRF_FUNC_CALL_BREAK(func) BRF_FUNC_CALL_(status,func,status,break)
#define BRF_FUNC_CALL_RET(func) BRF_FUNC_CALL_(status,func,status,return status)
// Read samples from string
static bool readSamples(DataBlock& buf, const String& list)
{
ObjList* l = list.split(',');
unsigned int n = l->count();
if (n < 2 || (n % 2) != 0) {
TelEngine::destruct(l);
return false;
}
buf.resize(n * sizeof(float));
float* f = (float*)buf.data(0);
for (ObjList* o = l->skipNull(); f && o; o = o->skipNext()) {
*f = static_cast<String*>(o->get())->toDouble();
if (*f >= -1 && *f <= 1)
f++;
else
f = 0;
}
TelEngine::destruct(l);
if (!f)
buf.clear();
return f != 0;
}
static int16_t s_sampleEnergize = 2047;
// Energize a number. Refer the input value to the requested energy
static inline int16_t sampleScale(float value, float scale)
{
return (int16_t)::round(value * scale);
}
static inline int16_t energize(float value, float scale, int16_t refVal, unsigned int& clamp)
{
int16_t v = sampleScale(value,scale);
if (v > refVal) {
clamp++;
return refVal;
}
if (v < -refVal) {
clamp++;
return -refVal;
}
return v;
}
static inline void brfCopyTxData(int16_t* dest, float* src, unsigned int samples,
float scaleI, int16_t maxI, float scaleQ, int16_t maxQ, unsigned int& clamped)
{
for (; samples; samples--) {
*dest++ = htole16(energize(*src++,scaleI,maxI,clamped));
*dest++ = htole16(energize(*src++,scaleQ,maxQ,clamped));
}
}
// Device calibration data
class BrfCalData
{
public:
inline BrfCalData(int mod) {
::memset(this,0,sizeof(BrfCalData));
module = mod;
desc = &s_calModuleDesc[module];
}
inline const char* modName() const
{ return calModName(module); }
int module;
const BrfCalDesc* desc;
uint8_t clkEn;
uint8_t inputMixer;
uint8_t loOpt;
uint8_t lnaGain;
int rxVga1;
int rxVga2;
};
// libusb transfer
class LusbTransfer : public Mutex
{
public:
inline LusbTransfer(BrfLibUsbDevice* dev = 0)
: Mutex(false,"LusbTransfer"),
device(dev), ep(255), transfer(0), status(0), m_running(false)
{}
inline ~LusbTransfer()
{ reset(); }
inline bool running() const
{ return m_running; }
inline void running(bool start) {
m_running = start;
if (m_running) {
status = 0;
error.clear();
}
}
inline bool alloc() {
if (transfer)
return true;
transfer = ::libusb_alloc_transfer(0);
if (transfer)
return true;
error = "Failed to allocate libusb transfer";
status = RadioInterface::Failure;
return false;
}
inline void reset() {
cancel();
if (transfer)
::libusb_free_transfer(transfer);
transfer = 0;
m_running = false;
}
// Fill bulk transfer. Allocate if not already done
// Return true on success (change state to Init)
bool fillBulk(uint8_t* data, unsigned int len, unsigned int tout);
bool submit();
unsigned int cancel(String* error = 0);
BrfLibUsbDevice* device;
uint8_t ep;
libusb_transfer* transfer;
unsigned int status;
String error;
protected:
bool m_running;
};
// Holds RX/TX related data
// Hold samples read/write related data
class BrfDevStatus
{
public:
inline BrfDevStatus(bool tx)
: rfEnabled(false), frequency(0), vga1(0), vga2(0), lpf(0),
dcOffsetI(0), dcOffsetQ(0), fpgaCorrPhase(0), fpgaCorrGain(0),
lpfBw(0), sampleRate(0),
m_tx(tx)
{}
inline BrfDevStatus(const BrfDevStatus& src)
{ *this = src; }
inline BrfDevStatus& operator=(const BrfDevStatus& src) {
::memcpy(this,&src,sizeof(src));
return *this;
}
inline bool tx() const
{ return m_tx; }
bool rfEnabled; // RF enabled flag
unsigned int frequency; // Used frequency
unsigned int freqOffset; // Used frequency offset
int vga1; // VGA1 gain
int vga2; // VGA2 gain
int lpf; // LPF status
int dcOffsetI; // Current I (in-phase) DC offset
int dcOffsetQ; // Current Q (quadrature) DC offset
int fpgaCorrPhase; // Current FPGA phase correction
int fpgaCorrGain; // Current FPGA gain correction
unsigned int lpfBw; // LPF bandwidth
unsigned int sampleRate; // Sampling rate
protected:
bool m_tx; // Direction
};
// Holds RX/TX related data
// Hold samples read/write related data
class BrfDevIO : public BrfDevStatus
{
public:
inline BrfDevIO(bool tx)
: BrfDevStatus(tx),
busy(false), showBuf(0), showBufData(true), checkTs(0),
mutex(false,tx ? "BrfDevIoTx" : "BrfDevIoRx"),
showDcOffsChange(0), startTime(0), transferred(0),
timestamp(0), lastTs(0), buffers(0), hdrLen(0), bufSamples(0),
bufSamplesLen(0), crtBuf(0), crtBufSampOffs(0),
syncTsRequested(false), syncTs(0),
#ifdef LITTLE_ENDIAN
m_bufEndianOk(true)
#else
m_bufEndianOk(false)
#endif
{}
void resetSamplesBuffer(unsigned int nSamples, unsigned int hLen,
unsigned int nBuffers = 1) {
bufSamples = nSamples;
bufSamplesLen = samplesi2bytes(bufSamples);
hdrLen = hLen;
bufLen = hdrLen + bufSamplesLen;
buffers = nBuffers ? nBuffers : 1;
buffer.assign(0,buffers * bufLen);
resetPosTime();
}
inline void resetPosTime() {
resetBufPos(m_tx);
timestamp = 0;
lastTs = 0;
startTime = 0;
transferred = 0;
}
inline void setRf(bool on) {
rfEnabled = on;
resetPosTime();
}
inline bool advanceBuffer() {
if (crtBuf < buffers)
setCrtBuf(crtBuf + 1);
return crtBuf < buffers;
}
inline uint8_t* bufStart(unsigned int index)
{ return buffer.data(index * bufLen); }
inline int16_t* samples(unsigned int index)
{ return (int16_t*)(bufStart(index) + hdrLen); }
inline int16_t* samplesEOF(unsigned int index)
{ return (int16_t*)(bufStart(index) + bufLen); }
// Retrieve a pointer to current buffer samples start (including offset)
// and available samples number
inline int16_t* crtBufSamples(unsigned int& avail) {
avail = bufSamples - crtBufSampOffs;
return samples(crtBuf) + crtBufSampOffs * 2;
}
inline uint64_t bufTs(unsigned int index) {
// Skip reserved
uint32_t* u = (uint32_t*)(bufStart(index) + 4);
// Get timestamp (LOW32 + HI32, little endian)
uint64_t ts = le32toh((uint32_t)(*u++ >> 1));
return ts | ((uint64_t)le32toh(*u) << 31);
}
inline void setBufTs(unsigned int index, uint64_t ts) {
uint32_t* u = (uint32_t*)(bufStart(index));
*u++ = htole32(0xdeadbeef);
*u++ = htole32((uint32_t)(ts << 1));
*u++ = htole32((uint32_t)(ts >> 31));
*u = htole32((uint32_t)-1);
}
inline void resetBufPos(bool start = true) {
#ifndef LITTLE_ENDIAN
m_bufEndianOk = false;
#endif
setCrtBuf(start ? 0 : buffers);
}
#ifdef LITTLE_ENDIAN
inline void fixEndian()
{}
#else
inline void fixEndian() {
if (m_bufEndianOk)
return;
m_bufEndianOk = true;
uint8_t* d = buffer.data(0);
for (unsigned int i = 0; i < buffers; i++) {
d += hdrLen;
for (uint8_t* last = d + bufSamplesLen; d != last; d += 2) {
uint8_t tmp = *d;
*d = d[1];
d[1] = tmp;
}
}
}
#endif
inline void dumpInt16Samples(String& s, unsigned int index, unsigned int sampOffs = 0,
int nSamples = -1) {
int16_t* p = samples(index) + sampOffs * 2;
unsigned int n = bufSamples - sampOffs;
if (nSamples > 0 && nSamples < (int)n)
n = nSamples;
for (int16_t* last = (p + n * 2); p != last; p += 2) {
if (s)
s << " ";
s << p[0] << "," << p[1];
}
}
bool busy; // Busy flag
int showBuf; // Show buffers
bool showBufData; // Display buffer data
int checkTs; // Check IO buffers timestamp
Mutex mutex; // Protect data changes when needed
unsigned int showDcOffsChange; // Show DC offset changed debug message
uint64_t startTime; // Absolute time for start (first TX/RX)
uint64_t transferred; // The number of samples transferred
// TX/RX data
uint64_t timestamp; // Last timestamp to/from device
uint64_t lastTs; // Last buffer timestamp (used when checking)
unsigned int buffers; // The number of buffers
unsigned int hdrLen; // Header length in bytes
unsigned int bufSamples; // Length of a single buffer in samples (without header)
unsigned int bufSamplesLen; // Length of a single buffer in bytes (without header)
unsigned int bufLen; // Length of a single buffer (in bytes)
unsigned int crtBuf; // Current buffer
unsigned int crtBufSampOffs; // Current buffer samples offset
DataBlock buffer; // I/O buffer
// Sync timestamp
bool syncTsRequested;
uint64_t syncTs;
protected:
// Reset current buffer to start
inline void setCrtBuf(unsigned int index = 0) {
crtBuf = index;
crtBufSampOffs = 0;
}
bool m_bufEndianOk;
};
// Temporary change alt setting. Restore on destruction
class BrfDevTmpAltSet
{
public:
inline BrfDevTmpAltSet(BrfLibUsbDevice* dev)
: m_device(dev), m_oper(0), m_tmpAltSet(BRF_ALTSET_INVALID)
{}
inline BrfDevTmpAltSet(BrfLibUsbDevice* dev, int altSet,
unsigned int& status, String* error, const char* oper)
: m_device(dev), m_oper(0), m_tmpAltSet(BRF_ALTSET_INVALID)
{ status = set(altSet,error,oper); }
// Temporary change to RF_LINK
inline BrfDevTmpAltSet(BrfLibUsbDevice* dev, unsigned int& status, String* error,
const char* oper)
: m_device(dev), m_oper(0), m_tmpAltSet(BRF_ALTSET_INVALID)
{ status = set(BRF_ALTSET_RF_LINK,error,oper); }
inline ~BrfDevTmpAltSet()
{ restore(); }
unsigned int set(int altSet, String* error, const char* oper);
inline unsigned int set(String* error, const char* oper)
{ return set(BRF_ALTSET_RF_LINK,error,oper); }
unsigned int restore();
private:
BrfLibUsbDevice* m_device;
const char* m_oper;
int m_tmpAltSet;
};
class BrfThread;
class BrfSerialize;
class BrfLibUsbDevice : public GenObject
{
friend class BrfDevTmpAltSet;
friend class BrfThread;
friend class BrfModule;
friend class BrfSerialize;
public:
enum UartDev {
UartDevGPIO = 0,
UartDevLMS,
UartDevVCTCXO,
UartDevSI5338,
UartDevCount
};
enum Endpoint {
EpSendSamples = 0,
EpSendCtrl,
EpReadSamples,
EpReadCtrl,
EpCount
};
// LNA selection
enum LmsLna {
LmsLnaNone = 0, // Disable all LNAs
LmsLna1, // Enable LNA1 (300MHz - 2.8GHz)
LmsLna2, // Enable LNA2 (1.5GHz - 3.8GHz)
LmsLna3 // Enable LNA3 (Unused on the bladeRF)
};
// PA Selection
enum LmsPa {
LmsPaNone = 0, // All PAs disabled
LmsPaAux, // AUX PA Enable (for RF Loopback)
LmsPa1, // PA1 Enable (300MHz - 2.8GHz)
LmsPa2, // PA2 Enable (1.5GHz - 3.8GHz)
};
// LNA gain values
enum LnaGain {
LnaGainUnhandled = 0,
LnaGainBypass = 1,
LnaGainMid = 2,
LnaGainMax = 3,
};
enum CorrectionType {
CorrLmsI,
CorrLmsQ,
CorrFpgaPHASE,
CorrFpgaGAIN,
};
// Loopback mode
enum Loopback {
LoopNone = 0, // Disabled
LoopFirmware, // Firmware loopback
LoopLpfToRxOut, // Baseband: TX LPF -> RX out
LoopLpfToVga2, // Baseband: TX LPF -> RX VGA2
LoopVga1ToVga2, // Baseband: TX VGA1 -> RX VGA2
LoopLpfToLpf, // Baseband: TX LPF -> RX LPF
LoopVga1ToLpf, // Baseband: TX VGA1 -> RX LPF
LoopRfLna1, // RF: mixer after PA -> LNA1
LoopRfLna2, // RF: mixer after PA -> LNA2
LoopRfLna3, // RF: mixer after PA -> LNA3
LoopUnknown
};
enum Lpf {
LpfInvalid = 0,
LpfDisabled = 1,
LpfBypass,
LpfNormal,
};
// Flags used to restore dev status
enum StatusFlags {
DevStatFreq = 0x0001,
DevStatVga1 = 0x0002,
DevStatVga2 = 0x0004,
DevStatLpf = 0x0008,
DevStatDcI = 0x0010,
DevStatDcQ = 0x0020,
DevStatLpfBw = 0x0040,
DevStatSampleRate = 0x0080,
DevStatVga = DevStatVga1 | DevStatVga2,
DevStatDc = DevStatDcI | DevStatDcQ,
};
~BrfLibUsbDevice();
inline BrfInterface* owner() const
{ return m_owner; }
inline libusb_device_handle* handle() const
{ return m_devHandle; }
inline int speed() const
{ return m_devSpeed; }
inline int bus() const
{ return m_devBus; }
inline int addr() const
{ return m_devAddr; }
inline const String& serial() const
{ return m_devSerial; }
inline const String& fwVerStr() const
{ return m_devFwVerStr; }
inline const String& fpgaFile() const
{ return m_devFpgaFile; }
inline const String& fpgaVerStr() const
{ return m_devFpgaVerStr; }
inline bool exiting() const
{ return m_exiting; }
inline void exiting(bool on)
{ m_exiting = on; }
inline bool closing() const
{ return m_closing; }
inline unsigned int cancelled(String* error = 0) {
if (exiting() || closing()) {
if (error)
*error = "Exiting";
return RadioInterface::Cancelled;
}
return checkCancelled(error);
}
inline int showBuf(bool tx, int val, bool tsOnly) {
Lock lck(m_dbgMutex);
getIO(tx).showBufData = !tsOnly;
return (getIO(tx).showBuf = val);
}
inline int checkTs(bool tx, int val) {
Lock lck(m_dbgMutex);
return (getIO(tx).checkTs = val);
}
inline int showRxDCInfo(int val) {
Lock lck(m_dbgMutex);
return (m_rxShowDcInfo = val);
}
inline unsigned int bufSamples(bool tx)
{ return getIO(tx).bufSamples; }
inline unsigned int bufCount(bool tx)
{ return getIO(tx).buffers; }
inline unsigned int totalSamples(bool tx) {
BrfDevIO& io = getIO(tx);
return io.buffers * io.bufSamples;
}
unsigned int setTxPattern(const String& pattern);
void dumpStats(String& buf, const char* sep);
void dumpDev(String& buf, bool info, bool state, const char* sep,
bool fromStatus = false, bool withHdr = true);
void dumpBoardStatus(String& buf, const char* sep);
unsigned int dumpPeripheral(uint8_t dev, uint8_t addr, uint8_t len, String* buf = 0);
// Initialize the device.
// Call the reset method in order to set the device to a known state
bool open(const NamedList& params);
// Close the device.
void close();
// Power on the radio
// Enable timestamps, enable RF TX/RX
unsigned int powerOn();
// Send an array of samples waiting to be transmitted
// samples: The number of I/Q samples (i.e. half buffer lengh)
unsigned int syncTx(uint64_t ts, float* data, unsigned int samples,
float* powerScale = 0);
// Receive data from the Rx interface of the bladeRF device
// samples: The number of I/Q samples (i.e. half buffer lengh)
unsigned int syncRx(uint64_t& ts, float* data, unsigned int& samples);
// Set the frequency on the Tx or Rx side
unsigned int setFrequency(uint32_t hz, bool tx);
// Retrieve frequency
unsigned int getFrequency(uint32_t& hz, bool tx);
// Set frequency offset
unsigned int setFreqOffset(int offs, int* newVal = 0);
// Get frequency offset
unsigned int getFreqOffset(int& offs);
// Set the LPF bandwidth for a specific module
unsigned int setLpfBandwidth(uint32_t band, bool tx);
// Get the LPF bandwidth for a specific module
unsigned int getLpfBandwidth(uint32_t& band, bool tx);
// LPF set/get
unsigned int setLpf(int lpf, bool tx);
unsigned int getLpf(int& lpf, bool tx);
// Set the sample rate on a specific module
unsigned int setSamplerate(uint32_t value, bool tx);
// Get the sample rate on a specific module
unsigned int getSamplerate(uint32_t& value, bool tx);
// Set the pre-mixer gain on transmission (interval [-35..-4])
// Set the post-mixer gain setting on transmission (interval: [0..25])
unsigned int setTxVga(int vga, bool preMixer);
// Set the post-mixer gain setting on transmission (interval: [0..25])
inline unsigned int setTxVga1(int vga)
{ return setTxVga(vga,true); }
inline unsigned int setTxVga2(int vga)
{ return setTxVga(vga,false); }
// Retrieve the pre/post mixer gain setting on transmission
unsigned int getTxVga(int& vga, bool preMixer);
inline unsigned int getTxVga1(int& vga)
{ return getTxVga(vga,true); }
inline unsigned int getTxVga2(int& vga)
{ return getTxVga(vga,false); }
// Set TX power balance
unsigned int setTxIQBalance(float value);
// Enable or disable the pre/post mixer gain on the receive side
unsigned int enableRxVga(bool on, bool preMixer);
inline unsigned int enableRxVga1(bool on)
{ return enableRxVga(on,true); }
inline unsigned int enableRxVga2(bool on)
{ return enableRxVga(on,false); }
// Set the pre-mixer RX gain setting on the receive side (interval [5..30])
// Set the post-mixer RX gain setting (interval [0..30])
unsigned int setRxVga(int vga, bool preMixer);
inline unsigned int setRxVga1(int vga)
{ return setRxVga(vga,true); }
inline unsigned int setRxVga2(int vga)
{ return setRxVga(vga,false); }
// Retrieve the pre/post mixer rx gain setting
unsigned int getRxVga(int& vga, bool preMixer);
inline unsigned int getRxVga1(int& vga)
{ return getRxVga(vga,true); }
inline unsigned int getRxVga2(int& vga)
{ return getRxVga(vga,false); }
// Set pre and post mixer value
unsigned int setGain(bool tx, int val, int* newVal = 0);
// Auto calibrate
unsigned int calibrate();
// Set Tx/Rx DC I/Q offset correction
unsigned int setDcOffset(bool tx, bool i, int16_t value);
// Retrieve Tx/Rx DC I/Q offset correction
unsigned int getDcOffset(bool tx, bool i, int16_t& value);
// Set/Get FPGA correction
unsigned int setFpgaCorr(bool tx, int corr, int16_t value);
unsigned int getFpgaCorr(bool tx, int corr, int16_t& value);
// Retrieve TX/RX timestamp
unsigned int getTimestamp(bool tx, uint64_t& ts);
// Write LMS register
unsigned int writeLMS(uint8_t addr, uint8_t value, uint8_t* rst = 0);
// Enable or disable loopback
unsigned int setLoopback(const char* name = 0);
unsigned int setLoopback(int mode = 0);
// Release data
virtual void destruct();
// Create an interface
static BrfLibUsbDevice* create(BrfInterface* owner);
// Utilities
static const char* speedStr(int speed) {
switch (speed) {
case LIBUSB_SPEED_SUPER:
return "SUPER";
case LIBUSB_SPEED_HIGH:
return "HIGH";
}
return "Unknown";
}
static uint64_t reduceFurther(uint64_t v1, uint64_t v2);
static void reduceRational(BrfRationalRate& rate);
static inline void rationalDouble(BrfRationalRate& rate) {
rate.integer *= 2;
rate.numerator *= 2;
reduceRational(rate);
}
static void calcSrate(Si5338MultiSynth& synth, BrfRationalRate& rate);
static unsigned int calcMultiSynth(Si5338MultiSynth& synth,
BrfRationalRate& rate, String* error = 0);
static void packRegs(Si5338MultiSynth& synth);
static void unpackRegs(Si5338MultiSynth& synth);
// Set error string
static inline unsigned int setError(unsigned int code, String* buf,
const char* error, const char* prefix = 0) {
if (!(buf && code))
return code;
String tmp = prefix;
tmp.append((error && *error) ? error : RadioInterface::errorName(code)," - ");
buf->append(tmp," - ");
return code;
}
static inline unsigned int setUnkValue(String& buf, const char* unsupp = 0,
const char* invalid = 0) {
if (unsupp)
buf << "Unsupported " << unsupp;
else if (invalid)
buf << "Invalid " << invalid;
else
buf << "Unknown value";
return RadioInterface::OutOfRange;
}
static inline unsigned int setUnhandled(String& buf, int val, const char* what = 0) {
buf << "Unhandled";
buf.append(what," ");
buf << val;
return RadioInterface::OutOfRange;
}
// Print libusb error
static inline String& appendLusbError(String& buf, int code, const char* prefix = 0) {
buf << prefix << "(" << code << " '" << ::libusb_error_name(code) << "')";
return buf;
}
static inline void appendLusbError(String* buf, int code, const char* prefix = 0) {
if (buf)
appendLusbError(*buf,code,prefix);
}
// Utility: check libusb result against LIBUSB_SUCCESS
// Print libusb result to string on failure and return indicated code
static inline unsigned int lusbCheckSuccess(int code, String* error,
const char* prefix = 0) {
if (code == LIBUSB_SUCCESS)
return 0;
if (code != LIBUSB_TRANSFER_CANCELLED) {
appendLusbError(error,code,prefix);
return RadioInterface::HardwareIOError;
}
if (error)
*error = RadioInterface::errorName(RadioInterface::Cancelled);
return RadioInterface::Cancelled;
}
// Retrieve UART addr for FPGA correction
static inline int fpgaCorrAddr(bool tx, int corr, String& what) {
if (corr == CorrFpgaPHASE) {
what = "PHASE";
return tx ? 10 : 6;
}
if (corr == CorrFpgaGAIN) {
what = "GAIN";
return tx ? 8 : 4;
}
what = corr;
return -1;
}
// Retrieve LMS addr for I/Q correction
static inline uint8_t lmsCorrIQAddr(bool tx, bool i) {
if (tx)
return i ? 0x42 : 0x43;
return i ? 0x71 : 0x72;
}
// Retrieve LMS addr for TX/RX VGA 1/2
static inline uint8_t lmsVgaAddr(bool tx, bool preMixer) {
if (tx)
return preMixer ? 0x41 : 0x45;
return preMixer ? 0x76 : 0x65;
}
// Retrieve LMS LPF base address
static inline uint8_t lmsLpfAddr(bool tx)
{ return tx ? 0x34 : 0x54; }
// Retrieve LMS PLL freq config addr
static inline uint8_t lmsFreqAddr(bool tx)
{ return tx ? 0x10 : 0x20; }
private:
BrfLibUsbDevice(BrfInterface* owner);
inline void doClose() {
m_closing = true;
closeDevice();
clearDeviceList();
m_closing = false;
}
unsigned int setStatus(const BrfDevStatus& stat, unsigned int flags,
String* error = 0);
unsigned int setStatus(const BrfDevStatus& statTx, unsigned int flagsTx,
const BrfDevStatus& statRx, unsigned int flagsRx, String* error = 0) {
unsigned int status = 0;
BRF_FUNC_CALL_RET(setStatus(statRx,flagsRx,error));
BRF_FUNC_CALL_RET(setStatus(statTx,flagsRx,error));
return internalPowerOn(false,statTx.rfEnabled,statRx.rfEnabled,error);
}
void internalDumpDev(String& buf, bool info, bool state, const char* sep,
bool internal, bool fromStatus = false, bool withHdr = true);
unsigned int internalPowerOn(bool rfLink, bool tx = true, bool rx = true,
String* error = 0);
// Send an array of samples waiting to be transmitted
// samples: The number of I/Q samples (i.e. half buffer lengh)
unsigned int send(uint64_t ts, float* data, unsigned int samples,
float* powerScale = 0);
void sendTxPatternChanged();
void sendCopyTxPattern(int16_t* buf, unsigned int avail,
float scaleI, int16_t maxI, float scaleQ, int16_t maxQ, unsigned int& clamped);
unsigned int recv(uint64_t& ts, float* data, unsigned int& samples);
unsigned int internalSetSampleRate(bool tx, uint32_t value, String* error = 0);
// Update FPGA (load, get version)
unsigned int updateFpga(const NamedList& params);
unsigned int internalSetFpgaCorr(bool tx, int corr, int16_t value,
String* error = 0, int clampLevel = DebugConf);
unsigned int internalGetFpgaCorr(bool tx, int corr, int16_t* value = 0,
String* error = 0);
unsigned int internalSetTxVga(int vga, bool preMixer, String* error = 0);
unsigned int internalGetTxVga(int* vga, bool preMixer, String* error = 0);
// Enable or disable the pre/post mixer gain on the receive side
unsigned int internalEnableRxVga(bool on, bool preMixer, String* error = 0);
unsigned int internalSetRxVga(int vga, bool preMixer, String* error = 0);
unsigned int internalGetRxVga(int* vga, bool preMixer, String* error = 0);
inline unsigned int internalRxVga(bool read, int& vga, bool preMixer,
String* error = 0) {
if (read)
return internalGetRxVga(&vga,preMixer,error);
return internalSetRxVga(vga,preMixer,error);
}
inline unsigned int internalSetVga(bool tx, int vga, bool preMixer,
String* error = 0) {
if (tx)
return internalSetTxVga(vga,preMixer,error);
return internalSetRxVga(vga,preMixer,error);
}
unsigned int internalSetGain(bool tx, int val, int* newVal = 0, String* error = 0);
unsigned int internalSetTxIQBalance(bool newGain, float newBalance = 0,
const char* param = 0);
inline unsigned int internalSetCorrectionIQ(bool tx, int I, int Q,
String* error = 0) {
unsigned int status = internalSetDcOffset(tx,true,I,error);
return !status ? internalSetDcOffset(tx,false,Q,error) : status;
}
inline unsigned int internalSetDcCorr(int txI, int txQ, int rxI, int rxQ,
String* error = 0) {
unsigned int status = 0;
BRF_FUNC_CALL(internalSetCorrectionIQ(true,txI,txQ,error));
BRF_FUNC_CALL(internalSetCorrectionIQ(false,rxI,rxQ,error));
return status;
}
unsigned int internalSetFreqOffs(int val, int* newVal, String* error = 0);
unsigned int internalSetFrequency(bool tx, uint32_t hz, String* error = 0);
unsigned int internalGetFrequency(bool tx, uint32_t* hz = 0, String* error = 0);
// Retrieve TX/RX timestamp
unsigned int internalGetTimestamp(bool tx, uint64_t& ts, String* error = 0);
// Retrieve and set frequency
inline unsigned int restoreFreq(bool tx, String* error = 0) {
uint32_t hz = 0;
unsigned int status = internalGetFrequency(tx,&hz,error);
return status == 0 ? internalSetFrequency(tx,hz,error) : status;
}
// Alt interface setting change
unsigned int lusbSetAltInterface(int altSetting, String* error = 0);
// Wrapper for libusb_control_transfer
unsigned int lusbCtrlTransfer(uint8_t reqType, int8_t request, uint16_t value,
uint16_t index, uint8_t* data, uint16_t len, String* error = 0,
unsigned int tout = 0);
// Wrapper for libusb synchronous bulk transfer
unsigned int lusbBulkTransfer(uint8_t endpoint, uint8_t* data, unsigned int len,
unsigned int* transferred = 0, String* error = 0, unsigned int tout = 0);
// Make an async usb transfer
unsigned int syncTransfer(int ep, uint8_t* data, unsigned int len, String* error = 0);
// Select amplifier (PA/LNA) from low/high frequency
inline unsigned int selectPaLna(bool tx, bool lowBand, String* error) {
if (tx)
return paSelect(lowBand ? LmsPa1 : LmsPa2,error);
return lnaSelect(lowBand ? LmsLna1 : LmsLna2,error);
}
// Read the value of a specific GPIO register
unsigned int gpioRead(uint8_t addr, uint32_t& value, uint8_t len, String* error = 0,
const char* loc = 0);
// Write a value to a specific GPIO register
unsigned int gpioWrite(uint8_t addr, uint32_t value, uint8_t len, String* error = 0,
const char* loc = 0);
// Read the lms configuration
inline unsigned int lmsRead(uint8_t addr, uint8_t& data, String* error = 0,
const char* loc = 0)
{ return accessPeripheralRead(UartDevLMS,addr,data,error,loc); }
inline unsigned int lmsRead2(uint8_t addr1, uint8_t& data1,
uint8_t addr2, uint8_t& data2, String* error = 0, const char* loc = 0) {
unsigned int status = lmsRead(addr1,data1,error,loc);
return status == 0 ? lmsRead(addr2,data2,error,loc) : status;
}
// Write the lms configuration
inline unsigned int lmsWrite(uint8_t addr, uint8_t data, String* error = 0,
const char* loc = 0)
{ return accessPeripheralWrite(UartDevLMS,addr,data,error,loc); }
inline unsigned int lmsWrite2(uint8_t addr1, uint8_t data1,
uint8_t addr2, uint8_t data2, String* error = 0, const char* loc = 0) {
unsigned int status = lmsWrite(addr1,data1,error,loc);
return status == 0 ? lmsWrite(addr2,data2,error,loc) : status;
}
inline unsigned int lms(bool read, uint8_t addr, uint8_t& data,
String* error = 0, const char* loc = 0) {
if (read)
return lmsRead(addr,data,error,loc);
return lmsWrite(addr,data,error,loc);
}
// Read address from LMS, clear mask, set val (using OR) and write it back
inline unsigned int lmsSet(uint8_t addr, uint8_t val, uint8_t clearMask,
String* error = 0) {
uint8_t data = 0;
unsigned int status = lmsRead(addr,data,error);
return status ? status : lmsWrite(addr,(data & ~clearMask) | val,error);
}
// Read address from LMS, set val (using OR) and write it back
inline unsigned int lmsSet(uint8_t addr, uint8_t val, String* error = 0) {
uint8_t data = 0;
unsigned int status = lmsRead(addr,data,error);
return status ? status : lmsWrite(addr,data | val,error);
}
// Read address from LMS, clear mask and write it back
inline unsigned int lmsReset(uint8_t addr, uint8_t clearMask, String* error) {
uint8_t data = 0;
unsigned int status = lmsRead(addr,data,error);
return status ? status : lmsWrite(addr,(data & ~clearMask),error);
}
// Reset LMS addr using mask. Optionally set a value
inline unsigned int lmsChange(uint8_t addr, uint8_t* maskReset, uint8_t* maskSet,
String* error) {
if (maskReset && maskSet)
return lmsSet(addr,*maskSet,*maskReset,error);
if (maskReset)
return lmsReset(addr,*maskReset,error);
return maskSet ? lmsSet(addr,*maskSet,error) : 0;
}
inline unsigned int lmsChangeMask(uint8_t addr, uint8_t mask, bool set,
String* error)
{ return lmsChange(addr,set ? 0 : &mask,set ? &mask : 0,error); }
// LNA
unsigned int lnaSelect(int lna, String* error = 0);
unsigned int lnaEnable(bool on, String* error = 0);
unsigned int lnaGainSet(uint8_t value, String* error = 0);
unsigned int lnaGainGet(uint8_t& value, String* error = 0);
inline unsigned int lnaGain(bool read, uint8_t& value, String* error = 0) {
if (read)
return lnaGainGet(value,error);
return lnaGainSet(value,error);
}
// LPF set/get
unsigned int internalSetLpfBandwidth(bool tx, uint32_t band, String* error = 0);
unsigned int internalSetLpf(bool tx, int lpf, String* error = 0);
unsigned int internalGetLpf(bool tx, int* lpf, String* error = 0);
// Fill the m_list member of the class
unsigned int updateDeviceList(String* error = 0);
inline void clearDeviceList() {
if (!m_list)
return;
::libusb_free_device_list(m_list,1);
m_dev = 0;
m_list = 0;
m_listCount = 0;
}
// Enable/disable RF and sample circulation on both RX and TX sides
inline unsigned int enableRfBoth(bool on, bool frontEndOnly, String* error = 0) {
unsigned int status = enableRf(true,on,frontEndOnly,error);
if (status == 0)
return enableRf(false,on,frontEndOnly,error);
return status;
}
unsigned int enableRf(bool tx, bool on, bool frontEndOnly = false, String* error = 0);
// Read the FPGA version
unsigned int getFpgaVersion(uint32_t& version);
// Check if fpga is loaded
// Return NoError/NotInitialized (result OK) or other error on failure
unsigned int checkFpga();
// Restore device after loading the FPGA
unsigned int restoreAfterFpgaLoad(String* error = 0);
// Reset the Usb interface using an ioctl call
unsigned int resetUsb(String* error = 0);
// Set the VCTCXO configuration to the correct value
unsigned int tuneVcocap(uint8_t addr, String* error = 0);
// Send requests to the bladeRF device regarding the FPGA image configuration.
unsigned int vendorCommand(uint8_t cmd, uint8_t endpoint, uint8_t* data, uint16_t len,
String* error = 0);
inline unsigned int vendorCommand(uint8_t cmd, uint8_t endpoint, int32_t& data,
String* error = 0)
{ return vendorCommand(cmd,endpoint,(uint8_t*)&data,sizeof(data),error); }
inline unsigned int vendorCommand0_4(uint8_t cmd, uint8_t endpoint, String* error = 0) {
uint32_t dummy = 0;
return vendorCommand(cmd,endpoint,(uint8_t*)&dummy,4,error);
}
// Access the bladeRF board in order to transmit data
unsigned int accessPeripheral(uint8_t dev, bool tx, uint8_t addr,
uint8_t* data, String* error = 0, uint8_t len = 1, const char* loc = 0);
inline unsigned int accessPeripheralWrite(uint8_t dev, uint8_t addr, uint8_t data,
String* error = 0, const char* loc = 0)
{ return accessPeripheral(dev,true,addr,&data,error,1,loc); }
inline unsigned int accessPeripheralRead(uint8_t dev, uint8_t addr, uint8_t& data,
String* error = 0, const char* loc = 0)
{ return accessPeripheral(dev,false,addr,&data,error,1,loc); }
inline unsigned int setSi5338(uint8_t addr, uint8_t data, String* error = 0)
{ return accessPeripheralWrite(UartDevSI5338,addr,data,error); }
inline unsigned int getSi5338(uint8_t addr, uint8_t& data, String* error = 0)
{ return accessPeripheralRead(UartDevSI5338,addr,data,error); }
unsigned int internalSetDcOffset(bool tx, bool i, int16_t value, String* error = 0);
unsigned int internalGetDcOffset(bool tx, bool i, int16_t* value, String* error = 0);
unsigned int enableTimestamps(bool on = true, String* error = 0);
unsigned int updateStatus(String* error = 0);
unsigned int paSelect(int pa, String* error = 0);
void clampFrequency(uint32_t& val, bool tx, const char* loc);
int clampInt(int val, int minVal, int maxVal, const char* what = 0,
int level = DebugNote);
inline int clampIntParam(const NamedList& params, const String& param,
int defVal, int minVal, int maxVal, int level = DebugConf)
{ return clampInt(params.getIntValue(param,defVal),minVal,maxVal,param,level); }
unsigned int openDevice(bool claim, String* error = 0);
void closeDevice();
void getDevStrDesc(String& data, uint8_t index, const char* what);
// Read pages from device using contrt to 0 (from -35)ol transfer
unsigned int ctrlTransferReadPage(uint8_t request, DataBlock& buf, String* error = 0);
// Read calibration cache page from device
unsigned int readCalCache(String* error = 0);
// Retrieve a filed from a buffer of elements
// Buffer format: 1 byte length + data + 2 bytes CRC16
// Return error string on failure
const char* getBufField(String& value, const char* field);
// Read calibration cache field
unsigned int getCalField(String& value, const String& name,
const char* desc = 0, String* error = 0);
String& dumpCalCache(String& dest);
// Update speed related data
unsigned int updateSpeed(String* error = 0);
// Compute Rx avg values, autocorrect offsets if configured
void computeRx(uint64_t ts);
// Check io timestamps
void ioBufCheckTs(bool tx, unsigned int nBufs = 0);
// Alter data
void updateAlterData(const NamedList& params);
void rxAlterData();
// Calibration utilities
unsigned int calBackupRestore(BrfCalData& bak, bool backup, String* error);
unsigned int calInitFinal(BrfCalData& bak, bool init, String* error);
unsigned int dcCalProcPrepare(const BrfCalData& bak, uint8_t subMod, String& error);
unsigned int dcCalProc(const BrfCalData& bak, uint8_t subMod, uint8_t dcCnt,
uint8_t& dcReg, String& error);
unsigned int dcCalProcPost(const BrfCalData& bak, uint8_t subMod, uint8_t dcReg,
String& error);
unsigned int readBuffer(uint64_t ts, float* buf, unsigned int samples,
String* error);
unsigned int readComputeX_LMSIQ(uint8_t dcI, uint8_t dcQ, double& erf,
float* buf, unsigned int samples, String* error, double& energy,
int* dumpRx = 0);
unsigned int calibrateRx(String* error);
unsigned int calibrateTxLOLeakage(String* error);
// Set error string or put a debug message
unsigned int showError(unsigned int code, const char* error, const char* prefix,
String* buf, int level = DebugNote);
void printIOBuffer(bool tx, const char* loc, int index = -1, unsigned int nBufs = 0);
inline BrfDevIO& getIO(bool tx)
{ return tx ? m_txIO : m_rxIO; }
inline unsigned int checkDbgInt(int& val, unsigned int step = 1) {
if (!(val && step))
return 0;
Lock lck(m_dbgMutex);
if (val < 0)
return step;
if (val >= (int)step)
val -= step;
else {
step = val;
val = 0;
}
return step;
}
// Enable or disable loopback
unsigned int internalSetLoopback(int mode = 0, String* error = 0);
unsigned int setLoopbackPath(int mode, String& error);
unsigned int enableRfLoopback(bool on, String& error);
void dumpLoopbackStatus();
unsigned int internalDumpPeripheral(uint8_t dev, uint8_t addr, uint8_t len,
String* buf, uint8_t lineLen);
inline int decodePa(uint8_t data) const {
if ((data & 0x02) == 0)
return LmsPaAux;
data &= 0x18;
if (data == 0x08)
return LmsPa1;
return (data == 0x10) ? LmsPa2 : LmsPaNone;
}
inline int decodeLpf(uint8_t reg1, uint8_t reg2) const {
int on = reg1 & (1 << 1);
int bypass = reg2 & (1 << 6);
if (on)
return bypass ? LpfInvalid : LpfNormal;
return bypass ? LpfBypass : LpfDisabled;
}
// Set RXVGA2 DECODE bit (LMS addr 0x64 bit 0)
// 0(true): Decode control signals
// 1(false): Use control signal from test mode registers
inline unsigned int setRxVga2Decode(bool on, String* error)
{ return on ? lmsReset(0x64,0x01,error) : lmsSet(0x64,0x01,error); }
// Request TX ON/OFF. Wait for change
inline unsigned int syncTs(bool tx, String* error = 0) {
BrfDevIO& io = getIO(tx);
io.syncTsRequested = true;
unsigned int intervals = (m_bulkTout / Thread::idleMsec()) + 1;
unsigned int status = 0;
while (io.syncTsRequested) {
Thread::idle();
BRF_FUNC_CALL_RET(cancelled(error));
if ((intervals--) == 0)
return setError(RadioInterface::Failure,error,"Sync TS timeout");
}
return 0;
}
inline bool setRxDcAuto(bool value) {
if (m_rxDcAuto != value)
return !(m_rxDcAuto = value);
return m_rxDcAuto;
}
BrfInterface* m_owner; // The interface owning the device
bool m_exiting; // Exiting flag
bool m_closing; // Closing flag
bool m_closingDevice; // Closing device flag
Mutex m_dbgMutex;
// libusb
libusb_context* m_context; // libusb context
libusb_device** m_list; // List of devices
unsigned int m_listCount; // Device list length
libusb_device_handle* m_devHandle; // Device handle
libusb_device* m_dev; // Pointer to used device (in m_list)
// Device info
int m_devBus; // Device bus
int m_devAddr; // Device address
int m_devSpeed; // Device speed
String m_devSerial; // Device serial number
String m_devFwVerStr; // Device firmware version string
String m_devFpgaVerStr; // Device FPGA version string
String m_devFpgaFile; // Device FPGA file (nul if not loaded)
uint16_t m_ctrlTransferPage; // Control transfer page size
DataBlock m_calCache;
//
unsigned int m_ctrlTout; // Control transfer timeout (in milliseconds)
unsigned int m_bulkTout; // Bulk transfer timeout (in milliseconds)
int m_altSetting;
int m_rxShowDcInfo; // Output Rx DC info
bool m_rxDcAuto; // Automatically adjust Rx DC offset
int m_rxDcOffsetMax; // Rx DC offset correction
int m_rxDcAvgI; // Current average for I (in-phase) DC RX offset
int m_rxDcAvgQ; // Current average for Q (quadrature) DC RX offset
int m_freqOffset; // Master clock frequency adjustment
BrfDevIO m_txIO;
BrfDevIO m_rxIO;
LusbTransfer m_usbTransfer[EpCount]; // List of USB transfers
int m_txISet; // Set TX DC I offs when syncing ts
int m_txQSet; // Set TX DC Q offs when syncing ts
int m_loopback; // Current loopback mode
String m_name;
// TX power scale
bool m_calibrated;
float m_txPowerBalance;
bool m_txPowerBalanceChanged;
float m_txPowerScaleI;
float m_txPowerScaleQ;
float m_wrPowerScaleI;
float m_wrPowerScaleQ;
int16_t m_wrMaxI;
int16_t m_wrMaxQ;
// Alter data
bool m_rxAlterData;
int16_t m_rxAlterIncrement;
bool m_txPatternChanged;
String m_txPatternStr;
DataBlock m_txPattern;
DataBlock m_txPatternBuffer;
unsigned int m_txPatternBufPos;
unsigned int m_txPatternBufSamples;
// Calibration
BrfThread* m_sendThread;
Mutex m_sendThreadMutex;
};
// Initialize data used to wait for interface Tx busy
// Clear the flag when destroyed
class BrfSerialize
{
public:
inline BrfSerialize(BrfLibUsbDevice* dev, bool tx, bool waitNow = true)
: status(0), m_device(dev), m_io(m_device->getIO(tx)), m_set(false) {
if (waitNow)
wait();
}
inline ~BrfSerialize()
{ drop(); }
inline void drop() {
if (m_set)
m_set = m_io.busy = false;
}
inline void wait() {
while (true) {
Lock lck(m_io.mutex,Thread::idleUsec());
if ((status = m_device->cancelled()) != 0)
return;
if (m_io.busy || !lck.locked())
continue;
m_set = m_io.busy = true;
return;
}
}
unsigned int status;
protected:
BrfLibUsbDevice* m_device;
BrfDevIO& m_io;
bool m_set;
};
class BrfInterface : public RadioInterface
{
YCLASS(BrfInterface,RadioInterface)
friend class BrfModule;
public:
~BrfInterface();
inline BrfLibUsbDevice* device() const
{ return m_dev; }
inline bool isDevice(void* dev) const
{ return m_dev && m_dev == (BrfLibUsbDevice*)dev; }
virtual unsigned int initialize(const NamedList& params = NamedList::empty());
virtual unsigned int setParams(NamedList& params, bool shareFate = true);
virtual unsigned int send(uint64_t when, float* samples, unsigned size,
float* powerScale = 0);
virtual unsigned int recv(uint64_t& when, float* samples, unsigned& size);
unsigned int setFrequency(uint64_t hz, bool tx);
unsigned int getFrequency(uint64_t& hz, bool tx) const;
virtual unsigned int setTxFreq(uint64_t hz)
{ return setFrequency(hz,true); }
virtual unsigned int getTxFreq(uint64_t& hz) const
{ return getFrequency(hz,true); }
virtual unsigned int setRxFreq(uint64_t hz)
{ return setFrequency(hz,false); }
virtual unsigned int getRxFreq(uint64_t& hz) const
{ return getFrequency(hz,false); }
virtual unsigned int setFreqOffset(int offs, int* newVal = 0)
{ return m_dev->setFreqOffset(offs,newVal); }
virtual unsigned int setSampleRate(uint64_t hz);
virtual unsigned int getSampleRate(uint64_t& hz) const;
virtual unsigned int setFilter(uint64_t hz);
virtual unsigned int getFilterWidth(uint64_t& hz) const;
unsigned int setRxGain(int val, unsigned port, bool preMixer);
virtual unsigned int setRxGain1(int val, unsigned port)
{ return setRxGain(val,port,true); }
virtual unsigned int setRxGain2(int val, unsigned port)
{ return setRxGain(val,port,false); }
unsigned int setTxGain(int val, unsigned port, bool preMixer);
virtual unsigned int setTxGain1(int val, unsigned port)
{ return setTxGain(val,port,true); }
virtual unsigned int setTxGain2(int val, unsigned port)
{ return setTxGain(val,port,false); }
// Calibration. Automatic tx/rx gain setting
// Set pre and post mixer value
unsigned int setGain(bool tx, int val, unsigned int port,
int* newValue = 0) const;
virtual unsigned int autocalDCOffsets()
{ return m_dev->calibrate(); }
unsigned int setOffsetCal(bool tx, bool Q, int val, unsigned port) const;
unsigned int getOffsetCal(bool tx, bool Q, int& val, unsigned port) const;
virtual unsigned int getTxIOffsetCal(int& val, unsigned port) const
{ return getOffsetCal(true,false,val,port); }
virtual unsigned int getTxQOffsetCal(int& val, unsigned port) const
{ return getOffsetCal(true,true,val,port); }
virtual unsigned int getRxIOffsetCal(int& val, unsigned port) const
{ return getOffsetCal(false,false,val,port); }
virtual unsigned int getRxQOffsetCal(int& val, unsigned port) const
{ return getOffsetCal(false,true,val,port); }
virtual unsigned int setTxIOffsetCal(int val, unsigned port) const
{ return setOffsetCal(true,false,val,port); }
virtual unsigned int setTxQOffsetCal(int val, unsigned port) const
{ return setOffsetCal(true,true,val,port); }
virtual unsigned int setRxIOffsetCal(int val, unsigned port) const
{ return setOffsetCal(false,false,val,port); }
virtual unsigned int setRxQOffsetCal(int val, unsigned port) const
{ return setOffsetCal(false,true,val,port); }
unsigned int getPhaseCal(bool tx, int& val, unsigned port) const;
unsigned int setPhaseCal(bool tx, int val, unsigned port);
virtual unsigned int getTxPhaseCal(int& val, unsigned port) const
{ return getPhaseCal(true,val,port); }
virtual unsigned int getRxPhaseCal(int& val, unsigned port) const
{ return getPhaseCal(false,val,port); }
virtual unsigned int setTxPhaseCal(unsigned val, unsigned port)
{ return setPhaseCal(true,val,port); }
virtual unsigned int setRxPhaseCal(unsigned val, unsigned port)
{ return setPhaseCal(true,val,port); }
virtual unsigned int getTxTime(uint64_t& time) const {
// Not tested
//return m_dev->getTimestamp(true,time);
time = 0;
return 0;
}
virtual unsigned int getRxTime(uint64_t& time) const {
// Not tested
//return m_dev->getTimestamp(false,time);
time = 0;
return 0;
}
virtual unsigned int getTime(uint64_t& time) const
{ return NotSupported; }
virtual unsigned int setTxPower(const unsigned dBm)
{ return setTxGain2(dBm,0); }
virtual unsigned int getFreqCal(int& val) const {
val = m_txFreqCorr;
return 0;
}
virtual unsigned int setFreqCal(int val);
virtual unsigned int getIQDelayCal(int& val, unsigned port = 0) const
{ return NotSupported; }
virtual unsigned int setPorts(unsigned ports) const
{ return NotSupported; }
virtual unsigned status(int port = -1) const
{ return (m_totalErr & FatalErrorMask); }
protected:
BrfInterface(const char* name);
// Method to call after creation to init the interface
virtual BrfLibUsbDevice* init();
virtual void destroyed();
private:
BrfLibUsbDevice* m_dev; // Used device
int m_txFreqCorr;
};
class BrfThread : public Thread
{
public:
inline BrfThread(BrfInterface* ifc, const char* name)
: Thread(name), m_name(name), m_iface(ifc)
{}
// Device send thread
inline BrfThread(BrfLibUsbDevice* dev, const char* name = "BrfDevSend")
: Thread(name), m_name(name), m_iface(0), m_device(dev)
{}
~BrfThread()
{ notify(); }
inline const char* name() const
{ return m_name; }
virtual void cleanup()
{ notify(); }
// Start this thread. Set destination pointer on success. Delete object on failure
BrfThread* start();
// Stop thread
static void cancelThread(BrfThread*& th, Mutex* mtx, unsigned int waitMs,
DebugEnabler* dbg, void* ptr);
protected:
virtual void run();
void notify();
String m_name;
RefPointer<BrfInterface> m_iface;
BrfLibUsbDevice* m_device;
};
class BrfTest : public BrfInterface, public Mutex
{
YCLASS(BrfTest,BrfInterface)
friend class BrfModule;
friend class BrfThread;
public:
enum State {
Idle = 0,
Running,
Stopping
};
BrfTest(const char* name, const NamedList& params, const NamedList& devOpen,
const NamedList& cmds);
inline int state() const
{ return m_state; }
inline void pause() {
m_pause = true;
Thread::msleep(100);
}
inline void resume() {
if (!m_pause)
return;
Thread::msleep(100);
updateTs(true);
updateTs(false);
m_pause = false;
}
bool start();
void stop();
bool execute(const String& cmd, const String& param, String& error,
bool fatal, const NamedList* params = 0);
protected:
bool execute(const NamedList& cmds, const char* prefix, String& error);
bool runInit();
void run();
void runSendOnly();
void runSendRecv();
void dumpStats();
bool workerTerminated(BrfThread* th);
bool checkPause(bool tx);
bool write();
bool read();
inline void updateTs(bool tx) {
uint64_t ts = 0;
if ((tx ? getTxTime(ts) : getRxTime(ts)) == 0)
(tx ? m_sendTs : m_readTs) = ts;
}
inline void resetBufs(unsigned int samples) {
m_bufs.reset(samples,0);
m_crt.assign(0,samplesf2bytes(m_bufs.bufSamples()));
m_aux = m_crt;
m_extra = m_crt;
m_bufs.crt.samples = (float*)m_crt.data(0);
m_bufs.aux.samples = (float*)m_aux.data(0);
m_bufs.extra.samples = (float*)m_extra.data(0);
}
int m_state;
bool m_pause;
bool m_pauseSend;
bool m_pauseRead;
unsigned int m_sendBufCount;
bool m_sendOnly;
BrfThread* m_worker;
// Send data
uint64_t m_sentSamples;
uint64_t m_sendTs;
DataBlock m_sendBufData;
float* m_sendBuf;
unsigned int m_sendBufSamples;
// Read data
RadioReadBufs m_bufs;
uint64_t m_readTs;
unsigned int m_skippedBuffs;
DataBlock m_crt;
DataBlock m_aux;
DataBlock m_extra;
// Params
NamedList m_params;
NamedList m_devOpen;
NamedList m_cmds;
};
class BrfModule : public Module
{
friend class BrfInterface;
public:
enum Relay {
RadioCreate = Private,
};
BrfModule();
~BrfModule();
bool findIfaceByDevice(RefPointer<BrfInterface>& iface, void* dev);
inline bool findIface(RefPointer<BrfInterface>& iface, const String& n) {
Lock lck(this);
ObjList* o = m_ifaces.find(n);
if (o)
iface = static_cast<BrfInterface*>(o->get());
return iface != 0;
}
inline void setTest(bool on, BrfTest* ptr = 0) {
Lock lck(this);
if (on) {
m_test = 0;
Lock lck(ptr);
if (ptr->state() == BrfTest::Running)
m_test = ptr;
}
else if (ptr && m_test == ptr)
m_test = 0;
}
protected:
virtual void initialize();
virtual bool received(Message& msg, int id);
virtual void statusModule(String& str);
virtual void statusParams(String& str);
virtual void statusDetail(String& str);
virtual bool commandComplete(Message& msg, const String& partLine,
const String& partWord);
BrfInterface* createIface(const NamedList& params);
void completeIfaces(String& dest, const String& partWord);
bool onCmdControl(BrfInterface* ifc, Message& msg);
bool onCmdStatus(String& retVal, String& line);
bool onCmdGain(BrfInterface* ifc, Message& msg);
bool onCmdCorrection(BrfInterface* ifc, Message& msg);
bool onCmdLmsWrite(BrfInterface* ifc, Message& msg);
bool onCmdBufOutput(BrfInterface* ifc, Message& msg);
bool onCmdShow(BrfInterface* ifc, Message& msg);
bool test(const String& cmd, NamedList& list);
void setDebugPeripheral(const String& value);
void setSampleEnergize(const String& value);
unsigned int m_ifaceId;
ObjList m_ifaces;
RefPointer<BrfTest> m_test;
};
static bool s_usbContextInit = false; // USB library init flag
INIT_PLUGIN(BrfModule);
static Configuration s_cfg; // Configuration file (protected by plugin mutex)
static uint8_t s_debugPeripheral = 0; // Debug peripheral access
static const String s_modCmds[] = {"test","help",""};
static const String s_ifcCmds[] = {"vgagain","correction","lmswrite",
"bufoutput","rxdcoutput","txpattern","show",""};
// libusb
static unsigned int s_lusbCtrlTransferTout = LUSB_CTRL_TIMEOUT; // Control transfer timeout def val (in milliseconds)
static unsigned int s_lusbBulkTransferTout = LUSB_BULK_TIMEOUT; // Bulk transfer timeout def val (in milliseconds)
static const String s_uartDevName[BrfLibUsbDevice::UartDevCount] = {"GPIO","LMS","VCTCXO","SI5338"};
static const TokenDict s_usbEndpoint[] = {
{"SEND_SAMPLES", BrfLibUsbDevice::EpSendSamples},
{"SEND_CTRL", BrfLibUsbDevice::EpSendCtrl},
{"READ_SAMPLES", BrfLibUsbDevice::EpReadSamples},
{"READ-CTRL", BrfLibUsbDevice::EpReadCtrl},
{0,0}
};
static const TokenDict s_loopback[] = {
{"firmware", BrfLibUsbDevice::LoopFirmware},
{"lpf-to-rxout", BrfLibUsbDevice::LoopLpfToRxOut},
{"lpf-to-vga2", BrfLibUsbDevice::LoopLpfToVga2},
{"vga1-to-vga2", BrfLibUsbDevice::LoopVga1ToVga2},
{"lpf-to-lpf", BrfLibUsbDevice::LoopLpfToLpf},
{"vga1-to-lpf", BrfLibUsbDevice::LoopVga1ToLpf},
{"pa-to-lna1", BrfLibUsbDevice::LoopRfLna1},
{"pa-to-lna2", BrfLibUsbDevice::LoopRfLna2},
{"pa-to-lna3", BrfLibUsbDevice::LoopRfLna3},
{"none", BrfLibUsbDevice::LoopNone},
{0,0}
};
static const TokenDict s_pa[] = {
{"AUX", BrfLibUsbDevice::LmsPaAux},
{"1", BrfLibUsbDevice::LmsPa1},
{"2", BrfLibUsbDevice::LmsPa2},
{0,0}
};
static const TokenDict s_lpf[] = {
{"disabled", BrfLibUsbDevice::LpfDisabled},
{"bypassed", BrfLibUsbDevice::LpfBypass},
{"normal", BrfLibUsbDevice::LpfNormal},
{0,0}
};
static const TokenDict s_lnaGain[] = {
{"BYPASS", BrfLibUsbDevice::LnaGainBypass},
{"MID", BrfLibUsbDevice::LnaGainMid},
{"MAX", BrfLibUsbDevice::LnaGainMax},
{"Unhandled", BrfLibUsbDevice::LnaGainUnhandled},
{0,0}
};
static bool completeStrList(String& dest, const String& partWord, const String* ptr)
{
if (!ptr)
return false;
while (*ptr)
Module::itemComplete(dest,*ptr++,partWord);
return false;
}
static inline void loadCfg(bool safe = true, NamedList* s1 = 0)
{
Lock lck(safe ? 0 : &__plugin);
s_cfg = Engine::configFile("ybladerf");
s_cfg.load();
if (!TelEngine::null(s1)) {
NamedList* tmp = s_cfg.getSection(*s1);
if (tmp)
*s1 = *tmp;
else
s1->assign("");
}
}
static void lusbSetDebugLevel(int level = -1)
{
// No lock needed: this function is called from plugin init (loads the config) or
// context init (locks the plugin)
if (!s_usbContextInit)
return;
if (level < 0) {
String* l = s_cfg.getKey(YSTRING("libusb"),YSTRING("debug_level"));
::libusb_set_debug(0,l ? l->toInteger(0,0,0) : 0);
}
else
::libusb_set_debug(0,level);
}
// libusb transfer stream callback
static void lusbTransferCb(libusb_transfer* transfer)
{
if (!transfer) {
DDebug(&__plugin,DebugWarn,"lusbTransferCb() called with NULL transfer");
return;
}
LusbTransfer* t = (LusbTransfer*)(transfer->user_data);
#ifdef DEBUG_LUSB_TRANSFER_CALLBACK
int level = DebugAll;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
level = DebugNote;
if (!__plugin.debugAt(level))
return;
RefPointer<BrfInterface> ifc;
String ifcInfo;
if (t && __plugin.findIfaceByDevice(ifc,t->device)) {
ifcInfo.printf("(%p %s)",(BrfInterface*)ifc,ifc->debugName());
ifc = 0;
}
String x;
String tmp;
x << "\r\ninterface=" << ifcInfo.safe("not found");
x << tmp.printf("\r\nhandle=%p",transfer->dev_handle);
x << tmp.printf("\r\nuser_data=%p",transfer->user_data);
x << tmp.printf("\r\nflags=0x%x",transfer->flags);
x << "\r\ntype=";
switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_CONTROL:
x << "CONTROL";
break;
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
x << "ISOCHRONOUS";
break;
case LIBUSB_TRANSFER_TYPE_BULK:
x << "BULK";
break;
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
x << "INTERRUPT";
break;
//case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
// x << "STREAM";
// break;
default:
x << (unsigned int)(transfer->type);
}
String endp;
switch (transfer->endpoint) {
case BRF_ENDP_TX_SAMPLES:
endp = lookup(BrfLibUsbDevice::EpSendSamples,s_usbEndpoint);
break;
case BRF_ENDP_TX_CTRL:
endp = lookup(BrfLibUsbDevice::EpSendCtrl,s_usbEndpoint);
break;
case BRF_ENDP_RX_SAMPLES:
endp = lookup(BrfLibUsbDevice::EpReadSamples,s_usbEndpoint);
break;
case BRF_ENDP_RX_CTRL:
endp = lookup(BrfLibUsbDevice::EpReadCtrl,s_usbEndpoint);
break;
default:
endp.printf("0x%x",transfer->endpoint);
}
x << "\r\nendpoint=" << endp;
x << "\r\ntimeout=" << transfer->timeout << "ms";
BrfLibUsbDevice::appendLusbError(x,transfer->status,"\r\nstatus=");
x << "\r\ncurrent_buffer_len=" << transfer->length;
x << "\r\ntransferred=" << transfer->actual_length;
Debug(&__plugin,level,"lusbTransferCb(%p)\r\n-----%s\r\n------",transfer,x.c_str());
#endif
if (!t)
return;
Lock lck(t);
t->status = BrfLibUsbDevice::lusbCheckSuccess(transfer->status,&t->error);
t->running(false);
}
//
// LusbTransfer
//
bool LusbTransfer::fillBulk(uint8_t* data, unsigned int len, unsigned int tout)
{
if (!alloc())
return false;
::libusb_fill_bulk_transfer(transfer,device->handle(),ep,data,len,lusbTransferCb,
this,tout);
return true;
}
bool LusbTransfer::submit()
{
status = BrfLibUsbDevice::lusbCheckSuccess(::libusb_submit_transfer(transfer),
&error,"libusb_submit_transfer() failed ");
return status == 0;
}
unsigned int LusbTransfer::cancel(String* error)
{
if (!transfer)
return 0;
int code = ::libusb_cancel_transfer(transfer);
if (code == LIBUSB_SUCCESS)
return 0;
m_running = false;
if (code == LIBUSB_ERROR_NOT_FOUND)
return 0;
return BrfLibUsbDevice::lusbCheckSuccess(code,error,
"libusb_cancel_transfer() failed ");
}
//
// BrfDevTmpAltSet
// Temporary change alt setting. Restore on destruction
//
unsigned int BrfDevTmpAltSet::set(int altSet, String* error, const char* oper)
{
restore();
if (!m_device || m_device->m_altSetting == altSet)
return 0;
unsigned int status = m_device->lusbSetAltInterface(altSet,error);
if (status)
return status;
m_oper = oper;
m_tmpAltSet = altSet;
DDebug(m_device->owner(),DebugAll,
"Temporary changed alt interface to %s for '%s' [%p]",
altSetName(m_tmpAltSet),m_oper,m_device->owner());
return 0;
}
unsigned int BrfDevTmpAltSet::restore()
{
if (m_tmpAltSet == BRF_ALTSET_INVALID)
return 0;
String e;
unsigned int status = m_device->lusbSetAltInterface(m_tmpAltSet,&e);
if (status == 0)
DDebug(m_device->owner(),DebugAll,
"Restored alt interface to %s after '%s' [%p]",
altSetName(m_tmpAltSet),m_oper,m_device->owner());
else
Debug(m_device->owner(),DebugGoOn,
"Failed to restore alt interface after '%s': %s [%p]",
m_oper,e.c_str(),m_device->owner());
m_tmpAltSet = BRF_ALTSET_INVALID;
return status;
}
//
// BrfLibUsbDevice
//
#define BRF_CHECK_DEV(text) { \
if (!m_devHandle) { \
Debug(m_owner,DebugGoOn,"%s: not open [%p]",text,m_owner); \
return RadioInterface::NotInitialized; \
} \
}
#define BRF_TX_SERIALIZE_(waitNow,instr) \
BrfSerialize txSerialize(this,true,waitNow); \
if (txSerialize.status) \
instr
#define BRF_TX_SERIALIZE BRF_TX_SERIALIZE_(true,return txSerialize.status)
#define BRF_TX_SERIALIZE_NONE BRF_TX_SERIALIZE_(true,return)
#define BRF_TX_SERIALIZE_BOOL BRF_TX_SERIALIZE_(true,return false)
#define BRF_RX_SERIALIZE_(waitNow,instr) \
BrfSerialize rxSerialize(this,false,waitNow); \
if (rxSerialize.status) \
instr
#define BRF_RX_SERIALIZE BRF_RX_SERIALIZE_(true,return rxSerialize.status)
#define BRF_RX_SERIALIZE_NONE BRF_RX_SERIALIZE_(true,return)
#define BRF_RX_SERIALIZE_BOOL BRF_RX_SERIALIZE_(true,return false)
BrfLibUsbDevice::BrfLibUsbDevice(BrfInterface* owner)
: m_owner(owner),
m_exiting(false),
m_closing(false),
m_closingDevice(false),
m_dbgMutex(false,"BrfDevDbg"),
m_context(0),
m_list(0),
m_listCount(0),
m_devHandle(0),
m_dev(0),
m_devBus(-1),
m_devAddr(-1),
m_devSpeed(LIBUSB_SPEED_HIGH),
m_ctrlTransferPage(0),
m_ctrlTout(s_lusbCtrlTransferTout),
m_bulkTout(s_lusbBulkTransferTout),
m_altSetting(BRF_ALTSET_INVALID),
m_rxShowDcInfo(0),
m_rxDcAuto(true),
m_rxDcOffsetMax(BRF_RX_DC_OFFSET_DEF),
m_rxDcAvgI(0),
m_rxDcAvgQ(0),
m_freqOffset(BRF_FREQ_OFFS_DEF),
m_txIO(true),
m_rxIO(false),
m_txISet(BRF_TX_DC_OFFSET_MIN - 1),
m_txQSet(BRF_TX_DC_OFFSET_MIN - 1),
m_loopback(0),
m_calibrated(false),
m_txPowerBalance(1),
m_txPowerBalanceChanged(false),
m_txPowerScaleI(1),
m_txPowerScaleQ(1),
m_wrPowerScaleI(s_sampleEnergize),
m_wrPowerScaleQ(s_sampleEnergize),
m_wrMaxI(s_sampleEnergize),
m_wrMaxQ(s_sampleEnergize),
m_rxAlterData(false),
m_rxAlterIncrement(0),
m_txPatternChanged(false),
m_txPatternBufPos(0),
m_txPatternBufSamples(0),
m_sendThread(0),
m_sendThreadMutex("BrfDevSendThread")
{
DDebug(&__plugin,DebugAll,"BrfLibUsbDevice(%p) [%p]",m_owner,this);
m_usbTransfer[EpSendSamples].device = this;
m_usbTransfer[EpSendSamples].ep = BRF_ENDP_TX_SAMPLES;
m_usbTransfer[EpSendCtrl].device = this;
m_usbTransfer[EpSendCtrl].ep = BRF_ENDP_TX_CTRL;
m_usbTransfer[EpReadSamples].device = this;
m_usbTransfer[EpReadSamples].ep = BRF_ENDP_RX_SAMPLES;
m_usbTransfer[EpReadCtrl].device = this;
m_usbTransfer[EpReadCtrl].ep = BRF_ENDP_RX_CTRL;
m_rxIO.vga1 = BRF_RXVGA1_GAIN_MAX + 1;
m_rxIO.dcOffsetI = BRF_RX_DC_OFFSET_MAX + 1;
m_rxIO.dcOffsetQ = BRF_RX_DC_OFFSET_MAX + 1;
m_txIO.vga1 = BRF_TXVGA1_GAIN_MIN - 1;
m_txIO.vga2 = BRF_TXVGA2_GAIN_MIN - 1;
m_txIO.dcOffsetI = BRF_RX_DC_OFFSET_MAX + 1;
m_txIO.dcOffsetQ = BRF_RX_DC_OFFSET_MAX + 1;
}
BrfLibUsbDevice::~BrfLibUsbDevice()
{
DDebug(&__plugin,DebugAll,"~BrfLibUsbDevice(%p) [%p]",m_owner,this);
doClose();
}
unsigned int BrfLibUsbDevice::setTxPattern(const String& pattern)
{
Lock lck(m_dbgMutex);
if (m_txPatternStr == pattern)
return 0;
bool hadData = (m_txPattern.length() != 0);
m_txPattern.clear();
m_txPatternStr.clear();
m_txPatternChanged = true;
String tmp;
bool readArray = true;
bool dumpArray = false;
if (pattern == "circle")
tmp = "1,0,0,1,-1,0,0,-1";
else if (pattern == "zero")
tmp = "0,0";
else if (pattern == "random") {
readArray = false;
unsigned int samples = totalSamples(true);
m_txPattern.resize(samplesf2bytes(samples));
float* f = (float*)m_txPattern.data(0);
for (float* last = f + m_txPattern.length() / sizeof(float); f != last; f++) {
long int r = Random::random();
if (!r)
continue;
uint64_t v = (r >= 0) ? r : -r;
*f = (float)(((v % 2) == 0) ? 1 : -1) * (v % 2047) / 2047;
}
//dumpArray = true;
}
else if (pattern == "increment") {
readArray = false;
unsigned int samples = 557;
m_txPattern.resize(samplesf2bytes(samples));
float* f = (float*)m_txPattern.data(0);
for (unsigned int i = 1; i <= samples; i++, f += 2)
f[0] = f[1] = (float)i / 2047;
//dumpArray = true;
}
const String* p = tmp ? (const String*)&tmp : &pattern;
if (readArray && *p && !readSamples(m_txPattern,*p)) {
Debug(m_owner,DebugNote,"Invalid tx pattern '%s' [%p]",p->c_str(),m_owner);
return RadioInterface::Failure;
}
if (m_txPattern.length()) {
m_txPatternStr = pattern;
String s;
if (dumpArray) {
appendComplex(s,m_txPattern);
s = "\r\n-----\r\n" + s + "\r\n";
}
Debug(m_owner,DebugInfo,"TX pattern set to '%s' [%p]%s",
m_txPatternStr.c_str(),m_owner,s.safe());
}
else if (hadData)
Debug(m_owner,DebugInfo,"TX pattern cleared [%p]",m_owner);
return 0;
}
static inline String& dumpIOAvg(String& buf, BrfDevIO& io, uint64_t now)
{
if (io.startTime && io.transferred) {
unsigned int sec = (unsigned int)((now - io.startTime) / 1000000);
if (sec) {
buf = io.transferred / sec;
return (buf << " samples/sec");
}
}
return (buf = "-");
}
void BrfLibUsbDevice::dumpStats(String& buf, const char* sep)
{
BRF_RX_SERIALIZE_NONE;
BRF_TX_SERIALIZE_NONE;
String s;
uint64_t now = Time::now();
buf << sep << "TxTS=" << m_txIO.timestamp;
buf << sep << "RxTS=" << m_rxIO.timestamp;
buf << sep << "TxAvg=" << dumpIOAvg(s,m_txIO,now);
buf << sep << "RxAvg=" << dumpIOAvg(s,m_rxIO,now);
}
void BrfLibUsbDevice::dumpDev(String& buf, bool info, bool state, const char* sep,
bool fromStatus, bool withHdr)
{
if (!(info || state))
return;
BRF_RX_SERIALIZE_NONE;
BRF_TX_SERIALIZE_NONE;
internalDumpDev(buf,info,state,sep,false,fromStatus,withHdr);
}
void BrfLibUsbDevice::dumpBoardStatus(String& buf, const char* sep)
{
#define ADD_INTERVAL(minVal,maxVal) if (!code) addIntervalInt(buf,minVal,maxVal);
#define BOARD_STATUS_SET_TMP(func,instr_ok) { \
code = func; \
if (!code) \
instr_ok; \
else \
tmp.printf("ERROR %u %s",code,RadioInterface::errorName(code)); \
}
#define BOARD_STATUS_SET(func,instr_ok,prefix,suffix) { \
BOARD_STATUS_SET_TMP(func,instr_ok); \
buf.append(prefix + tmp + (!code ? suffix : ""),sep); \
}
#define dumpDevAppend(func,val,prefix,suffix) BOARD_STATUS_SET(func,tmp = (int64_t)val,prefix,suffix)
#define dumpDevAppendFreq(func,val,prefix,suffix) \
BOARD_STATUS_SET(func,dumpFloatG(tmp,(double)val / 1000000,0,"MHz"),prefix,suffix)
#define reportLpf(tx) { \
BOARD_STATUS_SET(getLpf(intVal,tx),tmp = lookup(intVal,s_lpf),(tx ? "TxLpf=" : "RxLpf="),0); \
if (!code) { \
BOARD_STATUS_SET_TMP(getLpfBandwidth(u32Val,tx),dumpFloatG(tmp,(double)u32Val / 1000000,0,"MHz")); \
buf << " BW: " << tmp; \
} \
}
int intVal = 0;
int16_t int16Val = 0;
uint32_t u32Val = 0;
uint64_t u64Val = 0;
unsigned int code = 0;
String tmp;
dumpDevAppend(getTimestamp(false,u64Val),u64Val,"RxTS=",0);
dumpDevAppend(getTimestamp(true,u64Val),u64Val,"TxTS=",0);
dumpDevAppend(getRxVga1(intVal),intVal,"RxVGA1="," dB");
ADD_INTERVAL(BRF_RXVGA1_GAIN_MIN,BRF_RXVGA1_GAIN_MAX);
dumpDevAppend(getRxVga2(intVal),intVal,"RxVGA2="," dB");
ADD_INTERVAL(BRF_RXVGA2_GAIN_MIN,BRF_RXVGA2_GAIN_MAX);
dumpDevAppend(getTxVga1(intVal),intVal,"TxVGA1="," dB");
ADD_INTERVAL(BRF_TXVGA1_GAIN_MIN,BRF_TXVGA1_GAIN_MAX);
dumpDevAppend(getTxVga2(intVal),intVal,"TxVGA2="," dB");
ADD_INTERVAL(BRF_TXVGA2_GAIN_MIN,BRF_TXVGA2_GAIN_MAX);
dumpDevAppend(getDcOffset(false,true,int16Val),int16Val,"RxDCCorrI=",0);
ADD_INTERVAL(-BRF_RX_DC_OFFSET_MAX,BRF_RX_DC_OFFSET_MAX);
dumpDevAppend(getDcOffset(false,false,int16Val),int16Val,"RxDCCorrQ=",0);
ADD_INTERVAL(-BRF_RX_DC_OFFSET_MAX,BRF_RX_DC_OFFSET_MAX);
dumpDevAppend(getDcOffset(true,true,int16Val),int16Val,"TxDCCorrI=",0);
ADD_INTERVAL(BRF_TX_DC_OFFSET_MIN,BRF_TX_DC_OFFSET_MAX);
dumpDevAppend(getDcOffset(true,false,int16Val),int16Val,"TxDCCorrQ=",0);
ADD_INTERVAL(BRF_TX_DC_OFFSET_MIN,BRF_TX_DC_OFFSET_MAX);
dumpDevAppend(getFpgaCorr(false,CorrFpgaPHASE,int16Val),int16Val,"RxCorrFpgaPhase=",0);
ADD_INTERVAL(-BRF_FPGA_CORR_MAX,BRF_FPGA_CORR_MAX);
dumpDevAppend(getFpgaCorr(false,CorrFpgaGAIN,int16Val),int16Val,"RxCorrFpgaGain=",0);
ADD_INTERVAL(-BRF_FPGA_CORR_MAX,BRF_FPGA_CORR_MAX);
dumpDevAppend(getFpgaCorr(true,CorrFpgaPHASE,int16Val),int16Val,"TxCorrFpgaPhase=",0);
ADD_INTERVAL(-BRF_FPGA_CORR_MAX,BRF_FPGA_CORR_MAX);
dumpDevAppend(getFpgaCorr(true,CorrFpgaGAIN,int16Val),int16Val,"TxCorrFpgaGain=",0);
ADD_INTERVAL(-BRF_FPGA_CORR_MAX,BRF_FPGA_CORR_MAX);
dumpDevAppendFreq(getFrequency(u32Val,false),u32Val,"RxFreq=",0);
dumpDevAppendFreq(getFrequency(u32Val,true),u32Val,"TxFreq=",0);
dumpDevAppend(getSamplerate(u32Val,false),u32Val,"RxSampRate=",0);
dumpDevAppend(getSamplerate(u32Val,true),u32Val,"TxSampRate=",0);
reportLpf(false);
reportLpf(true);
{
BRF_TX_SERIALIZE_NONE;
String tmp;
buf.append("calibration-cache=" + dumpCalCache(tmp),sep);
}
#undef ADD_INTERVAL
#undef BOARD_STATUS_SET_TMP
#undef BOARD_STATUS_SET
#undef dumpDevAppend
#undef dumpDevAppendFreq
#undef reportLpf
}
unsigned int BrfLibUsbDevice::dumpPeripheral(uint8_t dev, uint8_t addr, uint8_t len, String* buf)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("dumpPeripheral()");
addr = clampInt(addr,0,0x7f);
len = clampInt(len,1,128 - addr);
return internalDumpPeripheral(dev,addr,len,buf,16);
}
// Initialize the device.
// Call the reset method in order to set the device to a known state
bool BrfLibUsbDevice::open(const NamedList& params)
{
BRF_RX_SERIALIZE_BOOL;
BRF_TX_SERIALIZE_BOOL;
doClose();
String e;
unsigned int status = 0;
while (true) {
BRF_FUNC_CALL_BREAK(resetUsb(&e));
BRF_FUNC_CALL_BREAK(openDevice(true,&e));
BRF_FUNC_CALL_BREAK(updateSpeed(&e));
m_calCache.clear();
readCalCache();
status = updateFpga(params);
if (status) {
e = "Failed to load FPGA";
break;
}
status = lusbSetAltInterface(BRF_ALTSET_IDLE,&e);
if (status)
break;
m_freqOffset = clampIntParam(params,"RadioFrequencyOffset",
BRF_FREQ_OFFS_DEF,BRF_FREQ_OFFS_MIN,BRF_FREQ_OFFS_MAX);
// Init TX/RX buffers
m_rxDcAuto = params.getBoolValue("rx_dc_autocorrect",true);
m_rxShowDcInfo = params.getIntValue("rx_dc_showinfo");
m_rxDcOffsetMax = BRF_RX_DC_OFFSET_DEF;
m_rxIO.dcOffsetI = BRF_RX_DC_OFFSET_MAX + 1;
m_rxIO.dcOffsetQ = BRF_RX_DC_OFFSET_MAX + 1;
int tmpInt = 0;
int i = 0;
int q = 0;
#if 1
i = clampIntParam(params,"RX.OffsetI",0,-BRF_RX_DC_OFFSET_MAX,BRF_RX_DC_OFFSET_MAX);
q = clampIntParam(params,"RX.OffsetQ",0,-BRF_RX_DC_OFFSET_MAX,BRF_RX_DC_OFFSET_MAX);
BRF_FUNC_CALL_BREAK(internalSetCorrectionIQ(false,i,q,&e));
#endif
BRF_FUNC_CALL_BREAK(internalEnableRxVga(true,true,&e));
BRF_FUNC_CALL_BREAK(internalEnableRxVga(true,false,&e));
i = clampIntParam(params,"TX.OffsetI",0,BRF_TX_DC_OFFSET_MIN,BRF_TX_DC_OFFSET_MAX);
q = clampIntParam(params,"TX.OffsetQ",0,BRF_TX_DC_OFFSET_MIN,BRF_TX_DC_OFFSET_MAX);
BRF_FUNC_CALL_BREAK(internalSetCorrectionIQ(true,i,q,&e));
// Set RX gain
m_rxIO.vga1 = BRF_RXVGA1_GAIN_MAX + 1;
BRF_FUNC_CALL_BREAK(internalSetGain(false,BRF_RXVGA2_GAIN_MIN));
// Pre/post mixer TX VGA
const String& txVga1 = params["tx_vga1"];
if (txVga1)
BRF_FUNC_CALL_BREAK(internalSetTxVga(txVga1.toInteger(BRF_TXVGA1_GAIN_DEF),true,&e));
const String& txVga2 = params["tx_vga2"];
if (txVga2)
BRF_FUNC_CALL_BREAK(internalSetTxVga(txVga2.toInteger(BRF_TXVGA2_GAIN_MIN),false,&e));
// Set FPGA correction
tmpInt = clampIntParam(params,"tx_fpga_corr_phase",0,-BRF_FPGA_CORR_MAX,BRF_FPGA_CORR_MAX);
status = internalSetFpgaCorr(true,CorrFpgaPHASE,tmpInt,&e);
if (status)
break;
// Make sure we have the correct values for status
BRF_FUNC_CALL_BREAK(updateStatus(&e));
// Set tx I/Q balance
const String& txPB = params["tx_powerbalance"];
internalSetTxIQBalance(false,txPB.toDouble(1),"tx_powerbalance");
// Set some optional params
setTxPattern(params["txpattern"]);
showBuf(true,params.getIntValue("txbufoutput",0),
params.getBoolValue("txbufoutput_nodata"));
showBuf(false,params.getIntValue("rxbufoutput",0),
params.getBoolValue("rxbufoutput_nodata"));
checkTs(true,params.getIntValue("txcheckts",0));
checkTs(false,params.getIntValue("rxcheckts",0));
break;
}
if (status == 0) {
updateAlterData(params);
String s;
internalDumpDev(s,true,false,"\r\n",true);
Debug(m_owner,DebugAll,"Opened device [%p]\r\n-----\r\n%s\r\n-----",
m_owner,s.c_str());
}
else {
Debug(m_owner,DebugWarn,"Failed to open USB device: %s [%p]",
e.safe("Unknown error"),m_owner);
doClose();
}
return status == 0;
}
// Close the device
void BrfLibUsbDevice::close()
{
BRF_RX_SERIALIZE_NONE;
BRF_TX_SERIALIZE_NONE;
doClose();
}
// Power on the radio
// Enable timestamps, enable RF TX/RX
unsigned int BrfLibUsbDevice::powerOn()
{
BRF_RX_SERIALIZE_BOOL;
BRF_TX_SERIALIZE_BOOL;
BRF_CHECK_DEV("powerOn()");
return internalPowerOn(true);
}
// Send an array of samples waiting to be transmitted
unsigned int BrfLibUsbDevice::syncTx(uint64_t ts, float* data, unsigned int samples,
float* powerScale)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("syncTx()");
return send(ts,data,samples,powerScale);
}
// Receive data from the Rx interface of the bladeRF device
unsigned int BrfLibUsbDevice::syncRx(uint64_t& ts, float* data, unsigned int& samples)
{
BRF_RX_SERIALIZE;
BRF_CHECK_DEV("syncRx()");
return recv(ts,data,samples);
}
unsigned int BrfLibUsbDevice::setFrequency(uint32_t hz, bool tx)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setFrequency()");
return internalSetFrequency(tx,hz);
}
unsigned int BrfLibUsbDevice::getFrequency(uint32_t& hz, bool tx)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getFrequency()");
return internalGetFrequency(tx,&hz);
}
// Set frequency offset
unsigned int BrfLibUsbDevice::setFreqOffset(int offs, int* newVal)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setFreqOffset()");
return internalSetFreqOffs(offs,newVal);
}
// Get frequency offset
unsigned int BrfLibUsbDevice::getFreqOffset(int& offs)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getFreqOffset()");
String val;
unsigned int status = getCalField(val,"DAC","DAC_TRIM");
if (status == 0)
offs = val.toInteger();
return status;
}
// Set the bandwidth for a specific module
unsigned int BrfLibUsbDevice::setLpfBandwidth(uint32_t band, bool tx)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setLpfBandwidth()");
return internalSetLpfBandwidth(tx,band);
}
// Get the bandwidth for a specific module
unsigned int BrfLibUsbDevice::getLpfBandwidth(uint32_t& band, bool tx)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getLpfBandwidth()");
String e;
unsigned int status = lusbSetAltInterface(BRF_ALTSET_RF_LINK,&e);
if (status == 0) {
uint8_t data = 0;
status = lmsRead(lmsLpfAddr(tx),data,&e);
if (status == 0) {
data >>= 2;
data &= 0xf;
band = s_bandSet[15 - data];
getIO(tx).lpfBw = band;
}
}
if (status == 0)
XDebug(m_owner,DebugAll,"Got %s LPF bandwidth %u [%p]",brfDir(tx),band,m_owner);
else
Debug(m_owner,DebugNote,"Failed to retrieve %s LPF bandwidth: %s [%p]",
brfDir(tx),e.c_str(),m_owner);
return status;
}
unsigned int BrfLibUsbDevice::setLpf(int lpf, bool tx)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setLpf()");
return internalSetLpf(tx,lpf);
}
unsigned int BrfLibUsbDevice::getLpf(int& lpf, bool tx)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getLpf()");
return internalGetLpf(tx,&lpf);
}
// Set the sample rate on a specific module
unsigned int BrfLibUsbDevice::setSamplerate(uint32_t value, bool tx)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setSamplerate()");
return internalSetSampleRate(tx,value);
}
// Get the sample rate on a specific module
unsigned int BrfLibUsbDevice::getSamplerate(uint32_t& value, bool tx)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getSamplerate()");
String e;
unsigned int status = lusbSetAltInterface(BRF_ALTSET_RF_LINK,&e);
while (!status) {
BrfRationalRate rate;
Si5338MultiSynth synth;
uint8_t val = 0;
synth.index = 1;
if (tx)
synth.index = 2;
synth.base = 53 + synth.index * 11;
// Read the enable bits
if ((status = getSi5338(36 + synth.index,val,&e)) != 0)
break;
synth.enable = val&7;
// Read all of the multisynth registers
for (int i = 0; i < 10; i++)
if ((status = getSi5338(synth.base + i,synth.regs[i],&e)) != 0)
break;
if (status)
break;
// Populate the RxDIV value from the register
if ((status = getSi5338(31 + synth.index,val,&e)) != 0)
break;
// RxDIV is stored as a power of 2, so restore it on readback
val = (val>>2)&7;
synth.r = (1<<val);
// Unpack the regs into appropriate values
unpackRegs(synth);
calcSrate(synth,rate);
if (rate.integer > 0xffffffff) {
e = "The value for the sample rate is too big";
status = RadioInterface::Failure;
break;
}
if (rate.numerator)
Debug(m_owner,DebugMild,
"Truncating the %s fractional part of the samplerate [%p]",
brfDir(tx),m_owner);
value = (uint32_t)rate.integer;
getIO(tx).sampleRate = value;
break;
}
if (status == 0)
XDebug(m_owner,DebugAll,"Got %s samplerate %u [%p]",brfDir(tx),value,m_owner);
else
Debug(m_owner,DebugNote,"Failed to get %s samplerate: %s [%p]",
brfDir(tx),e.c_str(),m_owner);
return status;
}
// Set the pre-mixer gain on transmission (interval [-35..-4])
// Set the post-mixer gain setting on transmission (interval: [0..25])
unsigned int BrfLibUsbDevice::setTxVga(int vga, bool preMixer)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setTxVga()");
return internalSetTxVga(vga,preMixer);
}
// Retrieve the pre/post mixer gain setting on transmission
unsigned int BrfLibUsbDevice::getTxVga(int& vga, bool preMixer)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getTxVga()");
return internalGetTxVga(&vga,preMixer);
}
// Set TX power balance
unsigned int BrfLibUsbDevice::setTxIQBalance(float value)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setTxIQBalance()");
return internalSetTxIQBalance(false,value);
}
// Enable or disable the pre/post mixer gain on the receive side
unsigned int BrfLibUsbDevice::enableRxVga(bool on, bool preMixer)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("enableRxVga()");
return internalEnableRxVga(on,preMixer);
}
// Set the pre-mixer gain setting on the receive side (interval [5..30])
// Set the post-mixer Rx gain setting (interval [0..30])
unsigned int BrfLibUsbDevice::setRxVga(int vga, bool preMixer)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setRxVga()");
return internalSetRxVga(vga,preMixer);
}
// Retrieve the pre/post mixer rx gain setting
unsigned int BrfLibUsbDevice::getRxVga(int& vga, bool preMixer)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getRxVga()");
return internalGetRxVga(&vga,preMixer);
}
// Set pre and post mixer value
unsigned int BrfLibUsbDevice::setGain(bool tx, int val, int* newVal)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setGain()");
return internalSetGain(tx,val,newVal);
}
// Auto calibrate DC offsets
unsigned int BrfLibUsbDevice::calibrate()
{
BRF_RX_SERIALIZE;
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("calibrate()");
#ifdef DEBUG
Debugger d(DebugAll,"CALIBRATE"," %s [%p]",m_owner->debugName(),m_owner);
#endif
Debug(m_owner,DebugInfo,"Calibrating ... [%p]",m_owner);
BrfDevStatus txOld(m_txIO);
BrfDevStatus rxOld(m_rxIO);
String e;
unsigned int status = internalSetDcCorr(0,0,0,0,&e);
int8_t calVal[BRF_CALIBRATE_LAST][BRF_CALIBRATE_MAX_SUBMODULES];
::memset(calVal,-1,sizeof(calVal));
for (int m = BRF_CALIBRATE_FIRST; !status && m <= BRF_CALIBRATE_LAST; m++) {
if (m == BRF_CALIBRATE_LPF_BANDWIDTH) {
DDebug(m_owner,DebugStub,"%s auto calibration not implemented [%p]",
calModName(m),m_owner);
continue;
}
if ((status = cancelled(&e)) != 0)
break;
BrfCalData bak(m);
Debug(m_owner,DebugAll,"Calibrating %s [%p]",bak.modName(),m_owner);
if ((status = calBackupRestore(bak,true,&e)) != 0)
break;
status = calInitFinal(bak,true,&e);
for (uint8_t subMod = 0; !status && subMod < bak.desc->subModules; subMod++) {
status = dcCalProcPrepare(bak,subMod,e);
if (!status) {
uint8_t dcReg = 0;
status = dcCalProc(bak,subMod,31,dcReg,e);
if (!status) {
calVal[m][subMod] = dcReg;
status = dcCalProcPost(bak,subMod,dcReg,e);
}
}
if (status)
e.printf(2048,"Failed to calibrate module %s - %s",
bak.modName(),e.c_str());
}
unsigned int tmp = calInitFinal(bak,false,status ? 0 : &e);
if (!status)
status = tmp;
tmp = calBackupRestore(bak,false,status ? 0 : &e);
if (!status)
status = tmp;
if (status)
break;
Debug(m_owner,DebugAll,"Calibrated %s [%p]",bak.modName(),m_owner);
}
bool txLoOk = false;
if (!status) {
status = calibrateTxLOLeakage(&e);
txLoOk = (status == 0);
}
m_calibrated = (status == 0);
if (!txLoOk) {
// Restore DC offsets
unsigned int tmp = internalSetDcCorr(txOld.dcOffsetI,txOld.dcOffsetQ,
rxOld.dcOffsetI,rxOld.dcOffsetQ,status ? 0 : &e);
if (!status)
status = tmp;
}
if (status) {
if (status == RadioInterface::Cancelled)
Debug(m_owner,DebugInfo,"Calibration cancelled [%p]",m_owner);
else
Debug(m_owner,DebugWarn,"Calibration failed: %s [%p]",e.c_str(),m_owner);
return status;
}
status = updateStatus();
if (status) {
Debug(m_owner,DebugWarn,
"Calibration finished. Failed to retrieve status [%p]",m_owner);
return status;
}
String s = "\r\n-----";
for (int m = BRF_CALIBRATE_FIRST; m <= BRF_CALIBRATE_LAST; m++) {
if (m == BRF_CALIBRATE_LPF_BANDWIDTH)
continue;
const BrfCalDesc& d = s_calModuleDesc[m];
String t;
if (d.subModules > 1)
for (uint8_t sm = 0; sm < d.subModules; sm++) {
t.printf("\r\n%s - %s: %d",calModName(m),d.subModName[sm],calVal[m][sm]);
s << t;
}
else
s << t.printf("\r\n%s: %d",calModName(m),calVal[m][0]);
}
s << "\r\nTX I/Q: " << m_txIO.dcOffsetI << "/" << m_txIO.dcOffsetQ;
s << "\r\nRX I/Q: " << m_rxIO.dcOffsetI << "/" << m_rxIO.dcOffsetQ;
s << "\r\n-----";
Debug(m_owner,DebugInfo,"Calibration finished [%p]%s",m_owner,s.c_str());
return 0;
}
// Set Tx/Rx DC I/Q offset correction
unsigned int BrfLibUsbDevice::setDcOffset(bool tx, bool i, int16_t value)
{
int rxDcAutoRestore = -1;
// Temporary disable RX auto correct
if (!tx) {
BRF_RX_SERIALIZE;
BRF_CHECK_DEV("setDcOffset()");
rxDcAutoRestore = setRxDcAuto(false) ? 1 : 0;
}
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setDcOffset()");
unsigned int status = internalSetDcOffset(tx,i,value);
if (tx)
return status;
if (status == 0) {
// Don't restore old RX DC autocorrect: the values are set by the upper layer
if (rxDcAutoRestore > 0)
Debug(m_owner,DebugInfo,
"Disabled RX DC autocorrect: I/Q values set by the upper layer [%p]",this);
}
else if (rxDcAutoRestore > 0)
// Failure: restore old RX DC autocorrect
m_rxDcAuto = true;
return status;
}
// Retrieve Tx/Rx DC I/Q offset correction
unsigned int BrfLibUsbDevice::getDcOffset(bool tx, bool i, int16_t& value)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getDcOffset()");
unsigned int status = internalGetDcOffset(tx,i,&value);
return status;
}
unsigned int BrfLibUsbDevice::setFpgaCorr(bool tx, int corr, int16_t value)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setFpgaCorr()");
return internalSetFpgaCorr(tx,corr,value,0,DebugNote);
}
unsigned int BrfLibUsbDevice::getFpgaCorr(bool tx, int corr, int16_t& value)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getFpgaCorr()");
int16_t v = 0;
unsigned int status = internalGetFpgaCorr(tx,corr,&v);
value = v;
return status;
}
unsigned int BrfLibUsbDevice::getTimestamp(bool tx, uint64_t& ts)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getTimestamp()");
return internalGetTimestamp(tx,ts);
}
unsigned int BrfLibUsbDevice::writeLMS(uint8_t addr, uint8_t value, uint8_t* rst)
{
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("getTimestamp()");
if (rst)
return lmsSet(addr,value,*rst);
return lmsWrite(addr,value);
}
unsigned int BrfLibUsbDevice::setLoopback(const char* name)
{
int mode = LoopNone;
if (!TelEngine::null(name))
mode = lookup(name,s_loopback,LoopUnknown);
if (mode == LoopUnknown) {
Debug(m_owner,DebugNote,"Unknown loopback mode '%s' [%p]",name,m_owner);
return RadioInterface::OutOfRange;
}
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setLoopback()");
return internalSetLoopback(mode);
}
// Enable or disable loopback
unsigned int BrfLibUsbDevice::setLoopback(int mode)
{
if (!lookup(mode,s_loopback)) {
Debug(m_owner,DebugNote,"Unknown loopback mode %d [%p]",mode,m_owner);
return RadioInterface::OutOfRange;
}
BRF_TX_SERIALIZE;
BRF_CHECK_DEV("setLoopback()");
return internalSetLoopback(mode);
}
// Release data
void BrfLibUsbDevice::destruct()
{
doClose();
for (unsigned int i = 0; i < EpCount; i++)
m_usbTransfer[i].reset();
GenObject::destruct();
}
// Create an interface
BrfLibUsbDevice* BrfLibUsbDevice::create(BrfInterface* owner)
{
if (!s_usbContextInit) {
Lock lck(__plugin);
if (!s_usbContextInit) {
int status = ::libusb_init(0);
if (status != LIBUSB_SUCCESS) {
String tmp;
Debug(&__plugin,DebugNote,"Failed to initialize libusb %s",
appendLusbError(tmp,status).c_str());
return 0;
}
Debug(&__plugin,DebugAll,"Initialized libusb context");
s_usbContextInit = true;
lusbSetDebugLevel();
}
}
return new BrfLibUsbDevice(owner);
}
uint64_t BrfLibUsbDevice::reduceFurther(uint64_t v1, uint64_t v2)
{
if (!(v1 && v2))
return 1;
while (v2) {
uint64_t tmp = v1 % v2;
v1 = v2;
v2 = tmp;
}
return v1;
}
void BrfLibUsbDevice::reduceRational(BrfRationalRate& rate)
{
while (rate.denominator > 0 && rate.numerator >= rate.denominator) {
rate.numerator = rate.numerator - rate.denominator;
rate.integer++;
}
// Reduce what's left of the fraction
uint64_t val = reduceFurther(rate.numerator,rate.denominator);
if (val) {
rate.numerator /= val;
rate.denominator /= val;
}
}
void BrfLibUsbDevice::calcSrate(Si5338MultiSynth& synth, BrfRationalRate& rate)
{
BrfRationalRate tmp;
tmp.integer = synth.a;
tmp.numerator = synth.b;
tmp.denominator = synth.c;
rate.integer = 0;
rate.numerator = SI5338_F_VCO * tmp.denominator;
rate.denominator = (uint64_t)synth.r * 2 *
(tmp.integer * tmp.denominator + tmp.numerator);
reduceRational(rate);
}
unsigned int BrfLibUsbDevice::calcMultiSynth(Si5338MultiSynth& synth,
BrfRationalRate& rate, String* error)
{
BrfRationalRate tmp;
// Double requested frequency since LMS requires 2:1 clock:sample rate
rationalDouble(rate);
// Find a suitable R value
uint8_t rValue = 1;
uint8_t rPower = 0;
while (rate.integer < 5000000 && rValue < 32) {
rationalDouble(rate);
rValue <<= 1;
rPower++;
}
if (rValue == 32 && rate.integer < 5000000)
return setError(RadioInterface::Failure,error,"Multi synth calculation failed");
// Find suitable MS (a, b, c) values
tmp.integer = 0;
tmp.numerator= SI5338_F_VCO * rate.denominator;
tmp.denominator= rate.integer * rate.denominator + rate.numerator;
reduceRational(tmp);
// Check values to make sure they are OK
if (tmp.integer < 8 || tmp.integer > 567)
return setError(RadioInterface::Failure,error,
"Multi synth calculation - the integer part is out of bounds");
// Loss of precision if numeratoror denominatorare greater than 2^30-1
bool warn = true;
while (tmp.numerator > (1 << 30) || tmp.denominator > (1 << 30)) {
if (warn) {
warn = false;
Debug(&__plugin,DebugMild,
"Multi synth calculation: numerator or denominator are too big, we'll loose precision");
}
tmp.numerator >>= 1;
tmp.denominator >>= 1;
}
if (tmp.integer > 0xffffffff || tmp.numerator > 0xffffffff ||
tmp.denominator > 0xffffffff)
return setError(RadioInterface::Failure,error,
"Multi synth calculation - rate parts are too big");
synth.a = (uint32_t)tmp.integer;
synth.b = (uint32_t)tmp.numerator;
synth.c = (uint32_t)tmp.denominator;
synth.r = rValue;
// Pack the registers
packRegs(synth);
return 0;
}
void BrfLibUsbDevice::packRegs(Si5338MultiSynth& synth)
{
uint64_t tmp = (uint64_t)synth.a * synth.c + synth.b;
tmp = tmp * 128 ;
tmp = tmp / synth.c - 512;
synth.p1 = (uint32_t)tmp;
tmp = (uint64_t)synth.b * 128;
tmp = tmp % synth.c;
synth.p2 = (uint32_t)tmp;
synth.p3 = synth.c;
// Set regs
synth.regs[0] = (uint8_t)synth.p1;
synth.regs[1] = (uint8_t)(synth.p1 >> 8);
synth.regs[2] = (uint8_t)((synth.p2 & 0x3f) << 2) | ((synth.p1 >> 16) & 0x3);
synth.regs[3] = (uint8_t)(synth.p2 >> 6);
synth.regs[4] = (uint8_t)(synth.p2 >> 14);
synth.regs[5] = (uint8_t)(synth.p2 >> 22);
synth.regs[6] = (uint8_t)synth.p3;
synth.regs[7] = (uint8_t)(synth.p3 >> 8);
synth.regs[8] = (uint8_t)(synth.p3 >> 16);
synth.regs[9] = (uint8_t)(synth.p3 >> 24);
}
void BrfLibUsbDevice::unpackRegs(Si5338MultiSynth& synth)
{
// Populate
synth.p1 = ((synth.regs[2] & 3) << 16) | (synth.regs[1] << 8) | (synth.regs[0]);
synth.p2 = (synth.regs[5] << 22) | (synth.regs[4] << 14) |
(synth.regs[3] << 6) | ((synth.regs[2] >> 2) & 0x3f);
synth.p3 = ((synth.regs[9] & 0x3f) << 24) | (synth.regs[8] << 16) |
(synth.regs[7] << 8) | (synth.regs[6]);
// c = p3
synth.c = synth.p3;
// a = (p1+512)/128
// NOTE: The +64 is for rounding purposes.
synth.a = (synth.p1 + 512) / 128;
// b = (((p1+512)-128*a)*c + (b % c) + 64)/128
uint64_t tmp = (synth.p1 + 512) - 128 * (uint64_t)synth.a;
tmp = (tmp * synth.c) + synth.p2;
tmp = (tmp + 64) / 128;
synth.b = (uint32_t)tmp;
}
unsigned int BrfLibUsbDevice::setStatus(const BrfDevStatus& stat, unsigned int flags,
String* error)
{
unsigned int status = 0;
BRF_FUNC_CALL_RET(cancelled(error));
unsigned int tmp = 0;
#define SET_STATUS_FUNC(flag,func) \
if ((flags & flag) != 0) { \
tmp = func; \
if (tmp && !status) { \
error = 0; \
status = tmp; \
} \
}
XDebug(m_owner,DebugAll,"Set %s status 0x%x [%p]",brfDir(stat.tx()),flags,m_owner);
SET_STATUS_FUNC(DevStatLpfBw,internalSetLpfBandwidth(stat.tx(),stat.lpfBw,error));
SET_STATUS_FUNC(DevStatSampleRate,internalSetSampleRate(stat.tx(),stat.sampleRate,error));
SET_STATUS_FUNC(DevStatFreq,internalSetFrequency(stat.tx(),stat.frequency,error));
SET_STATUS_FUNC(DevStatVga1,internalSetVga(stat.tx(),stat.vga1,true,error));
SET_STATUS_FUNC(DevStatVga2,internalSetVga(stat.tx(),stat.vga2,false,error));
SET_STATUS_FUNC(DevStatLpf,internalSetLpf(stat.tx(),stat.lpf,error));
SET_STATUS_FUNC(DevStatDcI,internalSetDcOffset(stat.tx(),true,stat.dcOffsetI,error));
SET_STATUS_FUNC(DevStatDcQ,internalSetDcOffset(stat.tx(),false,stat.dcOffsetQ,error));
return status;
}
void BrfLibUsbDevice::internalDumpDev(String& buf, bool info, bool state,
const char* sep, bool internal, bool fromStatus, bool withHdr)
{
String tmp;
if (state) {
if (withHdr) {
buf.append("RxVGA1=",sep) << m_rxIO.vga1;
buf << sep << "RxVGA2=" << m_rxIO.vga2;
buf << sep << "RxDCCorrI=" << m_rxIO.dcOffsetI;
buf << sep << "RxDCCorrQ=" << m_rxIO.dcOffsetQ;
buf << sep << "TxVGA1=" << m_txIO.vga1;
buf << sep << "TxVGA2=" << m_txIO.vga2;
buf << sep << dumpFloatG(tmp,(double)m_rxIO.frequency / 1000000,"RxFreq=","MHz");
if (internal) {
buf << sep << "TxDCCorrI=" << m_txIO.dcOffsetI;
buf << sep << "TxDCCorrQ=" << m_txIO.dcOffsetQ;
}
buf << sep << dumpFloatG(tmp,(double)m_txIO.frequency / 1000000,"TxFreq=","MHz");
buf << sep << "FreqOffset=" << m_freqOffset;
buf << sep << "RxSampRate=" << m_rxIO.sampleRate;
buf << sep << "TxSampRate=" << m_txIO.sampleRate;
buf << sep << "RxLpfBw=" << m_rxIO.lpfBw;
buf << sep << "TxLpfBw=" << m_txIO.lpfBw;
buf << sep << "RxRF=" << onStr(m_rxIO.rfEnabled);
buf << sep << "TxRF=" << onStr(m_txIO.rfEnabled);
if (internal) {
buf << sep << "RxLPF=" << lookup(m_rxIO.lpf,s_lpf);
buf << sep << "TxLPF=" << lookup(m_txIO.lpf,s_lpf);
}
}
else {
buf << "|" << m_rxIO.vga1;
buf << "|" << m_rxIO.vga2;
buf << "|" << m_rxIO.dcOffsetI;
buf << "|" << m_rxIO.dcOffsetQ;
buf << "|" << m_txIO.vga1;
buf << "|" << m_txIO.vga2;
buf << "|" << dumpFloatG(tmp,(double)m_rxIO.frequency / 1000000,0,"MHz");
buf << "|" << dumpFloatG(tmp,(double)m_txIO.frequency / 1000000,0,"MHz");
buf << "|" << m_freqOffset;
buf << "|" << m_rxIO.sampleRate;
buf << "|" << m_txIO.sampleRate;
buf << "|" << m_rxIO.lpfBw;
buf << "|" << m_txIO.lpfBw;
buf << "|" << onStr(m_rxIO.rfEnabled);
buf << "|" << onStr(m_txIO.rfEnabled);
}
}
if (!info)
return;
if (withHdr) {
buf.append("Address=",sep) << "USB/" << bus() << "/" << addr();
buf << sep << "Serial=" << serial();
buf << sep << "Speed=" << speedStr(speed());
buf << sep << "Firmware=" << fwVerStr();
buf << sep << "FPGA=" << fpgaVerStr();
if (!fromStatus)
buf.append(fpgaFile()," - ");
}
else {
if (buf)
buf << "|";
buf << "USB/" << bus() << "/" << addr();
buf << "|" << serial();
buf << "|" << speedStr(speed());
buf << "|" << fwVerStr();
buf << "|" << fpgaVerStr();
}
}
unsigned int BrfLibUsbDevice::internalPowerOn(bool rfLink, bool tx, bool rx, String* error)
{
String e;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this);
if (rfLink)
status = lusbSetAltInterface(BRF_ALTSET_RF_LINK,&e);
else
status = tmpAltSet.set(&e,"Power ON/OFF");
bool warn = (tx != m_txIO.rfEnabled) || (rx != m_rxIO.rfEnabled);
while (status == 0) {
if (tx || rx)
BRF_FUNC_CALL_BREAK(enableTimestamps(true,&e));
BRF_FUNC_CALL_BREAK(enableRf(true,tx,false,&e));
BRF_FUNC_CALL_BREAK(enableRf(false,rx,false,&e))
if (tx || rx) {
String extra;
if (!(tx && rx))
extra << ", " << brfDir(tx) << " only";
Debug(m_owner,DebugNote,"Powered ON the radio%s [%p]",extra.safe(),m_owner);
}
else if (warn)
Debug(m_owner,DebugNote,"Powered OFF the radio [%p]",m_owner);
return 0;
}
if (!warn)
return 0;
e.printf(1024,"Power %s failed: %s",((tx || rx) ? "ON" :"OFF"),e.c_str());
return showError(status,e,0,error);
}
// Send an array of samples waiting to be transmitted
unsigned int BrfLibUsbDevice::send(uint64_t ts, float* data, unsigned int samples,
float* powerScale)
{
#ifndef DEBUG_DEVICE_TX
XDebug(m_owner,DebugAll,"send(" FMT64U ",%p,%u) [%p]",ts,data,samples,m_owner);
#endif
BrfDevIO& io = m_txIO;
if (!io.startTime)
io.startTime = Time::now();
if (!(data && samples))
return 0;
#ifdef DEBUG_DEVICE_TX
Debugger debug(DebugInfo,"BrfLibUsbDevice::send()",
" %s: ts=" FMT64U " (expected=" FMT64U ") samples=%u [%p]",
m_owner->debugName(),ts,io.timestamp,samples,m_owner);
#endif
// Check timestamp
if (io.timestamp != ts) {
if (io.timestamp) {
int level = DebugInfo;
String s;
s << "(our=" << io.timestamp << " requested=" << ts << ")";
if (io.crtBuf || io.crtBufSampOffs) {
s << ", dropping previous data";
level = DebugNote;
}
Debug(m_owner,level,"TX: timestamps don't match %s [%p]",s.c_str(),m_owner);
}
io.resetBufPos();
io.timestamp = ts;
}
float scale = 0;
float* scaleI = &m_wrPowerScaleI;
float* scaleQ = &m_wrPowerScaleQ;
int16_t* maxI = &m_wrMaxI;
int16_t* maxQ = &m_wrMaxQ;
if (m_txPowerBalanceChanged) {
m_txPowerBalanceChanged = false;
m_wrPowerScaleI = m_txPowerScaleI * s_sampleEnergize;
m_wrPowerScaleQ = m_txPowerScaleQ * s_sampleEnergize;
m_wrMaxI = sampleScale(m_txPowerScaleI,s_sampleEnergize);
m_wrMaxQ = sampleScale(m_txPowerScaleQ,s_sampleEnergize);
Debug(m_owner,DebugInfo,"TX using power scale I=%g Q=%g maxI=%d maxQ=%d [%p]",
m_wrPowerScaleI,m_wrPowerScaleQ,m_wrMaxI,m_wrMaxQ,m_owner);
}
if (powerScale && m_wrPowerScaleI == s_sampleEnergize) {
scale = *powerScale * s_sampleEnergize;
scaleI = scaleQ = &scale;
maxI = maxQ = &s_sampleEnergize;
}
if (m_txPatternChanged)
sendTxPatternChanged();
unsigned int clamped = 0;
String e;
unsigned int status = lusbSetAltInterface(BRF_ALTSET_RF_LINK,&e);
while (!status) {
while (samples && io.crtBuf < io.buffers) {
unsigned int avail = 0;
int16_t* start = io.crtBufSamples(avail);
if (avail > samples)
avail = samples;
// New buffer: set the timestamp
if (!io.crtBufSampOffs)
io.setBufTs(io.crtBuf,io.timestamp);
samples -= avail;
#ifdef DEBUG_DEVICE_TX
Debugger loopDbg(DebugAll,"TX: processing buffer",
" %u/%u ts=" FMT64U " avail=%u remains=%u [%p]",
io.crtBuf + 1,io.buffers,io.timestamp,avail,samples,m_owner);
#endif
io.crtBufSampOffs += avail;
io.timestamp += avail;
if (m_txPatternBufSamples == 0) {
brfCopyTxData(start,data,avail,*scaleI,*maxI,*scaleQ,*maxQ,clamped);
data += avail * 2;
}
else
sendCopyTxPattern(start,avail,*scaleI,*maxI,*scaleQ,*maxQ,clamped);
if (io.crtBufSampOffs >= io.bufSamples)
io.advanceBuffer();
}
unsigned int nBuf = io.crtBuf;
unsigned int oldBufSampOffs = 0;
if (nBuf && io.crtBufSampOffs) {
if (io.crtBufSampOffs == 0)
nBuf++;
else
oldBufSampOffs = io.crtBufSampOffs;
}
if (io.syncTsRequested) {
if (m_txISet >= BRF_TX_DC_OFFSET_MIN) {
BRF_FUNC_CALL_BREAK(internalSetDcOffset(true,true,m_txISet));
m_txISet = BRF_TX_DC_OFFSET_MIN - 1;
}
if (m_txQSet >= BRF_TX_DC_OFFSET_MIN) {
BRF_FUNC_CALL_BREAK(internalSetDcOffset(true,false,m_txQSet));
m_txQSet = BRF_TX_DC_OFFSET_MIN - 1;
}
io.syncTs = ts + nBuf * io.bufSamples;
io.syncTsRequested = false;
}
if (!nBuf)
break;
if (checkDbgInt(io.checkTs))
ioBufCheckTs(true,nBuf);
#ifdef DEBUG_DEVICE_TX
String tmp;
for (unsigned int i = 0; i < nBuf; i++)
tmp.append(String(io.bufTs(i)),"/");
Debugger* sendDbg = new Debugger(DebugAll,"TX: writing to device",
" buffers=%u ts=%s [%p]",nBuf,tmp.c_str(),m_owner);
#endif
unsigned int nPrint = checkDbgInt(io.showBuf,nBuf);
if (nPrint)
printIOBuffer(true,"SEND",-1,nPrint);
status = syncTransfer(EpSendSamples,io.bufStart(0),io.bufLen * nBuf,&e);
#ifdef DEBUG_DEVICE_TX
delete sendDbg;
#endif
io.transferred += nBuf * io.bufSamples;
// Reset buffer
io.resetBufPos();
// Copy partial buffer from end
if (oldBufSampOffs) {
#ifdef DEBUG_DEVICE_TX
Debug(DebugAll,"TX: copying buffer %u to start [%p]",nBuf,m_owner);
#endif
::memcpy(io.bufStart(0),io.bufStart(nBuf),io.hdrLen + samplesi2bytes(oldBufSampOffs));
io.crtBufSampOffs = oldBufSampOffs;
}
}
if (status == 0) {
if (clamped)
Debug(m_owner,DebugNote,"Output buffer clamped %u times [%p]",
clamped,m_owner);
if (samples)
Debug(DebugFail,"Exiting with non 0 samples");
}
else if (status != RadioInterface::Cancelled)
Debug(m_owner,DebugNote,"Send failed (TS=" FMT64U "): %s [%p]",
io.timestamp,e.c_str(),m_owner);
return status;
}
void BrfLibUsbDevice::sendTxPatternChanged()
{
Lock lck(m_dbgMutex);
if (!m_txPatternChanged)
return;
m_txPatternChanged = false;
m_txPatternBuffer.clear();
m_txPatternBufSamples = bytes2samplesf(m_txPattern.length());
if (m_txPatternBufSamples) {
unsigned int bufs = (m_txIO.bufSamples + m_txPatternBufSamples - 1) /
m_txPatternBufSamples;
unsigned int n = samplesf2bytes(m_txPatternBufSamples);
while (bufs--)
m_txPatternBuffer.append(m_txPattern.data(0),n);
m_txPatternBufSamples = bytes2samplesf(m_txPatternBuffer.length());
Debug(m_owner,DebugNote,"Using send pattern %u samples [%p]",
m_txPatternBufSamples,m_owner);
}
else
m_txPatternBuffer.clear();
m_txPatternBufPos = m_txPatternBufSamples;
}
void BrfLibUsbDevice::sendCopyTxPattern(int16_t* buf, unsigned int avail,
float scaleI, int16_t maxI, float scaleQ, int16_t maxQ, unsigned int& clamped)
{
while (avail) {
if (m_txPatternBufPos == m_txPatternBufSamples)
m_txPatternBufPos = 0;
unsigned int cp = m_txPatternBufSamples - m_txPatternBufPos;
if (cp > avail)
cp = avail;
float* b = (float*)m_txPatternBuffer.data(0) + m_txPatternBufPos * 2;
avail -= cp;
m_txPatternBufPos += cp;
brfCopyTxData(buf,b,cp,scaleI,maxI,scaleQ,maxQ,clamped);
}
}
// Receive data from the Rx interface of the bladeRF device
// Remember: a sample is an I/Q pair
unsigned int BrfLibUsbDevice::recv(uint64_t& ts, float* data, unsigned int& samples)
{
#ifndef DEBUG_DEVICE_RX
XDebug(m_owner,DebugAll,"recv(" FMT64U ",%p,%u) [%p]",ts,data,samples,m_owner);
#endif
BrfDevIO& io = m_rxIO;
if (!io.startTime)
io.startTime = Time::now();
if (!(data && samples))
return 0;
#ifdef DEBUG_DEVICE_RX
Debugger debug(DebugInfo,"BrfLibUsbDevice::recv()",
" %s: ts=" FMT64U " samples=%u data=(%p) [%p]",
m_owner->debugName(),ts,samples,data,m_owner);
#endif
unsigned int samplesCopied = 0;
unsigned int samplesLeft = samples;
// Don't warn if requested timestamp is in the future
bool warnTsPast = ts && (ts <= io.timestamp);
uint64_t crtTs = ts;
String e;
unsigned int status = lusbSetAltInterface(BRF_ALTSET_RF_LINK,&e);
while (!status) {
bool done = false;
while (samplesLeft && io.crtBuf < io.buffers) {
// Retrieve buffer timestamp
uint64_t bufTs = io.bufTs(io.crtBuf);
if (io.crtBufSampOffs)
bufTs += io.crtBufSampOffs;
#ifdef DEBUG_DEVICE_RX
Debugger loopDbg(DebugAll,"RX: processing buffer",
" %u/%u ts=" FMT64U " crt_ts=" FMT64U " [%p]",
io.crtBuf + 1,io.buffers,bufTs,crtTs,m_owner);
#endif
// Check timestamp
if (bufTs > crtTs) {
// Buffer timestamp is in the future
// Already set valid data: stop and return it, don't advance the buffer
if (samplesCopied) {
done = true;
break;
}
// No data set: continue from this timestamp
if (crtTs)
Debug(m_owner,DebugNote,
"RX: reset timestamp in future in buffer %u/%u requested="
FMT64U " found=" FMT64U " [%p]",
io.crtBuf + 1,io.buffers,crtTs,bufTs,m_owner);
ts = crtTs = bufTs;
}
else if (bufTs < crtTs) {
// Timestamp in the past: check if can use some data, skip buffer
unsigned int avail = 0;
io.crtBufSamples(avail);
unsigned int skipSamples = avail;
uint64_t diff = crtTs - bufTs;
if (diff < skipSamples)
skipSamples = diff;
if (warnTsPast)
Debug(m_owner,DebugNote,
"RX: skipping %u/%u samples in buffer %u/%u:"
" timestamp in the past by " FMT64U " [%p]",
skipSamples,avail,io.crtBuf + 1,io.buffers,crtTs - bufTs,m_owner);
io.crtBufSampOffs += skipSamples;
if (io.crtBufSampOffs >= io.bufSamples) {
io.advanceBuffer();
continue;
}
}
warnTsPast = true;
unsigned int avail = 0;
int16_t* start = io.crtBufSamples(avail);
if (avail > samplesLeft)
avail = samplesLeft;
int16_t* last = start + avail * 2;
#ifdef DEBUG_DEVICE_RX
String s;
//io.dumpInt16Samples(s,io.crtBuf,io.crtBufSampOffs,avail);
//s = "\r\n-----\r\n" + s + "\r\n-----";
Debug(m_owner,DebugAll,"RX: buf %u/%u preparing to copy %u samples data=(%p) (end=(%p)) "
"start=(%p) last=(%p) [%p]%s",io.crtBuf + 1,io.buffers,avail,
data,data + avail,start,last,m_owner,s.safe());
#endif
// Copy data
while (start != last) {
*data++ = ((float)*start++) / 2048;
*data++ = ((float)*start++) / 2048;
}
samplesCopied += avail;
samplesLeft -= avail;
#ifdef DEBUG_DEVICE_RX
Debug(m_owner,DebugAll,
"RX: copied %u samples from buffer %u/%u status=%u/%u"
" remains=%u data=(%p) start=(%p) [%p]",
avail,io.crtBuf + 1,io.buffers,samplesCopied,samples,samplesLeft,
data,start,m_owner);
#endif
// Advance buffer offset, advance the buffer if we used all data
io.crtBufSampOffs += avail;
if (io.crtBufSampOffs >= io.bufSamples) {
io.advanceBuffer();
crtTs += avail;
}
}
if (done || !samplesLeft)
break;
status = syncTransfer(EpReadSamples,io.bufStart(0),io.buffer.length(),&e);
if (status)
break;
#ifdef DEBUG_DEVICE_RX
Debug(m_owner,DebugAll,"RX: read %u bytes from device [%p]",
io.buffer.length(),m_owner);
#endif
io.transferred += io.buffers * io.bufSamples;
io.fixEndian();
io.resetBufPos();
unsigned int nPrint = checkDbgInt(io.showBuf,io.buffers);
if (nPrint)
printIOBuffer(false,"RECV",-1,nPrint);
if (checkDbgInt(io.checkTs))
ioBufCheckTs(false);
if (m_rxDcAuto || m_rxShowDcInfo)
computeRx(crtTs);
if (m_rxAlterData)
rxAlterData();
}
samples = samplesCopied;
#ifdef DEBUG_DEVICE_RX
Debug(m_owner,DebugAll,
"BrfLibUsbDevice::recv() exiting status=%u ts=" FMT64U " samples=%u [%p]",
status,ts,samples,m_owner);
#endif
if (status == 0)
m_rxIO.timestamp = ts;
else if (status != RadioInterface::Cancelled)
Debug(m_owner,DebugNote,"Recv failed: %s [%p]",e.c_str(),m_owner);
return status;
}
unsigned int BrfLibUsbDevice::internalSetSampleRate(bool tx, uint32_t value,
String* error)
{
String e;
unsigned int status = lusbSetAltInterface(BRF_ALTSET_RF_LINK,&e);
while (!status) {
Si5338MultiSynth synth;
BrfRationalRate rate;
rate.integer = value;
rate.numerator = 0; // keeping the numerator and the donominator
rate.denominator = 1; // for future use
// Enforce minimum sample rate
reduceRational(rate);
if (rate.integer < BRF_SAMPLERATE_MIN)
Debug(m_owner,DebugGoOn,
"Requested sample rate is smaller than the allowed minimum value [%p]",
m_owner);
// Setup the multisynth enables and index
synth.enable = 0x01;
synth.index = 1;
if (tx) {
synth.enable |= 0x02;
synth.index = 2;
}
// Update the base address register
synth.base = 53 + synth.index * 11;
// Calculate multisynth values
BRF_FUNC_CALL_BREAK(calcMultiSynth(synth,rate,&e));
// Program it to the part
// Write out the enables
uint8_t val = 0;
BRF_FUNC_CALL_BREAK(getSi5338(36 + synth.index,val,&e));
val &= ~(7);
val |= synth.enable;
BRF_FUNC_CALL_BREAK(setSi5338(36 + synth.index,val,&e));
// Write out the registers
for (int i = 0 ; i < 10; i++)
BRF_FUNC_CALL_BREAK(setSi5338(synth.base + i,*(synth.regs + i),&e));
if (status)
break;
// Calculate r_power from c_count
uint8_t rPower = 0;
uint8_t rCount = synth.r >> 1;
while (rCount) {
rCount >>= 1;
rPower++;
}
// Set the r value to the log2(r_count) to match Figure 18
val = 0xc0;
val |= (rPower<<2);
BRF_FUNC_CALL_BREAK(setSi5338(31 + synth.index,val,&e));
if (getIO(tx).sampleRate != value) {
getIO(tx).sampleRate = value;
Debug(m_owner,DebugInfo,"%s samplerate set to %u [%p]",
brfDir(tx),value,m_owner);
}
return 0;
}
e.printf(1024,"Failed to set %s samplerate %u: %s",brfDir(tx),value,e.c_str());
return showError(status,e,0,error);
}
// Update FPGA (load, get version)
unsigned int BrfLibUsbDevice::updateFpga(const NamedList& params)
{
const String& oper = params[YSTRING("fpga_load")];
int load = 0;
if (!oper)
load = 1;
else if (oper == YSTRING("auto")) {
unsigned int code = checkFpga();
if (code == RadioInterface::NoError)
load = -1;
else {
load = 1;
if (code != RadioInterface::NotInitialized)
Debug(m_owner,DebugNote,"Forcing FPGA load (check failure) [%p]",m_owner);
}
}
else
load = oper.toBoolean(true) ? 1 : 0;
if (load > 0)
Debug(m_owner,DebugAll,"Updating FPGA [%p]",m_owner);
else
Debug(m_owner,DebugInfo,"Skipping FPGA load: %s [%p]",
(load ? "checked, already loaded" : "disabled by config"),m_owner);
m_devFpgaFile.clear();
m_devFpgaVerStr.clear();
String e;
unsigned int status = 0;
while (load > 0) {
m_devFpgaFile.clear();
String val;
status = getCalField(val,"B","FPGA size",&e);
if (status)
break;
String fName;
if (val == YSTRING("115") || val == YSTRING("40"))
fName = params.getValue("fpga_file_" + val,
"${modulepath}/server/bts/hostedx" + val + ".rbf");
else {
e << "Unknown FPGA size value '" << val << "'";
status = RadioInterface::Failure;
break;
}
Engine::runParams().replaceParams(fName);
const char* oper = 0;
// Read the FPGA contents
File f;
DataBlock buf;
if (f.openPath(fName,false,true)) {
int64_t len = f.length();
if (len > 0) {
buf.assign(0,len);
int rd = f.readData(buf.data(),buf.length());
if (rd != len)
oper = "read";
}
else if (f.error())
oper = "detect length";
}
else
oper = "open";
if (oper) {
status = RadioInterface::Failure;
String tmp;
Thread::errorString(tmp,f.error());
e << "File '" << fName << "' " << oper << " failed (" <<
f.error() << " '" << tmp << "')";
break;
}
Debug(m_owner,DebugAll,"Loading FPGA from '%s' len=%u [%p]",
fName.c_str(),buf.length(),m_owner);
// Write the FPGA
BrfDevTmpAltSet tmpAltSet(this,BRF_ALTSET_FPGA,status,&e,"FPGA load");
if (status)
break;
status = vendorCommand0_4(BRF_USB_CMD_BEGIN_PROG,LIBUSB_ENDPOINT_IN,&e);
if (status == 0) {
status = lusbBulkTransfer(BRF_ENDP_TX_CTRL,buf.data(0),buf.length(),
0,&e,3 * m_bulkTout);
if (status == 0) {
status = vendorCommand0_4(BRF_USB_CMD_QUERY_FPGA_STATUS,
LIBUSB_ENDPOINT_IN,&e);
if (status)
e = "Failed to end FPGA programming - " + e;
}
else
e = "Failed to send FPGA image - " + e;
}
else
e = "Failed to start FPGA programming - " + e;
tmpAltSet.restore();
status = restoreAfterFpgaLoad(&e);
if (status == 0) {
m_devFpgaFile = fName;
Debug(m_owner,DebugAll,"Loaded FPGA from '%s' [%p]",fName.c_str(),m_owner);
}
break;
}
if (status == 0) {
uint32_t ver = 0;
if (getFpgaVersion(ver) == 0)
ver2str(m_devFpgaVerStr,ver);
}
else
Debug(m_owner,DebugWarn,"Failed to load FPGA: %s [%p]",e.c_str(),m_owner);
return status;
}
unsigned int BrfLibUsbDevice::internalSetFpgaCorr(bool tx, int corr, int16_t value,
String* error, int lvl)
{
XDebug(m_owner,DebugAll,"internalSetFpgaCorr(%u,%d,%d) [%p]",tx,corr,value,m_owner);
String e;
unsigned int status = 0;
int orig = value;
String what;
int a = fpgaCorrAddr(tx,corr,what);
int* changed = 0;
if (a >= 0) {
if (corr == CorrFpgaGAIN) {
changed = &getIO(tx).fpgaCorrGain;
orig = clampInt(orig,-BRF_FPGA_CORR_MAX,BRF_FPGA_CORR_MAX,"FPGA GAIN",lvl);
value = orig + BRF_FPGA_CORR_MAX;
}
else if (corr == CorrFpgaPHASE) {
changed = &getIO(tx).fpgaCorrPhase;
orig = clampInt(orig,-BRF_FPGA_CORR_MAX,BRF_FPGA_CORR_MAX,"FPGA PHASE",lvl);
value = orig;
}
status = gpioWrite(a,value,2,&e);
}
else
status = setUnkValue(e,0,"FPGA corr value " + String(corr));
if (status) {
e.printf(1024,"Failed to set %s FPGA corr %s to %d (from %d) - %s [%p]",
brfDir(tx),what.c_str(),value,orig,e.c_str(),m_owner);
return showError(status,e,0,error);
}
if (changed && *changed != orig)
Debug(m_owner,DebugInfo,"%s FPGA corr %s set to %d (from %d) [%p]",
brfDir(tx),what.c_str(),value,orig,m_owner);
return 0;
}
unsigned int BrfLibUsbDevice::internalGetFpgaCorr(bool tx, int corr, int16_t* value,
String* error)
{
int16_t v = 0;
String e;
unsigned int status = 0;
String what;
int a = fpgaCorrAddr(tx,corr,what);
if (a >= 0) {
uint32_t u = 0;
status = gpioRead(a,u,2,&e);
if (status == 0) {
v = (int)u;
if (corr == CorrFpgaGAIN)
v -= BRF_FPGA_CORR_MAX;
if (value)
*value = v;
}
}
else
status = setUnkValue(e,0,"FPGA corr value " + String(corr));
if (status) {
e.printf(1024,"Failed to retrieve %s FPGA corr %s - %s [%p]",
brfDir(tx),what.c_str(),e.c_str(),m_owner);
return showError(status,e,0,error);
}
XDebug(m_owner,DebugAll,"Got %s FPGA corr %s %d [%p]",
brfDir(tx),what.c_str(),v,m_owner);
if (corr == CorrFpgaGAIN)
getIO(tx).fpgaCorrGain = v;
else if (corr == CorrFpgaPHASE)
getIO(tx).fpgaCorrPhase = v;
return 0;
}
unsigned int BrfLibUsbDevice::internalSetTxVga(int vga, bool preMixer, String* error)
{
String e;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"TX VGA set");
while (status == 0) {
uint8_t addr = lmsVgaAddr(true,preMixer);
uint8_t data = 0;
BRF_FUNC_CALL_BREAK(lmsRead(addr,data,&e));
if (preMixer) {
vga = clampInt(vga,BRF_TXVGA1_GAIN_MIN,BRF_TXVGA1_GAIN_MAX,"TX VGA1");
data = (uint8_t)((vga - BRF_TXVGA1_GAIN_MIN) & 0x1f);
}
else {
vga = clampInt(vga,BRF_TXVGA2_GAIN_MIN,BRF_TXVGA2_GAIN_MAX,"TX VGA2");
data &= ~0xf8;
data |= (uint8_t)(vga << 3);
}
BRF_FUNC_CALL_BREAK(lmsWrite(addr,data,&e));
int& old = preMixer ? m_txIO.vga1 : m_txIO.vga2;
if (old != vga) {
old = vga;
Debug(m_owner,DebugInfo,"TX VGA%c set to %ddB (0x%x) [%p]",
mixer(preMixer),vga,data,m_owner);
if (!preMixer)
internalSetTxIQBalance(true);
}
return 0;
}
e.printf(1024,"Failed to set TX VGA%c to from %d: %s",mixer(preMixer),vga,e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::internalGetTxVga(int* vga, bool preMixer, String* error)
{
uint8_t data = 0;
String e;
int v = 0;
unsigned int status = lmsRead(lmsVgaAddr(true,preMixer),data,&e);
if (status == 0) {
if (preMixer) {
v = (int)(data & 0x1f) + BRF_TXVGA1_GAIN_MIN;
m_txIO.vga1 = v;
}
else {
v = (data >> 3) & 0x1f;
if (v > BRF_TXVGA2_GAIN_MAX)
v = BRF_TXVGA2_GAIN_MAX;
m_txIO.vga2 = v;
}
if (vga)
*vga = v;
XDebug(m_owner,DebugAll,"Got TX VGA%c %ddB (0x%x) [%p]",
mixer(preMixer),v,data,m_owner);
return 0;
}
e.printf(1024,"Failed to retrieve TX VGA%c: %s",mixer(preMixer),e.c_str());
return showError(status,e,0,error);
}
// Enable or disable the pre/post mixer gain on the receive side
unsigned int BrfLibUsbDevice::internalEnableRxVga(bool on, bool preMixer, String* error)
{
XDebug(m_owner,DebugAll,"internalEnableRxVga(%u,%u) [%p]",on,preMixer,m_owner);
String e;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"Enable RX VGA");
while (status == 0) {
uint8_t addr = preMixer ? 0x7d : 0x64;
uint8_t data = 0;
BRF_FUNC_CALL_BREAK(lmsRead(addr,data,&e));
bool old = false;
if (preMixer) {
old = (data & 0x08) == 0;
if (on)
data &= ~0x08;
else
data |= 0x08;
}
else {
old = (data & 0x02) == 1;
if (on)
data |= 0x02;
else
data &= ~0x02;
}
BRF_FUNC_CALL_BREAK(lmsWrite(addr,data,&e));
if (old != on)
Debug(m_owner,DebugInfo,"%s RX VGA%c [%p]",
enabledStr(on),mixer(preMixer),m_owner);
return 0;
}
e.printf(1024,"Failed to %s RX VGA%c: %s",enableStr(on),mixer(preMixer),e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::internalSetRxVga(int vga, bool preMixer, String* error)
{
String e;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"RX VGA set");
while (status == 0) {
uint8_t addr = lmsVgaAddr(false,preMixer);
uint8_t data = 0;
BRF_FUNC_CALL_BREAK(lmsRead(addr,data,&e));
int orig = vga;
bool changed = false;
if (preMixer) {
vga = clampInt(vga,BRF_RXVGA1_GAIN_MIN,BRF_RXVGA1_GAIN_MAX,"RX VGA1");
data = (uint8_t)((data & ~0x7f) | s_rxvga1_set[vga]);
BRF_FUNC_CALL_BREAK(lmsWrite(addr,data,&e));
changed = (m_rxIO.vga1 != vga);
m_rxIO.vga1 = vga;
}
else {
vga = clampInt(vga / 3 * 3,BRF_RXVGA2_GAIN_MIN,BRF_RXVGA2_GAIN_MAX,"RX VGA2");
data = (uint8_t)((data & ~0x1f) | (vga / 3));
BRF_FUNC_CALL_BREAK(lmsWrite(addr,data,&e));
changed = (m_rxIO.vga2 != vga);
m_rxIO.vga2 = vga;
m_rxDcOffsetMax = (int)brfRxDcOffset(clampInt(orig,BRF_RXVGA2_GAIN_MIN,BRF_RXVGA2_GAIN_MAX));
}
if (changed)
Debug(m_owner,DebugInfo,"RX VGA%c set to %ddB 0x%x (from %d) [%p]",
mixer(preMixer),vga,data,orig,m_owner);
return 0;
}
e.printf(1024,"Failed to set RX VGA%c to %d: %s",mixer(preMixer),vga,e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::internalGetRxVga(int* vga, bool preMixer, String* error)
{
uint8_t data = 0;
String e;
unsigned int status = lmsRead(lmsVgaAddr(false,preMixer),data,&e);
if (status == 0) {
int v = 0;
if (preMixer) {
int idx = (data & 0x7f);
m_rxIO.vga1 = v = s_rxvga1_get[idx < 121 ? idx : 120];
}
else {
m_rxIO.vga2 = v = (data & 0x1f) * 3;
m_rxDcOffsetMax = (int)brfRxDcOffset(clampInt(v,BRF_RXVGA2_GAIN_MIN,BRF_RXVGA2_GAIN_MAX));
}
XDebug(m_owner,DebugAll,"Got RX VGA%c %ddB (reg=0x%x) [%p]",
mixer(preMixer),v,data,m_owner);
if (vga)
*vga = v;
return 0;
}
e.printf(1024,"Failed to retrieve RX VGA%c: %s",mixer(preMixer),e.c_str());
return showError(status,e,0,error);
}
// Set pre and post mixer value
unsigned int BrfLibUsbDevice::internalSetGain(bool tx, int val, int* newVal, String* error)
{
int vga1 = 0;
if (tx) {
vga1 = (m_txIO.vga1 >= BRF_TXVGA1_GAIN_MIN) ? m_txIO.vga1 : BRF_TXVGA1_GAIN_DEF;
val = clampInt(val + BRF_TXVGA2_GAIN_MAX,BRF_TXVGA2_GAIN_MIN,BRF_TXVGA2_GAIN_MAX);
}
else {
vga1 = m_rxIO.vga1 > BRF_RXVGA1_GAIN_MAX ? BRF_RXVGA1_GAIN_MAX : m_rxIO.vga1;
val = clampInt(val,BRF_RXVGA2_GAIN_MIN,BRF_RXVGA2_GAIN_MAX);
}
unsigned int status = internalSetVga(tx,vga1,true,error);
if (!status)
status = internalSetVga(tx,val,false,error);
if (status == 0 && newVal) {
*newVal = val;
if (tx)
*newVal -= BRF_TXVGA2_GAIN_MAX;
}
return status;
}
unsigned int BrfLibUsbDevice::internalSetTxIQBalance(bool newGain, float newBalance,
const char* param)
{
if (!newGain) {
if (newBalance <= 0 || newBalance >= 2) {
if (!param) {
Debug(m_owner,DebugNote,
"Failed to set power balance to %g expected interval (0..2) [%p]",
newBalance,m_owner);
return RadioInterface::OutOfRange;
}
Debug(m_owner,DebugConf,"Invalid %s=%g defaults to 1 [%p]",
param,newBalance,m_owner);
newBalance = 1;
}
if (m_txPowerBalance != newBalance) {
Debug(m_owner,DebugInfo,"TX power balance changed %g -> %g [%p]",
m_txPowerBalance,newBalance,m_owner);
m_txPowerBalance = newBalance;
}
}
float oldI = m_txPowerScaleI;
float oldQ = m_txPowerScaleQ;
// Update TX power scale
float tmp = ::powf(10.0,0.1 * getIO(true).vga2);
tmp = tmp < 1.0 ? 1.0 : (1.0 / ::sqrt(tmp));
m_txPowerScaleI = m_txPowerScaleQ = tmp;
if (m_txPowerBalance > 1)
m_txPowerScaleQ /= m_txPowerBalance;
else if (m_txPowerBalance < 1)
m_txPowerScaleI *= m_txPowerBalance;
else
m_txPowerScaleI = m_txPowerScaleQ = 1;
if (oldI == m_txPowerScaleI && oldQ == m_txPowerScaleQ)
return 0;
Debug(m_owner,DebugInfo,"TX power scale changed I: %g -> %g Q: %g -> %g [%p]",
oldI,m_txPowerScaleI,oldQ,m_txPowerScaleQ,m_owner);
m_txPowerBalanceChanged = true;
return 0;
}
unsigned int BrfLibUsbDevice::internalSetFreqOffs(int val, int* newVal, String* error)
{
val = clampInt(val,BRF_FREQ_OFFS_MIN,BRF_FREQ_OFFS_MAX,"FrequencyOffset");
String e;
unsigned int status = gpioWrite(0x22,(val && 0xff) << 8,2,&e);
if (status == 0) {
if (m_freqOffset != val) {
Debug(m_owner,DebugInfo,"FrequencyOffset set to %d [%p]",val,m_owner);
m_freqOffset = val;
}
if (newVal)
*newVal = val;
return 0;
}
return showError(status,e,"FrequencyOffset set failed",error);
}
unsigned int BrfLibUsbDevice::internalSetFrequency(bool tx, uint32_t hz, String* error)
{
XDebug(m_owner,DebugAll,"BrfLibUsbDevice::setFrequency(%u,%s) [%p]",
hz,brfDir(tx),m_owner);
String e;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"frequency set");
while (!status) {
status = RadioInterface::Failure;
uint8_t addr = lmsFreqAddr(tx);
clampFrequency(hz,tx,"setFrequency");
uint8_t pllFreq = 0xff;
for (int i = 0; s_freqLimits[i]; i += 3)
if (hz >= s_freqLimits[i] && hz <= s_freqLimits[i + 1]) {
pllFreq = s_freqLimits[i + 2];
break;
}
if (pllFreq == 0xff) {
status = setUnkValue(e,"frequency " + String(hz));
break;
}
// Integer part
uint64_t vco_x = ((uint64_t)1) << ((pllFreq & 7) - 3);
uint64_t tmp = (vco_x * hz) / s_freqRefClock;
if (tmp > 0xffff) {
e.printf("The integer part " FMT64U " of the frequency is too big",tmp);
status = RadioInterface::Failure;
break;
}
uint16_t nint = (uint16_t)tmp;
// Fractional part
tmp = (1 << 23) * (vco_x * hz - nint * s_freqRefClock);
tmp = (tmp + s_freqRefClock / 2) / s_freqRefClock;
if (tmp > 0xffffffff) {
e.printf("The fractional part " FMT64U " of the frequency is too big",tmp);
status = RadioInterface::Failure;
break;
}
uint32_t nfrac = (uint32_t)tmp;
bool lowBand = brfIsLowBand(hz);
// Reset CLK_EN for Rx/Tx DSM SPI
BRF_FUNC_CALL_BREAK(lmsSet(0x09,0x05,&e));
// Set PLL frequency and output buffer selection
pllFreq <<= 2;
pllFreq |= (lowBand ? 0x01 : 0x02);
BRF_FUNC_CALL_BREAK(lmsWrite(addr + 5,pllFreq,&e));
// Set frequency NINT/NFRAC
uint8_t d[4] = {(uint8_t)(nint >> 1),
(uint8_t)(((nint & 1) << 7) | ((nfrac >> 16) & 0x7f)),
(uint8_t)(nfrac >> 8),(uint8_t)nfrac};
BRF_FUNC_CALL_BREAK(accessPeripheral(UartDevLMS,true,addr,d,&e,4));
// Set PLL currents (ICHP=1.2mA, OFFUP=0mA, OFFDOWN=0mA)
BRF_FUNC_CALL_BREAK(lmsSet(addr + 6,0x0c,0x1f,&e));
BRF_FUNC_CALL_BREAK(lmsSet(addr + 7,0x00,0x1f,&e));
BRF_FUNC_CALL_BREAK(lmsSet(addr + 8,0x00,0x1f,&e));
// Loop through the VCOCAP to figure out optimal values
BRF_FUNC_CALL_BREAK(tuneVcocap(addr,&e));
// Reset CLK_EN for Rx/Tx DSM SPI
BRF_FUNC_CALL_BREAK(lmsReset(0x09,0x05,&e));
// Select PA/LNA amplifier
BRF_FUNC_CALL_BREAK(selectPaLna(tx,lowBand,&e));
// Set GPIO band according to the frequency
uint32_t gpio = 0;
BRF_FUNC_CALL_BREAK(gpioRead(0,gpio,4,&e));
uint32_t band = lowBand ? 2 : 1;
int shift = tx ? 3 : 5;
gpio &= (uint32_t)~(3 << shift);
gpio |= (uint32_t)(band << shift);
BRF_FUNC_CALL_BREAK(gpioWrite(0,gpio,4,&e));
BRF_FUNC_CALL_BREAK(internalSetFreqOffs(m_freqOffset,0,&e));
break;
}
if (status) {
e.printf(1024,"Failed to set %s frequency to %uHz - %s",brfDir(tx),hz,e.c_str());
return showError(status,e,0,error);
}
if (getIO(tx).frequency != hz) {
getIO(tx).frequency = hz;
Debug(m_owner,DebugInfo,"%s frequency set to %gMHz offset=%u [%p]",
brfDir(tx),(double)hz / 1000000,m_freqOffset,m_owner);
}
return 0;
}
unsigned int BrfLibUsbDevice::internalGetFrequency(bool tx, uint32_t* hz, String* error)
{
String e;
unsigned int status = 0;
uint32_t freq = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,tx ? "TX frequency get" : "RX frequency get");
while (!status) {
uint8_t addr = lmsFreqAddr(tx);
uint8_t data = 0;
uint64_t fint = 0; // Integer part of the freq
// Reading the integer part of the frequency
BRF_FUNC_CALL_BREAK(lmsRead(addr + 0,data,&e));
fint = (uint64_t)data << 24;
BRF_FUNC_CALL_BREAK(lmsRead(addr + 1,data,&e));
fint |= (data & 0x80) << 16;
// Read the fractionary part of the frequency
fint |= ((uint64_t)data & 0x7f) << 16;
BRF_FUNC_CALL_BREAK(lmsRead(addr + 2,data,&e));
fint |= (uint64_t)data << 8;
BRF_FUNC_CALL_BREAK(lmsRead(addr + 3,data,&e));
fint |= data;
// read the freq division
BRF_FUNC_CALL_BREAK(lmsRead(addr + 5,data,&e));
uint32_t division = data >> 2; // VCO division ratio
division = 1 << ((division & 7) + 20);
freq = (uint32_t)(((s_freqRefClock * fint) + (division >> 1)) / division);
break;
}
if (status == 0) {
getIO(tx).frequency = freq;
if (hz)
*hz = freq;
XDebug(m_owner,DebugAll,"Got %s frequency %uHz [%p]",brfDir(tx),freq,m_owner);
return 0;
}
e.printf(1024,"Failed to retrieve %s frequency - %s",brfDir(tx),e.c_str());
return showError(status,e,0,error);
}
// Retrieve TX/RX timestamp
unsigned int BrfLibUsbDevice::internalGetTimestamp(bool tx, uint64_t& ts, String* error)
{
String e;
unsigned int status = 0;
while (true) {
uint32_t low = 0;
uint32_t high = 0;
uint8_t addr = tx ? 0x18 : 0x10;
BRF_FUNC_CALL_BREAK(gpioRead(addr,low,4,&e));
BRF_FUNC_CALL_BREAK(gpioRead(addr + 4,high,4,&e));
ts = ((uint64_t)high << 31) | (low >> 1);
XDebug(m_owner,DebugAll,"Got %s ts=" FMT64U " [%p]",brfDir(tx),ts,m_owner);
return 0;
}
e.printf(1024,"Failed to retrieve %s timestamp - %s",brfDir(tx),e.c_str());
return showError(status,e,0,error);
}
// USB peripheral operation method
unsigned int BrfLibUsbDevice::lusbSetAltInterface(int val, String* error)
{
if (m_altSetting == val)
return 0;
unsigned int status = 0;
String e;
if (val >= BRF_ALTSET_MIN && val <= BRF_ALTSET_MAX)
status = lusbCheckSuccess(
::libusb_set_interface_alt_setting(m_devHandle,0,val),&e);
else
status = setUnkValue(e);
if (status == 0) {
DDebug(m_owner,DebugAll,"Alt interface changed %s -> %s [%p]",
altSetName(m_altSetting),altSetName(val),m_owner);
m_altSetting = val;
return 0;
}
String prefix;
prefix << "Failed to change alt interface to ";
if (val >= BRF_ALTSET_MIN && val <= BRF_ALTSET_MAX)
prefix << altSetName(val);
else
prefix << val;
return showError(status,e,prefix,error);
}
// Wrapper for libusb_control_transfer
unsigned int BrfLibUsbDevice::lusbCtrlTransfer(uint8_t reqType, int8_t request,
uint16_t value, uint16_t index, uint8_t* data, uint16_t len, String* error,
unsigned int tout)
{
#ifdef DEBUGGER_DEVICE_METH
String tmp;
//tmp.hexify(data,len,' ');
//tmp = " data=" + tmp;
Debugger d(DebugAll,"BrfLibUsbDevice::lusbCtrlTransfer()",
" (0x%x,0x%x,0x%x,%u,%p,%u,%u)%s [%p]",
reqType,request,value,index,data,len,tout,tmp.safe(),m_owner);
#endif
int code = ::libusb_control_transfer(m_devHandle,reqType,request,value,
index,data,len,tout ? tout : m_ctrlTout);
if (code == (int)len)
return 0;
String e;
unsigned int status = (code < 0) ? lusbCheckSuccess(code,&e) :
RadioInterface::HardwareIOError;
return showError(status,e,"Incomplete USB CTRL transfer",error);
}
// Wrapper for libusb bulk transfer
unsigned int BrfLibUsbDevice::lusbBulkTransfer(uint8_t endpoint, uint8_t* data,
unsigned int len, unsigned int* transferred, String* error, unsigned int tout)
{
#ifdef DEBUGGER_DEVICE_METH
Debugger d(DebugAll,"BrfLibUsbDevice::lusbBulkTransfer()",
" (0x%x,%p,%u,%u) [%p]",endpoint,data,len,tout,m_owner);
#endif
int nIO = 0;
int code = ::libusb_bulk_transfer(m_devHandle,endpoint,
data,len,&nIO,tout ? tout : m_bulkTout);
if (transferred)
*transferred = nIO;
if ((int)len == nIO)
return 0;
String e;
unsigned int status = (code < 0) ? lusbCheckSuccess(code,&e) :
RadioInterface::HardwareIOError;
return showError(status,e,"Incomplete USB BULK transfer",error);
}
// Make an async usb transfer
unsigned int BrfLibUsbDevice::syncTransfer(int ep, uint8_t* data, unsigned int len,
String* error)
{
LusbTransfer& t = m_usbTransfer[ep];
#ifdef DEBUG
if ((ep == EpReadSamples || ep == EpSendSamples) && !getIO(ep == EpSendSamples).busy)
Debug(m_owner,DebugFail,"syncTransfer() %s not locked [%p]",
brfDir(ep == EpSendSamples),m_owner);
if (t.running())
Debug(m_owner,DebugFail,"EP %s transfer is running [%p]",
lookup(ep,s_usbEndpoint),m_owner);
#endif
#ifdef DEBUGGER_DEVICE_METH
String tmp;
//tmp.hexify(data,len,' ');
//tmp = " data=" + tmp;
Debugger d(DebugAll,"BrfLibUsbDevice::syncTransfer()",
" (%s,%p,%u) transfer=(%p)%s [%p]",
lookup(ep,s_usbEndpoint),data,len,&t,tmp.safe(),m_owner);
#endif
t.running(true);
unsigned int cStatus = 0;
bool checkCancelled = !m_closingDevice;
if (t.fillBulk(data,len,m_bulkTout) && t.submit()) {
while (t.running()) {
struct timeval tv;
tv.tv_usec = 3 * Thread::idleUsec();
tv.tv_sec = 0;
::libusb_handle_events_timeout_completed(m_context,&tv,0);
if (checkCancelled && t.running() && cStatus == 0 &&
(cStatus = cancelled()) != 0) {
Debug(m_owner,DebugInfo,"Cancelling transfer %s [%p]",
lookup(ep,s_usbEndpoint),m_owner);
t.cancel();
}
}
}
Lock lck(t);
t.running(false);
if (checkCancelled && !t.status)
t.status = cancelled(&t.error);
return showError(t.status,t.error,"SYNC transfer failed",error);
}
// Read the value of a specific GPIO register
unsigned int BrfLibUsbDevice::gpioRead(uint8_t addr, uint32_t& value, uint8_t len,
String* error, const char* loc)
{
len = clampInt(len,1,sizeof(value),"GPIO read items",DebugGoOn);
uint8_t t[sizeof(value)];
unsigned int status = accessPeripheral(UartDevGPIO,false,addr,t,error,len,loc);
if (status)
return status;
value = 0;
// Data is in little endian order
#ifdef LITTLE_ENDIAN
for (uint8_t i = 0; i < len; i++)
value |= (uint64_t)(t[i] << (i * 8));
#else
for (uint8_t i = 0; i < len; i++)
value |= (uint64_t)(t[i] << ((len - i - 1) * 8));
#endif
return 0;
}
// Write a value to a specific GPIO register
unsigned int BrfLibUsbDevice::gpioWrite(uint8_t addr, uint32_t value, uint8_t len,
String* error, const char* loc)
{
len = clampInt(len,1,sizeof(value),"GPIO write items",DebugGoOn);
uint8_t t[sizeof(value)];
// Data is in little endian order
#ifdef LITTLE_ENDIAN
for (uint8_t i = 0; i < len; i++)
t[i] = (uint8_t)(value >> (i * 8));
#else
for (uint8_t i = 0; i < len; i++)
t[i] = (uint8_t)(value >> ((len - i - 1) * 8));
#endif
return accessPeripheral(UartDevGPIO,true,addr,t,error,len,loc);
}
unsigned int BrfLibUsbDevice::lnaSelect(int lna, String* error)
{
XDebug(m_owner,DebugAll,"lnaSelect(%d) [%p]",lna,m_owner);
String e;
unsigned int status = 0;
while (true) {
uint8_t data = 0;
BRF_FUNC_CALL_BREAK(lmsRead(0x75,data,&e));
int old = (data >> 4) & 0x03;
data &= ~0x30;
data |= ((lna & 0x03) << 4);
BRF_FUNC_CALL_BREAK(lmsWrite(0x75,data,&e));
if (old != lna) {
if (lna != LmsLnaNone)
Debug(m_owner,DebugInfo,"LNA %d enabled [%p]",lna,m_owner);
else
Debug(m_owner,DebugInfo,"LNAs disabled [%p]",m_owner);
}
return 0;
}
if (lna != LmsLnaNone)
e.printf(1024,"Failed to enable LNA %d - %s",lna,e.c_str());
else
e.printf(1024,"Failed to disable LNAs - %s",e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::lnaEnable(bool on, String* error)
{
XDebug(m_owner,DebugAll,"lnaEnable(%u) [%p]",on,m_owner);
uint8_t reg1 = 0;
uint8_t reg2 = 0;
String e;
unsigned int status = lmsRead2(0x7d,reg1,0x70,reg2,&e);
if (status == 0) {
bool changed = on != ((reg1 & 0x01) == 0 && (reg2 & 0x02) == 0);
if (on) {
reg1 &= ~0x01;
reg2 &= ~0x02;
}
else {
reg1 |= 0x01;
reg2 |= 0x02;
}
status = lmsWrite2(0x7d,reg1,0x70,reg2,&e);
if (status == 0) {
if (changed)
Debug(m_owner,DebugInfo,"%s LNA [%p]",enabledStr(on),m_owner);
return 0;
}
}
e.printf("Failed to %s LNA - %s",enableStr(on),e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::lnaGainSet(uint8_t value, String* error)
{
const char* what = lookup(value,s_lnaGain);
XDebug(m_owner,DebugAll,"lnaGainSet(%u,'%s') [%p]",value,what,m_owner);
String e;
unsigned int status = 0;
if (!what || value == LnaGainUnhandled)
status = setUnkValue(e);
while (status == 0) {
uint8_t data = 0;
BRF_FUNC_CALL_BREAK(lmsRead(0x75,data,&e));
uint8_t old = (uint8_t)((data >> 6) & 0x03);
data &= ~(3 << 6);
data |= ((value & 3) << 6);
BRF_FUNC_CALL_BREAK(lmsWrite(0x75,data,&e));
if (old != value)
Debug(m_owner,DebugInfo,"LNA GAIN set to %s [%p]",what,m_owner);
return 0;
}
e.printf(1024,"Failed to set LNA GAIN %u (%s) - %s",value,what,e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::lnaGainGet(uint8_t& value, String* error)
{
uint8_t data = 0;
unsigned int status = lmsRead(0x75,data,error,"LNA gain read register");
if (status)
return status;
data >>= 6;
data &= 3;
value = data;
if (value != LnaGainUnhandled)
return 0;
String e;
e.printf("LNA gain read abnormal value 0x%x",data);
return showError(RadioInterface::OutOfRange,e,0,error);
}
unsigned int BrfLibUsbDevice::internalSetLpfBandwidth(bool tx, uint32_t band,
String* error)
{
String e;
unsigned int status = lusbSetAltInterface(BRF_ALTSET_RF_LINK,&e);
while (!status) {
uint8_t data = 0;
uint8_t reg = lmsLpfAddr(tx);
BRF_FUNC_CALL_BREAK(lmsRead(reg,data,&e));
unsigned int i = 0;
for (; i < 15 && band > s_bandSet[i]; i++)
;
uint8_t bw = (uint8_t)(15 - i);
data &= ~0x3c; // Clear out previous bandwidth setting
data |= (bw << 2); // Apply new bandwidth setting
BRF_FUNC_CALL_BREAK(lmsWrite(reg,data,&e));
getIO(tx).lpfBw = s_bandSet[15 - i];
Debug(m_owner,DebugAll,"%s LPF bandwidth set to %u (0x%x) [%p]",
brfDir(tx),band,data,m_owner);
return 0;
}
e.printf(1024,"Failed to set %s LPF bandwidth %u: %s",brfDir(tx),band,e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::internalSetLpf(bool tx, int lpf, String* error)
{
const char* what = lookup(lpf,s_lpf);
XDebug(m_owner,DebugAll,"internalSetLpf(%u,%d,'%s') [%p]",tx,lpf,what,m_owner);
uint8_t addr = lmsLpfAddr(tx);
uint8_t reg1 = 0;
uint8_t reg2 = 0;
String e;
unsigned int status = 0;
if (what)
status = lmsRead2(addr,reg1,addr + 1,reg2,&e);
else
status = setUnkValue(e,0,"value");
if (status == 0) {
// Clear EN_LPF
switch (lpf) {
case LpfDisabled:
reg1 &= 0xfd; // Disable LPF: reset EN_LPF
reg2 &= 0xbf; // Normal operation: reset BYP_EN_LPF
break;
case LpfBypass:
reg1 &= 0xfd; // Disable LPF: reset EN_LPF
reg1 |= 0x40; // Bypass LPF: set BYP_EN_LPF
break;
case LpfNormal:
reg1 |= 0x02; // Enable LPF: set EN_LPF
reg2 &= 0xbf; // Normal operation: reset BYP_EN_LPF
break;
default:
status = setUnkValue(e,0,"value");
}
if (status == 0) {
status = lmsWrite2(addr,reg1,addr + 1,reg2,&e);
if (status == 0) {
if (getIO(tx).lpf != lpf) {
getIO(tx).lpf = lpf;
Debug(m_owner,DebugInfo,"%s LPF set to '%s' [%p]",
brfDir(tx),what,m_owner);
}
return 0;
}
}
}
e.printf(1024,"Failed to set %s LPF %u (%s) - %s",
brfDir(tx),lpf,TelEngine::c_safe(what),e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::internalGetLpf(bool tx, int* lpf, String* error)
{
uint8_t addr = lmsLpfAddr(tx);
uint8_t reg1 = 0;
uint8_t reg2 = 0;
String e;
unsigned int status = lmsRead2(addr,reg1,addr + 1,reg2,&e);
if (status == 0) {
int l = decodeLpf(reg1,reg2);
if (l != LpfInvalid) {
getIO(tx).lpf = l;
if (lpf)
*lpf = l;
XDebug(m_owner,DebugAll,"Got %s LPF %d (%s) [%p]",
brfDir(tx),l,lookup(l,s_lpf),m_owner);
return 0;
}
status = RadioInterface::OutOfRange;
e = "Invalid values, enabled and bypassed";
}
e.printf(1024,"Failed to retrieve %s LPF - %s",brfDir(tx),e.c_str());
return showError(status,e,0,error);
}
// Fill the m_list member of the class
unsigned int BrfLibUsbDevice::updateDeviceList(String* error)
{
clearDeviceList();
int n = ::libusb_get_device_list(m_context,&m_list);
if (n >= 0) {
m_listCount = n;
return 0;
}
String e;
appendLusbError(e,n);
return showError(RadioInterface::HardwareIOError,e,
"Failed to enumerate USB devices",error);
}
// Enable/disable RF and sample circulation on both RX and TX sides
unsigned int BrfLibUsbDevice::enableRf(bool tx, bool on, bool frontEndOnly, String* error)
{
#ifdef DEBUGGER_DEVICE_METH
Debugger d(DebugAll,"BrfLibUsbDevice::enableRf()",
" tx=%s on=%s frontEndOnly=%s [%p]",String::boolText(tx),String::boolText(on),
String::boolText(frontEndOnly),m_owner);
#endif
BrfDevIO& io = getIO(tx);
unsigned int status = 0;
String e;
if (!m_devHandle) {
if (!on) {
io.setRf(false);
return 0;
}
status = RadioInterface::NotInitialized;
e = "Not open";
}
while (!status) {
// RF front end
uint8_t addr = tx ? 0x40 : 0x70;
uint8_t val = tx ? 0x02 : 0x01;
status = lmsChangeMask(addr,val,on,&e);
if (status || frontEndOnly)
break;
// Samples circulation
uint8_t request = tx ? BRF_USB_CMD_RF_TX : BRF_USB_CMD_RF_RX;
uint32_t buf = (uint32_t)-1;
uint16_t value = on ? 1 : 0;
status = lusbCtrlTransfer(LUSB_CTRLTRANS_IFACE_VENDOR_IN,
request,value,0,(uint8_t*)&buf,sizeof(buf),&e);
if (status == 0 && le32toh(buf))
status = setError(RadioInterface::Failure,&e,"Device failure");
if (status)
e = "Samples circulation change failed - " + e;
break;
}
if (io.rfEnabled == on) {
io.setRf(on && status == 0);
return status;
}
io.setRf(on && status == 0);
const char* fEnd = frontEndOnly ? " front end" : "";
if (status == 0) {
Debug(m_owner,DebugAll,"%s RF %s%s [%p]",
enabledStr(on),brfDir(tx),fEnd,m_owner);
return 0;
}
e.printf(1024,"Failed to %s RF %s%s - %s",enableStr(on),brfDir(tx),fEnd,e.c_str());
return showError(status,e,0,error);
}
// Read the FPGA version
unsigned int BrfLibUsbDevice::getFpgaVersion(uint32_t& version)
{
#ifdef DEBUGGER_DEVICE_METH
Debugger d(DebugAll,"BrfLibUsbDevice::getFpgaVersion()");
#endif
BRF_CHECK_DEV("getFpgaVersion()");
String e;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"FPGA version get");
if (!status)
status = gpioRead(0x0c,version,4,&e);
if (status)
Debug(m_owner,DebugNote,"Failed to retrieve FPGA version: %s [%p]",
e.c_str(),m_owner);
return status;
}
// Check if fpga is loaded
unsigned int BrfLibUsbDevice::checkFpga()
{
String error;
int32_t data = 0;
unsigned int status = vendorCommand(BRF_USB_CMD_QUERY_FPGA_STATUS,BRF_ENDP_RX_SAMPLES,
data,&error);
if (status == 0) {
if (le32toh(data)) {
Debug(m_owner,DebugAll,"The FPGA is already configured [%p]",m_owner);
return 0;
}
Debug(m_owner,DebugAll,"The FPGA is not configured [%p]",m_owner);
return RadioInterface::NotInitialized;
}
Debug(m_owner,DebugNote,"FPGA check failed: %s [%p]",error.c_str(),m_owner);
return status;
}
// Restore device after loading the FPGA
unsigned int BrfLibUsbDevice::restoreAfterFpgaLoad(String* error)
{
String e;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"Restore after FPGA load");
while (!status) {
uint32_t gpio = 0;
status = gpioRead(0,gpio,4,&e);
if (status)
break;
if (gpio & 0x7fff) {
e.printf("Unexpected FPGA state 0x%x",gpio);
status = RadioInterface::Failure;
break;
}
// Enable the LMS and select the low band
status = gpioWrite(0,0x57,4,&e,"Failed to enable LMS and/or low band");
if (status)
break;
// Disable the TX/RX
if ((status = enableRfBoth(false,true,&e)) != 0)
break;
// Enabling LMS on TX side
status = lmsWrite(0x05,0x3e,&e,"Failed to enable LMS TX");
if (status)
break;
status = lmsWrite(0x47,0x49,&e,"Could not set the bias current for the LO");
if (status)
break;
status = lmsWrite(0x59,0x29,&e,"Could not set the ADC");
if (status)
break;
status = lmsWrite(0x64,0x36,&e,"Could not set the common mode for the ADC");
if (status)
break;
status = lmsWrite(0x79,0x37,&e,"Could not set the LNA gain");
break;
}
if (status == 0) {
XDebug(m_owner,DebugAll,"Restored device after FPGA load [%p]",m_owner);
return 0;
}
return showError(status,e,"Failed to restore device after FPGA load",error);
}
// Reset the Usb interface using an ioctl call
unsigned int BrfLibUsbDevice::resetUsb(String* error)
{
String e;
unsigned int status = openDevice(false,&e);
if (status)
return showError(status,e,"USB reset failed",error);
status = lusbCheckSuccess(::libusb_reset_device(m_devHandle),&e,"USB reset failed ");
if (!status)
Debug(m_owner,DebugAll,"Reset USB device bus=%d addr=%d [%p]",
m_devBus,m_devAddr,m_owner);
closeDevice();
return showError(status,e,0,error);
}
// Set the VCTCXO configuration to the correct value
unsigned int BrfLibUsbDevice::tuneVcocap(uint8_t addr, String* error)
{
uint8_t data = 0;
unsigned int status = lmsRead(addr + 9,data,error,"VCTCXO tune");
if (status)
return status;
uint8_t vcocap = 32;
uint8_t vtune = 0;
uint8_t step = vcocap >> 1;
data &= ~(0x3f);
for (int i = 0; i < 6; i++) {
if ((status = lmsWrite(addr + 9,vcocap | data,error,"VCTCXO tune")) != 0)
return status;
if ((status = lmsRead(addr + 10,vtune,error,"VCTCXO tune")) != 0)
return status;
vtune >>= 6;
if (vtune == VCO_NORM) {
XDebug(m_owner,DebugInfo,"tuneVcocap: Found normal VCO [%p]",m_owner);
break;
}
if (vtune == VCO_HIGH) {
XDebug(m_owner,DebugInfo,"tuneVcocap: VCO high [%p]",m_owner);
vcocap += step;
}
else if (vtune == VCO_LOW) {
XDebug(m_owner,DebugInfo,"tuneVcocap: VCO low [%p]",m_owner);
vcocap -= step ;
}
else
return setError(RadioInterface::Failure,error,
"VCTCXO tune - invalid tunning");
step >>= 1;
}
if (vtune != VCO_NORM)
return setError(RadioInterface::Failure,error,"VCTCXO tune - tunning not locked");
uint8_t start = vcocap;
while (start > 0 && vtune == VCO_NORM) {
start--;
if ((status = lmsWrite(addr + 9,start | data,error,"VCTCXO tune")) != 0)
return status;
if ((status = lmsRead(addr + 10,vtune,error,"VCTCXO tune")) != 0)
return status;
vtune >>= 6;
}
start++;
XDebug(m_owner,DebugInfo,"tuneVcocap: Found lower limit %u [%p]",start,m_owner);
if ((status = lmsWrite(addr + 9,vcocap | data,error,"VCTCXO tune")) != 0)
return status;
if ((status = lmsRead(addr + 10,vtune,error,"VCTCXO tune")) != 0)
return status;
vtune >>= 6;
uint8_t stop = vcocap;
while (stop < 64 && vtune == VCO_NORM) {
stop++;
if ((status = lmsWrite(addr + 9,stop | data,error,"VCTCXO tune")) != 0)
return status;
if ((status = lmsRead(addr + 10,vtune,error,"VCTCXO tune")) != 0)
return status;
vtune >>= 6;
}
stop--;
XDebug(m_owner,DebugAll,"tuneVcocap: Found lower limit %u [%p]",stop,m_owner);
vcocap = (start + stop) >> 1;
XDebug(m_owner,DebugInfo,"tuneVcocap: VCOCAP=%u [%p]",vcocap,m_owner);
if ((status = lmsWrite(addr + 9,vcocap | data,error,"VCTCXO tune")) != 0)
return status;
if ((status = lmsRead(addr + 10,vtune,error,"VCTCXO tune")) != 0)
return status;
vtune >>= 6;
DDebug(m_owner,DebugInfo,"tuneVcocap: VCTCXO=%u [%p]",vtune,m_owner);
if (vtune == VCO_NORM)
return 0;
return setError(RadioInterface::Failure,error,"VCTCXO tune failed");
}
// Send requests to the bladeRF device regarding the FPGA image configuration.
unsigned int BrfLibUsbDevice::vendorCommand(uint8_t cmd, uint8_t ep, uint8_t* data,
uint16_t len, String* error)
{
#ifdef DEBUGGER_DEVICE_METH
Debugger d(DebugAll,"BrfLibUsbDevice::vendorCommand()",
" (0x%x,0x%x,%p,%u) [%p]",cmd,ep,data,len,m_owner);
#endif
String e;
unsigned int status = lusbCtrlTransfer(LUSB_CTRLTRANS_IFACE_VENDOR | ep,
cmd,0,0,data,len,&e);
if (!status)
return 0;
e.printf(1024,"Vendor command 0x%x endpoint=0x%x failed - %s",cmd,ep,e.c_str());
return showError(status,e,0,error);
}
// Access the bladeRF board in order to transmit data
unsigned int BrfLibUsbDevice::accessPeripheral(uint8_t dev, bool tx, uint8_t addr,
uint8_t* data, String* error, uint8_t len, const char* loc)
{
static const uint8_t s_dev[UartDevCount] = {0,0x10,0x20,0x30};
if (dev >= UartDevCount)
return RadioInterface::Failure;
#ifdef DEBUGGER_DEVICE_METH
String tmp;
if (tx)
tmp.hexify(data,len,' ');
Debugger debug(DebugInfo,"BrfLibUsbDevice::accessPeripheral()",
" dev=%s dir=%s addr=0x%x len=%u bits=%s [%p]",
s_uartDevName[dev].c_str(),brfDir(tx),addr,len,tmp.safe(),m_owner);
#endif
String e;
unsigned int status = 0;
uint8_t a = addr;
uint8_t* d = data;
uint8_t n = len;
uint8_t maskDirDev = (uint8_t)((tx ? 0x40 : 0x80) | s_dev[dev]);
uint8_t buf[16] = {(uint8_t)'N'};
#define BRF_ACCESS_PERIPHERAL(nItems) \
{ \
buf[1] = (uint8_t)(maskDirDev | nItems); \
uint8_t* b = &buf[2]; \
::memset(b,0,sizeof(buf) - 2); \
for (uint8_t i = 0; i < nItems; i++) { \
*b++ = a + i; \
if (tx) \
*b = d[i]; \
b++; \
} \
status = syncTransfer(EpSendCtrl,buf,sizeof(buf),&e); \
if (!status) \
status = syncTransfer(EpReadCtrl,buf,sizeof(buf),&e); \
if (status == 0 && !tx) { \
b = &buf[3]; \
for (uint8_t i = 0; i < nItems; i++, b += 2) \
d[i] = *b; \
} \
}
if (n > 7) {
n = len % 7;
for (uint8_t full = len / 7; !status && full; full--, a += 7, d += 7)
BRF_ACCESS_PERIPHERAL(7);
}
if (n && !status)
BRF_ACCESS_PERIPHERAL(n);
#undef BRF_ACCESS_PERIPHERAL
if (status) {
e.printf(1024,"%s%s%s %s failed addr=0x%x len=%d - %s",
TelEngine::c_safe(loc),(loc ? " - " : ""),s_uartDevName[dev].c_str(),
tx ? "write" : "read",addr,len,e.c_str());
return showError(status,e,0,error);
}
if (s_debugPeripheral && m_owner->debugAt(DebugAll)) {
uint8_t mask = (tx ? 0x80 : 0x40) | (1 << dev);
if ((s_debugPeripheral & mask) == mask) {
String s;
Debug(m_owner,DebugAll,"%s %s addr=0x%x len=%u '%s' [%p]",
s_uartDevName[dev].c_str(),brfDir(tx),addr,len,
s.hexify(data,len,' ').c_str(),m_owner);
}
}
return 0;
}
unsigned int BrfLibUsbDevice::internalSetDcOffset(bool tx, bool i, int16_t value,
String* error)
{
int& old = i ? getIO(tx).dcOffsetI : getIO(tx).dcOffsetQ;
if (old == value)
return 0;
uint8_t addr = lmsCorrIQAddr(tx,i);
String e;
uint8_t data = 0;
unsigned int status = 0;
while (true) {
if (tx) {
if (value < BRF_TX_DC_OFFSET_MIN || value > BRF_TX_DC_OFFSET_MAX) {
status = setUnkValue(e,0,"value");
break;
}
}
else if (value < -BRF_RX_DC_OFFSET_MAX || value > BRF_RX_DC_OFFSET_MAX) {
status = setUnkValue(e,0,"value");
break;
}
BrfDevTmpAltSet tmpAltSet(this,status,&e,"DC offset set");
if (status)
break;
status = lmsRead(addr,data,&e);
if (status)
break;
if (tx)
// MSB bit is the sign (1: positive)
data = 128 + value;
else {
// MSB bit has nothing to do with RX DC offset
// Bit 6 is the sign bit (1: negative)
uint8_t b7 = (uint8_t)(data & 0x80);
if (value >= 0)
data = (uint8_t)((value >= 64) ? 0x3f : (value & 0x3f));
else {
data = (uint8_t)((value <= -64) ? 0x3f : ((-value) & 0x3f));
data |= 0x40;
}
data |= b7;
}
status = lmsWrite(addr,data,&e);
break;
}
if (status == 0) {
int tmp = decodeDCOffs(tx,data);
if (tmp != old) {
old = tmp;
if (getIO(tx).showDcOffsChange == 0)
Debug(m_owner,DebugAll,"%s DC offset %c set to %d (from %d) reg=0x%x [%p]",
brfDir(tx),brfIQ(i),old,value,data,m_owner);
}
return 0;
}
e.printf(1024,"%s DC offset %c set to %d failed - %s",
brfDir(tx),brfIQ(i),value,e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::internalGetDcOffset(bool tx, bool i, int16_t* value,
String* error)
{
uint8_t addr = lmsCorrIQAddr(tx,i);
String e;
uint8_t data = 0;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"DC offset get");
if (!status)
status = lmsRead(addr,data,&e);
if (!status) {
int& old = i ? getIO(tx).dcOffsetI : getIO(tx).dcOffsetQ;
old = decodeDCOffs(tx,data);
if (value)
*value = old;
XDebug(m_owner,DebugAll,"Got %s DC offset %c %d (0x%x) [%p]",
brfDir(tx),brfIQ(i),old,data,m_owner);
return 0;
}
e.printf(1024,"%s DC offset %c get failed - %s",brfDir(tx),brfIQ(i),e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::enableTimestamps(bool on, String* error)
{
String e;
unsigned int status = 0;
while (true) {
uint32_t val = 0;
BRF_FUNC_CALL_BREAK(gpioRead(0,val,4,&e));
if (on)
val |= 0x10000;
else
val &= ~0x10000;
BRF_FUNC_CALL_BREAK(gpioWrite(0,val,4,&e));
if (on) {
BRF_FUNC_CALL_BREAK(gpioRead(0,val,4,&e));
if ((val & 0x10000) == 0) {
status = setError(RadioInterface::Failure,&e,"not enabled");
break;
}
}
Debug(m_owner,DebugAll,"%s timestamps [%p]",enabledStr(on),m_owner);
return 0;
}
e.printf(1024,"Failed to %s timestamps - %s",enableStr(on),e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::updateStatus(String* error)
{
unsigned int status = 0;
// Frequency
BRF_FUNC_CALL(internalGetFrequency(true));
BRF_FUNC_CALL(internalGetFrequency(false));
// Update VGA data
BRF_FUNC_CALL(internalGetTxVga(0,true,error));
BRF_FUNC_CALL(internalGetTxVga(0,false,error));
BRF_FUNC_CALL(internalGetRxVga(0,true,error));
BRF_FUNC_CALL(internalGetRxVga(0,false,error));
// LPF
internalGetLpf(true,0,error);
internalGetLpf(false,0,error);
// Update DC offsets
BRF_FUNC_CALL(internalGetDcOffset(true,true,0,error));
BRF_FUNC_CALL(internalGetDcOffset(true,false,0,error))
BRF_FUNC_CALL(internalGetDcOffset(false,true,0,error));
BRF_FUNC_CALL(internalGetDcOffset(false,false,0,error));
// Update FPGA correction
BRF_FUNC_CALL(internalGetFpgaCorr(true,CorrFpgaGAIN,0,error));
BRF_FUNC_CALL(internalGetFpgaCorr(true,CorrFpgaPHASE,0,error));
BRF_FUNC_CALL(internalGetFpgaCorr(false,CorrFpgaGAIN,0,error));
BRF_FUNC_CALL(internalGetFpgaCorr(false,CorrFpgaPHASE,0,error));
return status;
}
unsigned int BrfLibUsbDevice::paSelect(int pa, String* error)
{
uint8_t data = 0;
String e;
unsigned int status = lmsRead(0x44,data,&e);
while (status == 0) {
int old = decodePa(data);
// Disable all PAs
data = (data & ~0x1c) | (1 << 1);
switch (pa) {
case LmsPaAux:
data &= ~(1 << 1);
break;
case LmsPa1:
data |= (1 << 3);
break;
case LmsPa2:
data |= (1 << 4);
break;
case LmsPaNone:
break;
default:
status = setUnkValue(e);
}
if (status)
break;
status = lmsWrite(0x44,data,&e);
if (status == 0) {
int level = (old != pa) ? DebugInfo : DebugAll;
if (pa != LmsPaNone)
Debug(m_owner,level,"PA %s enabled [%p]",lookup(pa,s_pa),m_owner);
else
Debug(m_owner,level,"PAs disabled [%p]",m_owner);
return 0;
}
break;
}
if (pa != LmsPaNone)
e.printf(1024,"Failed to enable PA %d (%s) - %s",pa,lookup(pa,s_pa),e.c_str());
else
e.printf(1024,"Failed to disable PAs - %s",e.c_str());
return showError(status,e,0,error);
}
// Clamp frequency value
void BrfLibUsbDevice::clampFrequency(uint32_t& val, bool tx, const char* loc)
{
if (val >= BRF_FREQUENCY_MIN && val <= BRF_FREQUENCY_MAX)
return;
uint32_t c = val < BRF_FREQUENCY_MIN ? BRF_FREQUENCY_MIN : BRF_FREQUENCY_MAX;
Debug(m_owner,DebugNote,"%s: clamping %s frequency %u to %u [%p]",
loc,brfDir(tx),val,c,m_owner);
val = c;
}
int BrfLibUsbDevice::clampInt(int val, int minVal, int maxVal, const char* what,
int level)
{
if (val >= minVal && val <= maxVal)
return val;
int c = val < minVal ? minVal : maxVal;
if (what)
Debug(m_owner,level,"Clamping %s %d -> %d [%p]",what,val,c,m_owner);
return c;
}
unsigned int BrfLibUsbDevice::openDevice(bool claim, String* error)
{
closeDevice();
m_dev = 0;
unsigned int status = updateDeviceList(error);
if (status)
return status;
unsigned int failedDesc = 0;
libusb_device_descriptor desc;
for (unsigned int i = 0; !m_dev && i < m_listCount; i++) {
if (::libusb_get_device_descriptor(m_list[i],&desc)) {
failedDesc++;
continue;
}
if (desc.idVendor == 7504 && desc.idProduct == 24678)
m_dev = m_list[i];
}
if (!m_dev) {
String e = "No device found";
if (failedDesc)
e << " (failed to retrieve " << failedDesc <<
" device descriptor(s))";
return setError(RadioInterface::NotInitialized,error,e);
}
m_devBus = ::libusb_get_bus_number(m_dev);
m_devAddr = ::libusb_get_device_address(m_dev);
m_devSpeed = ::libusb_get_device_speed(m_dev);
DDebug(m_owner,DebugAll,"Opening device bus=%u addr=%u [%p]",bus(),addr(),m_owner);
status = lusbCheckSuccess(::libusb_open(m_dev,&m_devHandle),
error,"Failed to open the libusb device ");
if (status)
return status;
getDevStrDesc(m_devSerial,desc.iSerialNumber,"serial number");
getDevStrDesc(m_devFwVerStr,4,"firmware version");
if (claim)
status = lusbCheckSuccess(::libusb_claim_interface(m_devHandle,0),
error,"Failed to claim the interface ");
return status;
}
void BrfLibUsbDevice::closeDevice()
{
if (!m_devHandle)
return;
m_closingDevice = true;
internalPowerOn(false,false,false);
m_closingDevice = false;
::libusb_close(m_devHandle);
m_devHandle = 0;
m_devBus = -1;
m_devAddr = -1;
m_devSpeed = LIBUSB_SPEED_HIGH;
m_devSerial.clear();
m_devFwVerStr.clear();
m_devFpgaVerStr.clear();
m_devFpgaFile.clear();
Debug(m_owner,DebugAll,"Device closed [%p]",m_owner);
}
void BrfLibUsbDevice::getDevStrDesc(String& data, uint8_t index, const char* what)
{
unsigned char buf[256];
int len = ::libusb_get_string_descriptor_ascii(m_devHandle,index,buf,sizeof(buf) - 1);
if (len >= 0) {
buf[len] = 0;
data = (const char*)buf;
return;
}
data.clear();
String tmp;
Debug(m_owner,DebugInfo,"Failed to retrieve device %s %s [%p]",
what,appendLusbError(tmp,len).c_str(), m_owner);
}
// Read pages from device using control transfer
unsigned int BrfLibUsbDevice::ctrlTransferReadPage(uint8_t request, DataBlock& buf,
String* error)
{
if (!m_ctrlTransferPage)
return setError(RadioInterface::Failure,error,"Invalid CTRL transfer page size");
buf.resize(BRF_FLASH_PAGE_SIZE);
uint8_t* b = buf.data(0);
// Retrieve data from the firmware page buffer
for (unsigned int offs = 0; offs < buf.length(); offs += m_ctrlTransferPage) {
unsigned int status = lusbCtrlTransfer(LUSB_CTRLTRANS_DEV_VENDOR_IN,
request,0,offs,b + offs,m_ctrlTransferPage,error);
if (status)
return status;
}
return 0;
}
// Read calibration cache page from device
unsigned int BrfLibUsbDevice::readCalCache(String* error)
{
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,BRF_ALTSET_SPI_FLASH,status,error,
"read calibration cache");
m_calCache.clear();
if (status == 0)
return ctrlTransferReadPage(BRF_USB_CMD_READ_CAL_CACHE,m_calCache,error);
return status;
}
static uint16_t crc16(uint8_t* buf, unsigned int len)
{
uint16_t crc = 0;
for (uint8_t* last = buf + len; buf < last; buf++) {
crc ^= (uint16_t)(((uint16_t)*buf) << 8);
for (int i = 0; i < 8; i++) {
if ((crc & 0x8000) != 0)
crc = (uint16_t)(crc << 1) ^ 0x1021;
else
crc = (uint16_t)(crc << 1);
}
}
return crc;
}
// Retrieve a filed from a buffer of elements
// Buffer format: 1 byte length + data + 2 bytes CRC16
const char* BrfLibUsbDevice::getBufField(String& value, const char* field)
{
if (TelEngine::null(field))
return "empty-field";
uint8_t* b = m_calCache.data(0);
unsigned int len = m_calCache.length();
if (!len)
return "calibration-cache-not-loaded";
for (uint8_t dataLen = 0; len; len -= dataLen, b += dataLen) {
dataLen = *b;
// No more data ?
if (dataLen == 0xff)
return "unexpected end of data";
// Do we have enough data ?
if (len < (dataLen + 2u))
return "wrong data - invalid field length";
uint16_t crc = le32toh(*(uint16_t*)(b + dataLen + 1));
uint16_t crcCheck = crc16(b,dataLen + 1);
if (crcCheck != crc)
return "wrong data - invalid CRC";
unsigned int fLen = 0;
const char* s = (const char*)(b + 1);
const char* f = field;
for (; fLen <= dataLen && *f && *s == *f; s++, f++)
fLen++;
if (!*f) {
value.assign(s,dataLen - fLen);
return 0;
}
dataLen += 3;
}
return "not found";
}
// Read calibration cache field
unsigned int BrfLibUsbDevice::getCalField(String& value, const String& name,
const char* desc, String* error)
{
String e = getBufField(value,name);
if (!e)
return 0;
e.printf(2048,"Failed to retrieve calibration cache field '%s' (%s) - %s",
name.c_str(),desc,e.c_str());
return showError(RadioInterface::Failure,e,0,error);
}
String& BrfLibUsbDevice::dumpCalCache(String& dest)
{
String e;
dest.append("(LEN|VALUE|CRC)"," ");
uint8_t* b = m_calCache.data(0);
unsigned int len = m_calCache.length();
for (uint8_t dataLen = 0; len; len -= dataLen, b += dataLen) {
dataLen = *b;
// No more data ?
if (dataLen == 0xff) {
len = 0;
break;
}
dest << " " << dataLen;
// Do we have enough data ?
if (len < (dataLen + 2u)) {
dest << "-|-";
break;
}
String crcS;
crcS.hexify(b + dataLen + 1,2);
uint16_t crc = le32toh(*(uint16_t*)(b + dataLen + 1));
uint16_t crcCheck = crc16(b,dataLen + 1);
if (crcCheck != crc)
crcS << "(invalid)";
dest << "|" << String((const char*)(b + 1),dataLen) << "|" << crcS;
dataLen += 3;
}
if (len)
dest << " garbage=" << len;
return dest;
}
// Update speed related data
unsigned int BrfLibUsbDevice::updateSpeed(String* error)
{
if (speed() == LIBUSB_SPEED_SUPER || speed() == LIBUSB_SPEED_HIGH) {
unsigned int brfBufSamples = ((speed() == LIBUSB_SPEED_SUPER) ? 512 : 256) - 4;
unsigned int nBuffers = 4;
m_txIO.resetSamplesBuffer(brfBufSamples,16,nBuffers);
m_rxIO.resetSamplesBuffer(brfBufSamples,16,nBuffers);
if (speed() == LIBUSB_SPEED_SUPER)
m_ctrlTransferPage = BRF_FLASH_PAGE_SIZE;
else
m_ctrlTransferPage = 64;
return 0;
}
m_ctrlTransferPage = 0;
String e;
e << "Unsupported USB speed " << m_devSpeed;
return setError(RadioInterface::OutOfRange,error,e);
}
// Check timestamps after reading from device
void BrfLibUsbDevice::ioBufCheckTs(bool tx, unsigned int nBufs)
{
String invalid;
BrfDevIO& io = getIO(tx);
if (!nBufs)
nBufs = io.buffers;
unsigned int i = 0;
if (!io.lastTs) {
io.lastTs = io.bufTs(0);
i = 1;
}
for (; i < nBufs; i++) {
uint64_t crt = io.bufTs(i);
if ((io.lastTs + io.bufSamples) != crt) {
if (!invalid)
invalid << ": invalid timestamps (buf=ts/delta)";
invalid << " " << (i + 1) << "=" << crt << "/" << (int64_t)(crt - io.lastTs);
}
io.lastTs = crt;
}
if (invalid)
Debug(m_owner,invalid ? DebugNote : DebugAll,"%s: %u buffers%s [%p]",
brfDir(tx),nBufs,invalid.safe(),m_owner);
}
void BrfLibUsbDevice::updateAlterData(const NamedList& params)
{
if (params.getBoolValue(YSTRING("rx_alter_increment")))
m_rxAlterIncrement = 1;
else
m_rxAlterIncrement = 0;
m_rxAlterData = (m_rxAlterIncrement != 0);
}
void BrfLibUsbDevice::rxAlterData()
{
BrfDevIO& io = m_rxIO;
if (m_rxAlterIncrement) {
int maxVal = 9999 - io.bufSamples * 4;
for (unsigned int i = 0; i < io.buffers; i++) {
if (m_rxAlterIncrement >= maxVal)
m_rxAlterIncrement = 1;
int16_t* p = io.samples(i);
int16_t* last = io.samplesEOF(i);
while (p != last) {
*p++ = m_rxAlterIncrement;
*p++ = -m_rxAlterIncrement;
m_rxAlterIncrement++;
}
}
}
#if 0
else
return;
printIOBuffer(false,"alter");
#endif
}
unsigned int BrfLibUsbDevice::calBackupRestore(BrfCalData& bak, bool backup,
String* error)
{
const char* what = backup ? "backup" : "restore";
DDebug(m_owner,DebugAll,"Calibration %s %s [%p]",what,bak.modName(),m_owner);
unsigned int status = 0;
String e;
while (true) {
// We will backup the data in the CLK_EN register in case something goes wrong
status = lms(backup,0x09,bak.clkEn,&e);
if (status)
break;
if (bak.module == BRF_CALIBRATE_RX_LPF || bak.module == BRF_CALIBRATE_RX_VGA2) {
BRF_FUNC_CALL_BREAK(lms(backup,0x71,bak.inputMixer,&e));
BRF_FUNC_CALL_BREAK(lms(backup,0x7c,bak.loOpt,&e));
BRF_FUNC_CALL_BREAK(lnaGain(backup,bak.lnaGain,&e));
BRF_FUNC_CALL_BREAK(internalRxVga(backup,bak.rxVga1,true,&e));
status = internalRxVga(backup,bak.rxVga2,false,&e);
break;
}
if (bak.module == BRF_CALIBRATE_TX_LPF ||
bak.module == BRF_CALIBRATE_LPF_TUNING) {
DDebug(m_owner,DebugAll,"calBackupRestore: nothing to do for %s [%p]",
bak.modName(),this);
break;
}
status = setUnhandled(e,bak.module,"module");
break;
}
if (status == 0)
return 0;
e.printf(2048,"Failed to %s calibration data for module %s - %s",
what,bak.modName(),e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::calInitFinal(BrfCalData& bak, bool init, String* error)
{
const char* what = init ? "initialize" : "finalize";
DDebug(m_owner,DebugAll,"Calibration %s %s [%p]",what,bak.modName(),m_owner);
String e;
unsigned int status = 0;
while (true) {
// Enable the appropriate CLK_EN bit
if (init)
status = lmsWrite(0x09,bak.clkEn | bak.desc->clkEnMask,&e);
if (status)
break;
if (bak.module == BRF_CALIBRATE_LPF_TUNING) {
DDebug(m_owner,DebugAll,"calInitFinal(%s): nothing to do for %s [%p]",
what,bak.modName(),this);
break;
}
// Enable special conditions
if (bak.module == BRF_CALIBRATE_RX_LPF || bak.module == BRF_CALIBRATE_RX_VGA2) {
// Set RXVGA2 DECODE on init/finalize
if (!init) {
if (bak.module == BRF_CALIBRATE_RX_VGA2) {
BRF_FUNC_CALL_BREAK(setRxVga2Decode(true,&e));
}
}
// FAQ 5.26 (rev 1.0r13) DC comparators should be
// powered up before calibration and then powered down after it
if (bak.module == BRF_CALIBRATE_RX_VGA2) {
BRF_FUNC_CALL_BREAK(lmsChangeMask(0x63,0xc0,!init,&e));
}
else
BRF_FUNC_CALL_BREAK(lmsChangeMask(0x5f,0x80,!init,&e));
// Done for finalize
if (!init)
break;
#if 0
// TODO: Check it !!! It is really necessary ?
// Connect LNA to the external pads and internally terminate
status = lmsWrite2(0x71,bak.inputMixer & 0x7f,0x7c,bak.loOpt | 0x04,&e);
if (status)
break;
#endif
// FAQ 4.2 (rev 1.0r13): Attempt to calibrate RX at max gain
BRF_FUNC_CALL_BREAK(lnaGainSet(LnaGainMax,&e));
BRF_FUNC_CALL_BREAK(internalSetRxVga(BRF_RXVGA1_GAIN_MAX,true,&e));
BRF_FUNC_CALL_BREAK(internalSetRxVga(BRF_RXVGA2_GAIN_MAX,false,&e));
if (bak.module == BRF_CALIBRATE_RX_VGA2)
status = setRxVga2Decode(true,&e);
break;
}
if (bak.module == BRF_CALIBRATE_TX_LPF) {
// TX_DACBUF_PD (TX data DAC buffers)
// LMS6002 Quick starter manual, Section 6.1
// No signal should be applied to DACs (power down: bit is 1)
// PD_DCOCMP_LPF (DC offset comparator of DC offset cancellation)
// It must be powered down (bit set to 1) when calibrating
if (init) {
BRF_FUNC_CALL_BREAK(lmsSet(0x36,0x80,&e));
BRF_FUNC_CALL_BREAK(lmsReset(0x3f,0x80,&e));
}
else {
BRF_FUNC_CALL_BREAK(lmsSet(0x3f,0x80,&e));
BRF_FUNC_CALL_BREAK(lmsReset(0x36,0x80,&e));
}
break;
}
status = setUnhandled(e,bak.module,"module");
break;
}
if (status == 0)
return 0;
e.printf(2048,"Failed to %s calibration for module %s - %s",
what,bak.modName(),e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::dcCalProcPrepare(const BrfCalData& bak, uint8_t subMod,
String& e)
{
if (bak.module != BRF_CALIBRATE_RX_VGA2)
return 0;
// Prepare RX VGA2 calibration
if (subMod > 4)
return setUnhandled(e,subMod,"submodule");
// RXVGA2 DC REF module (subMod 0)
// Set RXVGA2GAIN A and B to default values
if (subMod == 0)
return lmsWrite(0x68,0x01,&e);
// VGA2 A/B I/Q channels
// Set DECODE bit to direct signal on start
if (subMod == 1) {
unsigned int status = setRxVga2Decode(false,&e);
if (status)
return status;
}
// subMod 1: set RXVGA2GAIN A=18dB and B=0
// subMod 3: set RXVGA2GAIN A=0 and B=18dB
if (subMod == 1 || subMod == 3)
return lmsWrite(0x68,(subMod == 1) ? 0x06 : 0x60,&e);
return 0;
}
unsigned int BrfLibUsbDevice::dcCalProc(const BrfCalData& bak, uint8_t subMod,
uint8_t dcCnt, uint8_t& dcReg, String& e)
{
// Set active calibration module address
uint8_t data = 0;
unsigned int status = 0;
BRF_FUNC_CALL_RET(lmsRead(bak.desc->addr + 3,data,&e));
data &= ~(0x07);
data |= subMod & 0x07;
BRF_FUNC_CALL_RET(lmsWrite(bak.desc->addr + 3,data,&e));
// Set CNTVAL
BRF_FUNC_CALL_RET(lmsWrite(bak.desc->addr + 2,dcCnt & 0x1f,&e));
// DC_LOAD: Auto load DC_CNTVAL (1: load, 0: don't load)
data |= 0x10;
BRF_FUNC_CALL_RET(lmsWrite(bak.desc->addr + 3,data,&e));
// Disable auto load of DC_CNTVAL, just in case something goes wrong
data &= ~0x10;
BRF_FUNC_CALL_RET(lmsWrite(bak.desc->addr + 3,data,&e));
uint8_t clbrStart = data | 0x20;
uint8_t clbrStop = data & ~0x20;
// See Section 4.1: General DC calibration procedure
bool first = true;
while (true) {
// Calibrate
// Enable and disable DC_START_CLBR
BRF_FUNC_CALL_RET(lmsWrite2(bak.desc->addr + 3,clbrStart,bak.desc->addr + 3,clbrStop,&e));
// We should wait for 6.4 us for calibration to end
Thread::msleep(1);
dcReg = 0xff;
for (unsigned int i = 0; i < 30; i++) {
String tmp;
BRF_FUNC_CALL_RET(cancelled(&e));
// Poll for DC_CLBR_DONE
status = lmsRead(bak.desc->addr + 1,data,&tmp);
if (status) {
Debug(m_owner,DebugMild,"%s [%p]",e.c_str(),m_owner);
status = 0;
continue;
}
if ((data & 0x02) != 0)
continue;
// Read DC_REG
BRF_FUNC_CALL_RET(lmsRead(bak.desc->addr,data,&e));
dcReg = (data & 0x3f);
}
if (dcReg == 0xff)
return setError(RadioInterface::Failure,&e,"Calibration loop timeout");
if (first) {
if (dcReg != 31)
break;
first = false;
continue;
}
if (dcReg == 0) {
e << "Algorithm does not converge for submodule " << subMod;
return RadioInterface::Failure;
}
break;
}
DDebug(m_owner,DebugAll,"%s calibrated submodule %u -> %u [%p]",
bak.modName(),subMod,dcReg,m_owner);
return 0;
}
unsigned int BrfLibUsbDevice::dcCalProcPost(const BrfCalData& bak, uint8_t subMod,
uint8_t dcReg, String& e)
{
unsigned int status = 0;
if (bak.module == BRF_CALIBRATE_LPF_TUNING) {
// Set DC_REG in TX/RX LPF DCO_DACCAL
uint8_t addr[] = {0x35,0x55};
for (uint8_t i = 0; !status && i < sizeof(addr); i++)
status = lmsSet(addr[i],dcReg,0x3f,&e);
if (status)
e.printf("Failed to set DCO_DACCAL - %s",e.c_str());
}
return status;
}
unsigned int BrfLibUsbDevice::readBuffer(uint64_t ts, float* buf, unsigned int samples,
String* error)
{
unsigned int status = 0;
while (samples) {
unsigned int n = samples;
BRF_FUNC_CALL_RET(recv(ts,buf,n));
BRF_FUNC_CALL_RET(cancelled(error));
ts += n;
buf += n * 2;
samples -= n;
}
return 0;
}
unsigned int BrfLibUsbDevice::readComputeX_LMSIQ(uint8_t dcI, uint8_t dcQ, double& erf,
float* buf, unsigned int samples, String* error, double& energy, int* dumpRx)
{
unsigned int status = 0;
// Apply I/Q offset
m_txISet = decodeDCOffs(true,dcI);
m_txQSet = decodeDCOffs(true,dcQ);
BRF_FUNC_CALL_RET(syncTs(true,error));
BRF_FUNC_CALL_RET(readBuffer(m_txIO.syncTs + 10 * samples,buf,samples,error));
bool dump = dumpRx && *dumpRx;
if (dump) {
if (*dumpRx > 0)
*dumpRx = *dumpRx - 1;
String tmp;
unsigned int n = 50;
if (n > samples)
n = samples;
appendComplex(tmp,buf,n);
Output("RECV (%u/%u): %s",n,samples,tmp.c_str());
}
energy = 0;
double avgI = 0;
double avgQ = 0;
float* last = buf + samples * 2;
for (float* b = buf; b != last; ) {
energy += *b * *b;
avgI += *b++;
energy += *b * *b;
avgQ += *b++;
}
avgI /= samples;
avgQ /= samples;
erf = avgI * avgI + avgQ * avgQ;
energy /= samples;
return 0;
}
unsigned int BrfLibUsbDevice::calibrateRx(String* error)
{
//Debugger debug(DebugAll,"Calibrating RX"," [%p]",m_owner);
String e;
unsigned int status = 0;
BrfDevStatus txOld(m_txIO);
BrfDevStatus rxOld(m_rxIO);
bool oldRxDcAuto = setRxDcAuto(false);
int oldCheckTs = m_rxIO.checkTs;
m_rxIO.checkTs = 0;
while (true) {
// Set VGA to min values
BRF_FUNC_CALL_BREAK(internalSetVga(true,BRF_TXVGA1_GAIN_MIN,true,error));
BRF_FUNC_CALL_BREAK(internalSetVga(true,BRF_TXVGA2_GAIN_MIN,false,error));
BRF_FUNC_CALL_BREAK(internalSetVga(false,BRF_RXVGA1_GAIN_MIN,true,error));
BRF_FUNC_CALL_BREAK(internalSetVga(false,BRF_RXVGA2_GAIN_MIN,false,error));
// TX / RX different freq
if (txOld.frequency == rxOld.frequency)
BRF_FUNC_CALL_BREAK(internalSetFrequency(true,txOld.frequency + 10000000,error));
// Disable TX, enable RX
BRF_FUNC_CALL_BREAK(internalPowerOn(false,false,true,&e));
// Read data, adjust RX DC I / Q
Thread::msleep(500);
unsigned int samples = 4000; //m_rxIO.bufSamples * m_rxIO.buffers;
DataBlock d(0,samplesf2bytes(samples));
uint64_t ts = 0;
BRF_FUNC_CALL_BREAK(internalGetTimestamp(false,ts,error));
float* buf = (float*)d.data(0);
BRF_FUNC_CALL_BREAK(readBuffer(ts + 5 * samples,buf,samples,error));
String tmp;
//appendComplex(tmp,buf,samples);
//tmp = "\r\n-----\r\n" + tmp + "\r\n-----";
float avgI = 0;
float avgQ = 0;
for (float* last = buf + samples * 2; buf != last; ) {
avgI += *buf++ * 2048;
avgQ += *buf++ * 2048;
}
int i = avgI / samples;
int q = avgQ / samples;
Debug(m_owner,DebugInfo,"Calibrate RX: I/Q=%d/%d samples=%u [%p]%s",
i,q,samples,m_owner,tmp.safe());
internalSetCorrectionIQ(false,i,q,error);
break;
}
m_rxIO.checkTs = oldCheckTs;
setRxDcAuto(oldRxDcAuto);
unsigned int restore = DevStatFreq | DevStatVga;
setStatus(txOld,restore,rxOld,restore);
return status;
}
unsigned int BrfLibUsbDevice::calibrateTxLOLeakage(String* error)
{
Debug(m_owner,DebugInfo,"Calibrating TX LO Leakage [%p]",m_owner);
String e;
unsigned int status = 0;
String pattern = m_txPatternStr;
int oldLoop = m_loopback;
BrfDevStatus txOld(m_txIO);
BrfDevStatus rxOld(m_rxIO);
m_txIO.showDcOffsChange++;
bool oldRxDcAuto = setRxDcAuto(false);
int dumpRx = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"Calibrate TX LO Leakage");
while (status == 0) {
// Calibrate RX
BRF_FUNC_CALL_BREAK(calibrateRx(&e));
// Turn radio OFF
BRF_FUNC_CALL_BREAK(internalPowerOn(false,false,false,&e));
// Set loopback
bool paOn = false;
bool lowBand = brfIsLowBand(txOld.frequency);
int lp = lowBand ? LoopRfLna1 : LoopRfLna2;
const char* pattern = "circle";
NamedList p("calibrate-tx-lo");
loadCfg(false,&p);
if (p.c_str()) {
lp = lookup(p["loopback"],s_loopback,lp);
paOn = p.getBoolValue("transmit");
dumpRx = p.getIntValue("dumprx");
pattern = p.getValue("txpattern",pattern);
}
BRF_FUNC_CALL_BREAK(internalSetLoopback(lp,&e));
if (paOn)
BRF_FUNC_CALL_BREAK(selectPaLna(true,lowBand,&e));
setTxPattern(pattern);
#if 1
// Set VGA
BRF_FUNC_CALL_BREAK(internalSetVga(true,-10,true,&e));
BRF_FUNC_CALL_BREAK(internalSetVga(true,25,false,&e));
BRF_FUNC_CALL_BREAK(internalSetVga(false,30,true,&e));
BRF_FUNC_CALL_BREAK(internalSetVga(false,30,false,&e));
#endif
// Turn radio ON
BRF_FUNC_CALL_BREAK(internalPowerOn(false,true,true,&e));
Thread::msleep(500);
dumpLoopbackStatus();
String s;
internalDumpDev(s,false,true,"\r\n",true,false,true);
String dev;
internalDumpPeripheral(UartDevLMS,0,128,&dev,16);
s << "\r\nLMS:" << dev;
Debug(m_owner,DebugAll,"TX LO Leakage starting status: [%p]\r\n-----\r\n%s\r\n-----",
m_owner,s.c_str());
// Start sending thread
m_sendThread = (new BrfThread(this))->start();
if (!m_sendThread) {
status = RadioInterface::Failure;
e << "Failed to start send data thread";
}
break;
}
while (status == 0) {
unsigned int samples = m_rxIO.bufSamples * m_rxIO.buffers;
DataBlock d(0,samplesf2bytes(samples));
float* buf = (float*)d.data(0);
double bestErf = 0xffffffffffffffff;
int bestDcI = 128;
int bestDcQ = 128;
int dcI = 0;
int dcQ = 0;
double oldErf = 0xffffffffffffffff;
double erf = 0;
double energy = 0;
BRF_FUNC_CALL_BREAK(readComputeX_LMSIQ(bestDcI,bestDcQ,bestErf,buf,samples,&e,energy,&dumpRx));
Output("Starting with I/Q=%d/%d best_erf=%g energy=%g",bestDcI,bestDcQ,bestErf,energy);
String tmp = "TX LO Leakage ";
for (int i = 0; !status && i < 6; i++) {
int loops = 0;
int step = i > 1 ? 1 : 15;
char loopType = 0;
#define SET_I_Q_TEMP_LOOPS(_i,_q,_iLoop,_loops) \
loopType = _iLoop; \
dcI = clampInt(_i,0,255,tmp + "init I"); \
dcQ = clampInt(_q,0,255,tmp + "init Q"); \
loops = _loops; \
break;
switch (i) {
case 0: SET_I_Q_TEMP_LOOPS(255,128,'I',17);
case 1: SET_I_Q_TEMP_LOOPS(bestDcI,255,'Q',17);
case 2: SET_I_Q_TEMP_LOOPS(bestDcI + 15,bestDcQ,'I',31);
case 3: SET_I_Q_TEMP_LOOPS(bestDcI,bestDcQ + 15,'Q',31);
case 4: SET_I_Q_TEMP_LOOPS(bestDcI + 2,bestDcQ,'I',5);
case 5: SET_I_Q_TEMP_LOOPS(bestDcI,bestDcQ + 2,'Q',5);
}
#undef SET_I_Q_TEMP_LOOPS
int& bestIQ = (loopType == 'I') ? bestDcI : bestDcQ;
int& iq = (loopType == 'I') ? dcI : dcQ;
Output("%d: LoopMode=%c erf_loops=%d step=%d I/Q=%d/%d best_I/Q=%d/%d",
i + 1,loopType,loops,step,dcI,dcQ,bestDcI,bestDcQ);
while (loops--) {
BRF_FUNC_CALL_BREAK(readComputeX_LMSIQ(dcI,dcQ,erf,buf,samples,&e,energy,&dumpRx));
String dir;
if (oldErf < erf)
dir << "UP";
else if (oldErf > erf)
dir << "DOWN";
else
dir << "-";
oldErf = erf;
String dump;
dump.printf(1024," I=%-3d Q=%-3d erf=%.6f\tenergy=%.6f\t%s",dcI,dcQ,erf,energy,dir.safe());
if (bestErf > erf) {
dump << "\t\t" << loopType << " " << bestIQ << " -> " << iq;
bestErf = erf;
bestIQ = iq;
}
Output("%s",dump.c_str());
iq = clampInt(iq - step,0,255,tmp + " dec step");
}
if (status)
break;
}
if (!status) {
txOld.dcOffsetI = clampInt(decodeDCOffs(true,bestDcI),BRF_TX_DC_OFFSET_MIN,
BRF_TX_DC_OFFSET_MAX);
txOld.dcOffsetQ = clampInt(decodeDCOffs(true,bestDcQ),BRF_TX_DC_OFFSET_MIN,
BRF_TX_DC_OFFSET_MAX);
Debug(m_owner,DebugNote,"TX LO Leakage cal finished I=%d Q=%d [%p]",
txOld.dcOffsetI,txOld.dcOffsetQ,m_owner);
}
break;
}
setRxDcAuto(oldRxDcAuto);
m_txIO.showDcOffsChange--;
Debug(m_owner,DebugAll,"Finalizing TX LO Leakage calibration [%p]",m_owner);
BrfThread::cancelThread(m_sendThread,&m_sendThreadMutex,1000,m_owner,m_owner);
// Restore loopback
internalSetLoopback(oldLoop);
// Restore status
unsigned int restore = DevStatFreq | DevStatVga;
setStatus(txOld,restore | DevStatDc,rxOld,restore);
// Restore TX pattern
setTxPattern(pattern);
if (status == 0) {
Debug(m_owner,DebugInfo,"Calibrated TX LO Leakage I=%d Q=%d [%p]",
m_txIO.dcOffsetI,m_txIO.dcOffsetQ,m_owner);
return 0;
}
e.printf(1024,"TX LO Leakage calibration failed: %s",e.c_str());
return showError(status,e,0,error);
}
static inline void computeMinMax(int& minVal, int& maxVal, int val)
{
if (minVal > val)
minVal = val;
if (maxVal < val)
maxVal = val;
}
static inline void computeRxAdjustPeak(int& p, int val, uint64_t& peakTs, uint64_t& ts)
{
if (p >= val)
return;
p = val;
peakTs = ts;
}
// DC offsets compensation feedback using an exponential moving average
static inline int computeCorrection(int& rxDcAvg, int offs, int avg, int dcOffsMax)
{
rxDcAvg = avg + ((BRF_RX_DC_OFFSET_AVG_DAMPING - 1) * rxDcAvg /
BRF_RX_DC_OFFSET_AVG_DAMPING);
if ((rxDcAvg > dcOffsMax)) {
if (offs < BRF_RX_DC_OFFSET_MAX) {
offs++;
rxDcAvg = 0;
}
}
else if ((rxDcAvg < -dcOffsMax) && (offs > -BRF_RX_DC_OFFSET_MAX)) {
offs--;
rxDcAvg = 0;
}
return offs;
}
// Compute Rx avg values, autocorrect offsets if configured
void BrfLibUsbDevice::computeRx(uint64_t ts)
{
unsigned int dbg = checkDbgInt(m_rxShowDcInfo);
if (!(dbg || m_rxDcAuto))
return;
// Compute averages and peak values
int dcIMin = 32767;
int dcIMax = -32767;
int dcIAvg = 0;
int dcQMin = 32767;
int dcQMax = -32767;
int dcQAvg = 0;
int peak = 0;
uint64_t peakTs = 0;
for (unsigned int i = 0; i < m_rxIO.buffers; i++) {
int16_t* d = m_rxIO.samples(i);
for (int16_t* last = m_rxIO.samplesEOF(i); d != last;) {
int dcI = *d++;
int dcQ = *d++;
dcIAvg += dcI;
dcQAvg += dcQ;
if (!dbg)
continue;
computeMinMax(dcIMin,dcIMax,dcI);
computeMinMax(dcQMin,dcQMax,dcQ);
computeRxAdjustPeak(peak,dcIMax,peakTs,ts);
computeRxAdjustPeak(peak,-dcIMin,peakTs,ts);
computeRxAdjustPeak(peak,dcQMax,peakTs,ts);
computeRxAdjustPeak(peak,-dcQMin,peakTs,ts);
ts++;
}
}
int div = m_rxIO.buffers * m_rxIO.bufSamples;
dcIAvg /= div;
dcQAvg /= div;
if (dbg)
Debug(m_owner,DebugInfo,
"RX DC values min/avg/max I=%d/%d/%d Q=%d/%d/%d peak=%d TS=" FMT64U "[%p]",
dcIMin,dcIAvg,dcIMax,dcQMin,dcQAvg,dcQMax,peak,peakTs,m_owner);
if (!m_rxDcAuto)
return;
int corrI = computeCorrection(m_rxDcAvgI,m_rxIO.dcOffsetI,dcIAvg,m_rxDcOffsetMax);
int corrQ = computeCorrection(m_rxDcAvgQ,m_rxIO.dcOffsetQ,dcQAvg,m_rxDcOffsetMax);
if (corrI == m_rxIO.dcOffsetI && corrQ == m_rxIO.dcOffsetQ)
return;
BRF_TX_SERIALIZE_NONE;
DDebug(m_owner,DebugInfo,"Adjusting Rx DC offsets I=%d Q=%d [%p]",
corrI,corrQ,m_owner);
internalSetCorrectionIQ(false,corrI,corrQ);
}
// Set error string or put a debug message
unsigned int BrfLibUsbDevice::showError(unsigned int code, const char* error,
const char* prefix, String* buf, int level)
{
if (buf)
return setError(code,buf,error,prefix);
String tmp;
setError(code,&tmp,error,prefix);
Debug(m_owner,level,"%s [%p]",tmp.c_str(),m_owner);
return code;
}
void BrfLibUsbDevice::printIOBuffer(bool tx, const char* loc, int index, unsigned int nBufs)
{
BrfDevIO& io = getIO(tx);
if (!nBufs)
nBufs = io.buffers;
for (unsigned int i = 0; i < nBufs; i++) {
if (index >= 0 && index != (int)i)
continue;
String s;
if (io.showBufData) {
io.dumpInt16Samples(s,i);
if (s)
s = "\r\n-----\r\n" + s + "\r\n-----";
}
Output("%s: %s [%s] buffer %u TS=" FMT64U " [%p]%s",
m_owner->debugName(),brfDir(tx),loc,i,io.bufTs(i),m_owner,s.safe());
}
}
// Enable or disable loopback
unsigned int BrfLibUsbDevice::internalSetLoopback(int mode, String* error)
{
if (m_loopback == mode)
return 0;
const char* what = lookup(mode,s_loopback);
#if 1
XDebug(m_owner,DebugAll,"internalSetLoopback(%d) '%s' [%p]",mode,what,m_owner);
#else
Debugger d(DebugAll,"BrfLibUsbDevice::internalSetLoopback()"," %d '%s' [%p]",
mode,what,m_owner);
#endif
String e;
unsigned int status = 0;
BrfDevTmpAltSet tmpAltSet(this,status,&e,"Set loopback");
while (status == 0) {
// Disable everything before enabling the loopback
BRF_FUNC_CALL_BREAK(lnaSelect(LmsLnaNone,&e));
BRF_FUNC_CALL_BREAK(paSelect(LmsPaNone,&e));
BRF_FUNC_CALL_BREAK(setLoopbackPath(LoopNone,e));
// Prepare the loopback (enable / disable modules)
switch (mode) {
case LoopFirmware:
status = RadioInterface::OutOfRange;
e = "Not implemented";
break;
case LoopLpfToRxOut:
// Disable RX VGA2 and LPF
BRF_FUNC_CALL_BREAK(internalEnableRxVga(false,false,&e));
BRF_FUNC_CALL_BREAK(internalSetLpf(false,LpfDisabled,&e));
break;
case LoopLpfToVga2:
case LoopVga1ToVga2:
// Disable RX VGA1 and LPF
BRF_FUNC_CALL_BREAK(internalEnableRxVga(false,false,&e));
BRF_FUNC_CALL_BREAK(internalSetLpf(false,LpfDisabled,&e));
break;
case LoopLpfToLpf:
case LoopVga1ToLpf:
// Disable RX VGA1, Enable RX LPF and RX VGA2
BRF_FUNC_CALL_BREAK(internalEnableRxVga(false,true,&e));
BRF_FUNC_CALL_BREAK(internalSetLpf(false,LpfNormal,&e));
BRF_FUNC_CALL_BREAK(internalEnableRxVga(true,false,&e));
break;
case LoopRfLna1:
case LoopRfLna2:
case LoopRfLna3:
// Select PA AUX and disable LNAs
BRF_FUNC_CALL_BREAK(paSelect(LmsPaAux,&e));
BRF_FUNC_CALL_BREAK(lnaEnable(false,&e));
// Enable RX path
BRF_FUNC_CALL_BREAK(internalEnableRxVga(true,true,&e));
BRF_FUNC_CALL_BREAK(internalSetLpf(false,LpfNormal,&e));
BRF_FUNC_CALL_BREAK(internalEnableRxVga(true,false,&e));
// Select output buffer in RX PLL
switch (mode) {
case LoopRfLna1: status = lmsSet(0x25,LmsLna1,0x03,&e); break;
case LoopRfLna2: status = lmsSet(0x25,LmsLna2,0x03,&e); break;
case LoopRfLna3: status = lmsSet(0x25,LmsLna3,0x03,&e); break;
}
if (!status)
status = enableRfLoopback(true,e);
break;
case LoopNone:
BRF_FUNC_CALL_BREAK(restoreFreq(true,&e));
BRF_FUNC_CALL_BREAK(internalEnableRxVga(true,true,&e));
BRF_FUNC_CALL_BREAK(internalSetLpf(false,LpfNormal,&e));
BRF_FUNC_CALL_BREAK(internalEnableRxVga(true,false,&e));
BRF_FUNC_CALL_BREAK(enableRfLoopback(false,e));
BRF_FUNC_CALL_BREAK(lnaEnable(true,&e));
status = restoreFreq(false,&e);
break;
default:
Debug(m_owner,DebugStub,"Loopback: unhandled value %d [%p]",mode,m_owner);
status = setUnkValue(e,"mode " + String(mode));
}
if (!status)
status = setLoopbackPath(mode,e);
break;
}
if (status == 0) {
Debug(m_owner,DebugNote,"Loopback changed '%s' -> '%s' [%p]",
lookup(m_loopback,s_loopback),what,m_owner);
m_loopback = mode;
return 0;
}
if (mode != LoopNone)
e.printf(1024,"Failed to set loopback to %d (%s): %s",
mode,what,e.c_str());
else
e.printf(1024,"Failed to disable loopback: %s",e.c_str());
return showError(status,e,0,error);
}
unsigned int BrfLibUsbDevice::setLoopbackPath(int mode, String& error)
{
const char* what = lookup(mode,s_loopback);
XDebug(m_owner,DebugAll,"setLoopbackPath(%d) '%s' [%p]",mode,what,m_owner);
uint8_t rf = 0;
uint8_t baseband = 0;
String e;
unsigned int status = lmsRead2(0x08,rf,0x46,baseband,&e);
if (status == 0) {
// Stop all loopbacks
rf &= ~0x7f;
baseband &= ~0x0c;
switch (mode) {
case LoopFirmware:
status = RadioInterface::OutOfRange;
e = "Not implemented";
break;
case LoopLpfToRxOut:
rf |= 0x10; // LBEN_OPIN
baseband |= 0x04; // LOOPBBEN[1:0] 1
break;
case LoopLpfToVga2:
rf |= 0x20; // LBEN_VGA2IN
baseband |= 0x04; // LOOPBBEN[1:0] 1
break;
case LoopVga1ToVga2:
rf |= 0x20; // LBEN_VGA2IN
baseband |= 0x08; // LOOPBBEN[1:0] 2
break;
case LoopLpfToLpf:
rf |= 0x40; // LBEN_LPFIN
baseband |= 0x04; // LOOPBBEN[1:0] 1
break;
case LoopVga1ToLpf:
rf |= 0x40; // LBEN_LPFIN
baseband |= 0x08; // LOOPBBEN[1:0] 2
break;
case LoopRfLna1:
rf |= 0x01;
break;
case LoopRfLna2:
rf |= 0x02;
break;
case LoopRfLna3:
rf |= 0x03;
break;
case LoopNone:
break;
default:
Debug(m_owner,DebugStub,"Loopback path set: unhandled value %d [%p]",
mode,m_owner);
status = setUnkValue(e,"mode " + String(mode));
}
if (status == 0)
status = lmsWrite2(0x08,rf,0x46,baseband,&e);
}
if (status == 0)
Debug(m_owner,DebugAll,"Loopback path switches configured for '%s' [%p]",
what,m_owner);
else
error << "Failed to configure path switches - " << e;
return status;
}
unsigned int BrfLibUsbDevice::enableRfLoopback(bool on, String& error)
{
String e;
unsigned int status = lmsChangeMask(0x0b,0x01,on,&e);
if (status == 0) {
Debug(m_owner,DebugAll,"%s RF loopback [%p]",enabledStr(on),m_owner);
return 0;
}
error << "Failed to " << enableStr(on) << " RF loopback -" << e;
return status;
}
void BrfLibUsbDevice::dumpLoopbackStatus()
{
#define BRF_LS_RESULT(name,mask) \
s << "\r\n " << name << ": " << (status ? "ERROR" : tmp.printf("0x%x",data & mask).c_str())
String s;
String tmp;
uint8_t data = 0;
unsigned int status = 0;
// TX Path
s << "\r\nTX PATH:";
status = lmsRead(0x35,data);
BRF_LS_RESULT("BYP_EN_LPF",0x40); // LPF bypass enable
status = lmsRead(0x46,data);
BRF_LS_RESULT("LOOPBBEN[1:0]",0x0c);
status = lmsRead(0x08,data);
BRF_LS_RESULT("LBEN_OPIN",0x10); // enabled: RX VGA2 and RXLPF should be disabled
BRF_LS_RESULT("LBEN_VGA2IN",0x20); // enabled: LPF should be disabled
BRF_LS_RESULT("LBEN_LPFIN",0x40); // enabled: RXTIA should be disabled
BRF_LS_RESULT("LBRFEN (TXMIX)",0x0f); // LNAs should be disabled
status = lmsRead(0x0b,data);
BRF_LS_RESULT("PD[0] (RF loopback)",0x01);
s << "\r\nRX PATH:";
status = lmsRead(0x55,data);
BRF_LS_RESULT("BYP_EN_LPF",0x40); // LPF bypass enable
status = lmsRead(0x09,data);
BRF_LS_RESULT("RXOUT",0x80); // RXOUT switch
Debug(m_owner,DebugAll,"Loopback switches: [%p]\r\n-----%s\r\n-----",
m_owner,s.c_str());
#undef BRF_LS_RESULT
}
unsigned int BrfLibUsbDevice::internalDumpPeripheral(uint8_t dev, uint8_t addr,
uint8_t len, String* buf, uint8_t lineLen)
{
uint8_t data[256];
unsigned int status = 0;
BRF_FUNC_CALL_RET(accessPeripheral(dev,false,addr,data,0,len));
bool outHere = (buf == 0);
String s;
if (!buf)
buf = &s;
if (lineLen) {
String s1, s2;
uint8_t* d = data;
uint8_t a = addr;
uint8_t n = len / lineLen;
for (; n; n--, d += lineLen, a += lineLen)
*buf << "\r\n" << s1.hexify(&a,1) << "\t" << s2.hexify(d,lineLen,' ');
n = len % lineLen;
if (n)
*buf << "\r\n" << s1.hexify(&a,1) << "\t" << s2.hexify(d,n,' ');
}
else
buf->hexify(data,len,' ');
if (outHere)
Output("%s %s status (addr=0x%x):\r\n-----%s\r\n-----",
m_owner->debugName(),s_uartDevName[dev].c_str(),addr,buf->c_str());
return 0;
}
//
// BrfInterface
//
BrfInterface::BrfInterface(const char* name)
: RadioInterface(name),
m_dev(0),
m_txFreqCorr(0)
{
debugChain(&__plugin);
Debug(this,DebugAll,"Interface created [%p]",this);
}
BrfInterface::~BrfInterface()
{
Debug(this,DebugAll,"Interface destroyed [%p]",this);
}
BrfLibUsbDevice* BrfInterface::init()
{
m_dev = BrfLibUsbDevice::create(this);
Debug(this,DebugAll,"Created device (%p) [%p]",m_dev,this);
return m_dev;
}
unsigned int BrfInterface::initialize(const NamedList& params)
{
return m_dev->powerOn();
}
unsigned int BrfInterface::setParams(NamedList& params, bool shareFate)
{
unsigned int code = 0;
NamedList failed("");
#define SETPARAMS_HANDLE_CODE(c) { \
if (c) { \
if (!code || code == Pending) \
code = c; \
failed.addParam(cmd + "_failed",String(c)); \
if (shareFate && c != Pending) \
break; \
} \
}
#define METH_CALL(func) { \
unsigned int c = func(); \
SETPARAMS_HANDLE_CODE(c); \
continue; \
}
#define METH_CALL_1(func,value) { \
unsigned int c = func(value); \
SETPARAMS_HANDLE_CODE(c); \
continue; \
}
#ifdef XDEBUG
String tmp;
params.dump(tmp,"\r\n");
Debug(this,DebugAll,"setParams [%p]\r\n-----\r\n%s\r\n-----",this,tmp.c_str());
#endif
for (ObjList* o = params.paramList()->skipNull(); o; o = o->skipNext()) {
NamedString* ns = static_cast<NamedString*>(o->get());
if (!ns->name().startsWith("cmd:"))
continue;
String cmd = ns->name().substr(4);
if (!cmd)
continue;
if (cmd == YSTRING("setSampleRate"))
METH_CALL_1(setSampleRate,(uint64_t)ns->toInt64());
if (cmd == YSTRING("setFilter"))
METH_CALL_1(setFilter,(uint64_t)ns->toInt64());
if (cmd == "autocalDCOffsets")
METH_CALL(autocalDCOffsets);
Debug(this,DebugNote,"setParams: unhandled cmd '%s' [%p]",cmd.c_str(),this);
SETPARAMS_HANDLE_CODE(NotSupported);
}
#undef SETPARAMS_HANDLE_CODE
#undef METH_CALL
#undef METH_CALL_1
if (code)
params.copyParams(failed);
#ifdef XDEBUG
tmp.clear();
params.dump(tmp,"\r\n");
Debug(this,DebugAll,"setParams [%p]\r\n-----\r\n%s\r\n-----",this,tmp.c_str());
#endif
return code;
}
unsigned int BrfInterface::send(uint64_t when, float* samples, unsigned size,
float* powerScale)
{
return m_dev->syncTx(when,samples,size,powerScale);
}
unsigned int BrfInterface::recv(uint64_t& when, float* samples, unsigned int& size)
{
return m_dev->syncRx(when,samples,size);
}
unsigned int BrfInterface::setFrequency(uint64_t hz, bool tx)
{
XDebug(this,DebugAll,"BrfInterface::setFrequency(" FMT64U ",%s) [%p]",
hz,brfDir(tx),this);
if (hz > 0xffffffff) {
Debug(this,DebugNote,
"Failed to set %s frequency " FMT64U ": out of range [%p]",
brfDir(tx),hz,this);
return OutOfRange;
}
uint32_t freq = (uint32_t)hz;
unsigned int status = m_dev->setFrequency(freq,tx);
if (status)
return status;
uint32_t tmp = 0;
status = m_dev->getFrequency(tmp,tx);
if (status)
return status;
if (tmp != freq) {
Debug(this,DebugNote,"Failed to set %s frequency requested=%u read=%u [%p]",
brfDir(tx),freq,tmp,this);
return NotExact;
}
return 0;
}
unsigned int BrfInterface::getFrequency(uint64_t& hz, bool tx) const
{
uint32_t freq = 0;
unsigned int status = m_dev->getFrequency(freq,tx);
if (status == 0)
hz = freq;
return status;
}
unsigned int BrfInterface::setSampleRate(uint64_t hz)
{
XDebug(this,DebugAll,"BrfInterface::setSampleRate(" FMT64U ") [%p]",hz,this);
uint32_t srate = (uint32_t)hz;
unsigned int status = m_dev->setSamplerate(srate,true);
if (status)
return status;
status = m_dev->setSamplerate(srate,false);
if (status)
return status;
uint32_t tmp = 0;
status = m_dev->getSamplerate(tmp,true);
if (status)
return status;
if (tmp != srate) {
Debug(this,DebugNote,"Failed to set TX samplerate requested=%u read=%u [%p]",
srate,tmp,this);
return NotExact;
}
status = m_dev->getSamplerate(tmp,false);
if (status)
return status;
if (tmp != srate) {
Debug(this,DebugNote,"Failed to set RX samplerate requested=%u read=%u [%p]",
srate,tmp,this);
return NotExact;
}
return NoError;
}
unsigned int BrfInterface::getSampleRate(uint64_t& hz) const
{
uint32_t srate = 0;
unsigned int status = m_dev->getSamplerate(srate,true);
if (status == 0)
hz = srate;
return status;
}
unsigned int BrfInterface::setFilter(uint64_t hz)
{
XDebug(this,DebugAll,"BrfInterface::setFilter(" FMT64U ") [%p]",hz,this);
if (hz > 0xffffffff) {
Debug(this,DebugNote,
"Failed to set filter " FMT64U ": out of range [%p]",
hz,this);
return OutOfRange;
}
uint32_t band = hz;
unsigned int status = m_dev->setLpfBandwidth(band,true);
if (status)
return status;
status = m_dev->setLpfBandwidth(band,false);
if (status)
return status;
uint32_t tmp = 0;
status = m_dev->getLpfBandwidth(tmp,true);
if (status)
return status;
if (tmp != band){
Debug(this,DebugNote,"Failed to set TX filter band requested=%u read=%u [%p]",
band,tmp,this);
return NotExact;
}
status = m_dev->getLpfBandwidth(tmp,false);
if (status)
return status;
if (tmp != band){
Debug(this,DebugNote,"Failed to set RX filter band requested=%u read=%u [%p]",
band,tmp,this);
return NotExact;
}
return NoError;
}
unsigned int BrfInterface::getFilterWidth(uint64_t& hz) const
{
uint32_t band = 0;
unsigned int status = m_dev->getLpfBandwidth(band,true);
if (status == 0)
hz = band;
return status;
}
unsigned int BrfInterface::setRxGain(int val, unsigned port, bool preMixer)
{
XDebug(this,DebugAll,"BrfInterface::setRxGain(%d,%u,VGA%c) [%p]",
val,port,mixer(preMixer),this);
unsigned int status = m_dev->enableRxVga(true,preMixer);
if (status)
return status;
status = m_dev->setRxVga(val,preMixer);
if (status)
return status;
int tmp = 0;
status = m_dev->getRxVga(tmp,preMixer);
if (status)
return status;
if (tmp == val)
return NoError;
Debug(this,DebugNote,"Failed to set RX VGA%c requested=%d read=%d [%p]",
mixer(preMixer),val,tmp,this);
return NotExact;
}
unsigned int BrfInterface::setTxGain(int val, unsigned port, bool preMixer)
{
XDebug(this,DebugAll,"BrfInterface::setTxGain(%d,%u,VGA%c) [%p]",
val,port,mixer(preMixer),this);
unsigned int status = m_dev->setTxVga(val,preMixer);
if (status)
return status;
int tmp = 0;
status = m_dev->getTxVga(tmp,preMixer);
if (status)
return status;
if (tmp == val)
return NoError;
Debug(this,DebugNote,"Failed to set TX VGA%c requested=%d read=%d [%p]",
mixer(preMixer),val,tmp,this);
return NotExact;
}
// Calibration. Automatic tx/rx gain setting
// Set pre and post mixer value
unsigned int BrfInterface::setGain(bool tx, int val, unsigned int port,
int* newVal) const
{
return m_dev->setGain(tx,val,newVal);
}
unsigned int BrfInterface::setOffsetCal(bool tx, bool Q, int val, unsigned port) const
{
return m_dev->setDcOffset(tx,!Q,val);
}
unsigned int BrfInterface::getOffsetCal(bool tx, bool Q, int& val, unsigned port) const
{
int16_t tmp = 0;
unsigned int status = m_dev->getDcOffset(tx,!Q,tmp);
if (status == 0)
val = tmp;
return status;
}
unsigned int BrfInterface::getPhaseCal(bool tx, int& val, unsigned port) const
{
int16_t tmp = 0;
unsigned int status = m_dev->getFpgaCorr(tx,BrfLibUsbDevice::CorrFpgaPHASE,tmp);
if (status == 0)
val = tmp;
return status;
}
unsigned int BrfInterface::setPhaseCal(bool tx, int val, unsigned port)
{
return m_dev->setFpgaCorr(tx,BrfLibUsbDevice::CorrFpgaPHASE,val);
}
unsigned int BrfInterface::setFreqCal(int val)
{
uint64_t freq = 0;
unsigned int status = getTxFreq(freq);
if (status != NoError)
return status;
freq += val;
status = setTxFreq(freq);
if (status == NoError)
m_txFreqCorr = freq;
else
Debug(this,DebugNote,"Failed to set calibrated frequency %d [%p]",val,this);
return status;
}
void BrfInterface::destroyed()
{
Debug(this,DebugAll,"Destroying device=(%p) [%p]",m_dev,this);
Lock lck(__plugin);
__plugin.m_ifaces.remove(this,false);
lck.drop();
TelEngine::destruct(m_dev);
RadioInterface::destroyed();
}
//
// BrfModule
//
BrfModule::BrfModule()
: Module("bladerf","misc",true),
m_ifaceId(0)
{
String tmp;
const libusb_version* ver = ::libusb_get_version();
tmp.printf(" using libusb %u.%u.%u.%u",ver->major,ver->minor,ver->micro,ver->nano);
if (!TelEngine::null(ver->rc))
tmp << " rc='" << ver->rc << "'";
if (!TelEngine::null(ver->describe))
tmp << " desc='" << ver->describe << "'";
Output("Loaded module BladeRF%s",tmp.safe());
}
BrfModule::~BrfModule()
{
Output("Unloading module BladeRF");
if (m_ifaces.skipNull())
Debug(this,DebugWarn,"Exiting with %u interface(s) in list!!!",m_ifaces.count());
else if (s_usbContextInit) {
::libusb_exit(0);
Debug(this,DebugAll,"Cleared libusb context");
}
}
bool BrfModule::findIfaceByDevice(RefPointer<BrfInterface>& iface, void* dev)
{
if (!dev)
return false;
Lock lck(this);
for (ObjList* o = m_ifaces.skipNull(); o; o = o->skipNext()) {
iface = static_cast<BrfInterface*>(o->get());
if (iface && iface->isDevice(dev))
return true;
iface = 0;
}
return false;
}
void BrfModule::initialize()
{
Output("Initializing module BladeRF");
lock();
loadCfg();
NamedList gen(*s_cfg.createSection(YSTRING("general")));
NamedList lusb(*s_cfg.createSection(YSTRING("libusb")));
unlock();
if (!relayInstalled(RadioCreate)) {
setup();
installRelay(Halt);
installRelay(Control);
installRelay(RadioCreate,"radio.create",gen.getIntValue("priority",90));
}
lusbSetDebugLevel();
s_lusbCtrlTransferTout = lusb.getIntValue(YSTRING("ctrl_transfer_timeout"),
LUSB_CTRL_TIMEOUT,200,2000);
s_lusbBulkTransferTout = lusb.getIntValue(YSTRING("bulk_transfer_timeout"),
LUSB_BULK_TIMEOUT,200,2000);
setDebugPeripheral(gen[YSTRING("peripheraldebug")]);
setSampleEnergize(gen[YSTRING("sampleenergize")]);
}
bool BrfModule::received(Message& msg, int id)
{
if (id == RadioCreate) {
// Override parameters from received params
const String& what = msg[YSTRING("radio_driver")];
if (what && what != YSTRING("bladerf"))
return false;
BrfInterface* ifc = createIface(msg);
if (ifc)
msg.setParam(new NamedPointer("interface",ifc,name()));
else
msg.setParam(YSTRING("error"),"failure");
return ifc != 0;
}
if (id == Control) {
const String& comp = msg[YSTRING("component")];
RefPointer<BrfInterface> ifc;
if (comp == name() || findIface(ifc,comp))
return onCmdControl(ifc,msg);
return false;
}
else if (id == Halt) {
NamedList dummy("");
test("stop",dummy);
}
return Module::received(msg,id);
}
void BrfModule::statusModule(String& str)
{
Module::statusModule(str);
}
void BrfModule::statusParams(String& str)
{
Module::statusParams(str);
Lock lck(this);
str.append("ifaces=",",") << m_ifaces.count();
}
void BrfModule::statusDetail(String& str)
{
Module::statusDetail(str);
}
bool BrfModule::commandComplete(Message& msg, const String& partLine,
const String& partWord)
{
if (partLine == YSTRING("control")) {
itemComplete(msg.retValue(),name(),partWord);
completeIfaces(msg.retValue(),partWord);
return false;
}
String tmp = partLine;
if (tmp.startSkip("control")) {
if (tmp == name())
return completeStrList(msg.retValue(),partWord,s_modCmds);
RefPointer<BrfInterface> ifc;
if (findIface(ifc,tmp))
return completeStrList(msg.retValue(),partWord,s_ifcCmds);
}
return Module::commandComplete(msg,partLine,partWord);
}
BrfInterface* BrfModule::createIface(const NamedList& params)
{
// Debugger d(debugLevel(),"BrfModule::createIface()");
Lock lck(this);
NamedList p(*s_cfg.createSection("general"));
// Allow using a different interface profile
// Override general parameters
const String& profile = params[YSTRING("profile")];
NamedList* sect = 0;
if (profile && profile != YSTRING("general"))
sect = s_cfg.getSection(profile);
if (sect) {
for (const ObjList* o = sect->paramList()->skipNull(); o; o = o->skipNext()) {
const NamedString* ns = static_cast<const NamedString*>(o->get());
p.setParam(ns->name(),*ns);
}
}
// Override parameters from received params
String prefix = params.getValue(YSTRING("radio_params_prefix"),"radio.");
if (prefix)
p.copySubParams(params,prefix,true,true);
BrfInterface* ifc = new BrfInterface(name() + "/" + String(++m_ifaceId));
m_ifaces.append(ifc)->setDelete(false);
BrfLibUsbDevice* dev = ifc->init();
lck.drop();
if (dev && dev->open(p))
return ifc;
TelEngine::destruct(ifc);
return 0;
}
void BrfModule::completeIfaces(String& dest, const String& partWord)
{
Lock lck(this);
for (ObjList* o = m_ifaces.skipNull(); o; o = o->skipNext()) {
RefPointer<BrfInterface> ifc = static_cast<BrfInterface*>(o->get());
if (ifc)
itemComplete(dest,ifc->toString(),partWord);
}
}
bool BrfModule::onCmdControl(BrfInterface* ifc, Message& msg)
{
static const char* s_help =
"\r\ncontrol ifc_name vgagain tx=boolean vga={1|2} [gain=]"
"\r\n Set or retrieve TX/RX VGA mixer gain"
"\r\ncontrol ifc_name correction tx=boolean corr={dc-i|dc-q|fpga-gain|fpga-phase} [value=]"
"\r\n Set or retrieve TX/RX DC I/Q or FPGA GAIN/PHASE correction"
"\r\ncontrol ifc_name lmswrite addr= value= [resetmask=]"
"\r\n Set LMS value at given address. Use reset mask for partial register set"
"\r\ncontrol ifc_name bufoutput tx=boolean [count=] [nodata=boolean]"
"\r\n Set TX/RX buffer output"
"\r\ncontrol ifc_name rxdcoutput [count=]"
"\r\n Set interface RX DC info output"
"\r\ncontrol ifc_name txpattern [pattern=]"
"\r\n Set interface TX pattern"
"\r\ncontrol ifc_name show [info=status|statistics|boardstatus|peripheral] [peripheral=all|list(lms,gpio,vctcxo,si5338)] [addr=] [len=]"
"\r\n Verbose output various interface info"
"\r\ncontrol module_name test oper=start|stop|pause|resume|exec"
"\r\n Test commands"
"\r\ncontrol module_name help"
"\r\n Display control commands help";
const String& cmd = msg[YSTRING("operation")];
// Module commands
if (!ifc) {
if (cmd == YSTRING("test"))
return test(msg[YSTRING("oper")],msg);
if (cmd == YSTRING("help")) {
msg.retValue() << s_help;
return true;
}
return false;
}
// Interface commands
if (cmd == YSTRING("vgagain"))
return onCmdGain(ifc,msg);
if (cmd == YSTRING("correction"))
return onCmdCorrection(ifc,msg);
if (cmd == YSTRING("lmswrite"))
return onCmdLmsWrite(ifc,msg);
if (cmd == YSTRING("bufoutput"))
return onCmdBufOutput(ifc,msg);
if (cmd == YSTRING("rxdcoutput")) {
if (!ifc->device())
return retMsgError(msg,"No device");
ifc->device()->showRxDCInfo(msg.getIntValue(YSTRING("count")));
return true;
}
if (cmd == YSTRING("txpattern")) {
if (!ifc->device())
return retMsgError(msg,"No device");
ifc->device()->setTxPattern(msg[YSTRING("pattern")]);
return true;
}
if (cmd == YSTRING("show"))
return onCmdShow(ifc,msg);
return true;
}
bool BrfModule::onCmdStatus(String& retVal, String& line)
{
static const String s_devInfo("withdevinfo");
String ifcName;
bool devInfo = false;
if (line && getFirstStr(ifcName,line)) {
if (ifcName == s_devInfo) {
devInfo = true;
ifcName.clear();
}
else if (line) {
String tmp;
devInfo = getFirstStr(tmp,line) && (tmp == s_devInfo);
}
}
String extra;
String stats;
String info;
if (ifcName) {
stats << "interface=" << ifcName;
RefPointer<BrfInterface> ifc;
if (findIface(ifc,ifcName) && ifc->device());
ifc->device()->dumpDev(info,devInfo,true,",",true,true);
}
else {
unsigned int n = 0;
lock();
ListIterator iter(m_ifaces);
for (GenObject* gen = 0; (gen = iter.get()) != 0; ) {
RefPointer<BrfInterface> ifc = static_cast<BrfInterface*>(gen);
if (!ifc)
continue;
unlock();
n++;
BrfLibUsbDevice* dev = ifc->device();
if (dev) {
String tmp;
dev->dumpDev(tmp,devInfo,true,",",true,false);
info.append(ifc->toString(),",") << "=" << tmp;
}
lock();
}
unlock();
extra << "format=RxVGA1|RxVGA2|RxDCCorrI|RxDCCorrQ|TxVGA1|TxVGA2|"
"RxFreq|TxFreq|RxSampRate|TxSampRate|RxLpfBw|TxLpfBw|RxRF|TxRF";
if (devInfo)
extra << "|Address|Serial|Speed|Firmware|FPGA";
stats << "count=" << n;
}
retVal << "module=" << name();
retVal.append(extra,",") << ";";
if (stats)
retVal << stats << ";";
retVal << info;
retVal << "\r\n";
return true;
}
#define BRF_GET_BOOL_PARAM(bDest,param) \
const String& tmpGetBParamStr = msg[YSTRING(param)]; \
if (!tmpGetBParamStr.isBoolean()) \
return retParamError(msg,param); \
bDest = tmpGetBParamStr.toBoolean();
// control ifc_name vgagain tx=boolean mixer={1|2} [value=]
bool BrfModule::onCmdGain(BrfInterface* ifc, Message& msg)
{
if (!ifc->device())
return retMsgError(msg,"No device");
bool tx = true;
BRF_GET_BOOL_PARAM(tx,"tx");
const String& what = msg[YSTRING("vga")];
bool preMixer = (what == YSTRING("1"));
if (!preMixer && what != YSTRING("2"))
return retParamError(msg,"vga");
unsigned int code = 0;
int crt = 0;
const String& value = msg[YSTRING("gain")];
if (value)
code = tx ? ifc->device()->setTxVga(value.toInteger(),preMixer) :
ifc->device()->setRxVga(value.toInteger(),preMixer);
if (!code)
code = tx ? ifc->device()->getTxVga(crt,preMixer) :
ifc->device()->getRxVga(crt,preMixer);
if (code)
return retValFailure(msg,code);
msg.setParam(YSTRING("value"),String(crt));
msg.retValue() = crt;
return true;
}
// control ifc_name correction tx=boolean corr={dc-i/dc-q/fpga-gain/fpga-phase} [value=]
bool BrfModule::onCmdCorrection(BrfInterface* ifc, Message& msg)
{
if (!ifc->device())
return retMsgError(msg,"No device");
bool tx = true;
BRF_GET_BOOL_PARAM(tx,"tx");
const String& corr = msg[YSTRING("corr")];
if (!corr)
return retParamError(msg,"corr");
const String& value = msg[YSTRING("value")];
unsigned int code = 0;
int16_t crt = 0;
bool i = (corr == YSTRING("dc-i"));
if (i || (corr == YSTRING("dc-q"))) {
if (value)
code = ifc->device()->setDcOffset(tx,i,value.toInteger());
if (!code)
code = ifc->device()->getDcOffset(tx,i,crt);
}
else {
int c = (corr == YSTRING("fpga-phase")) ? BrfLibUsbDevice::CorrFpgaPHASE:
BrfLibUsbDevice::CorrFpgaGAIN;
if (c == BrfLibUsbDevice::CorrFpgaGAIN && corr != YSTRING("fpga-gain"))
return retParamError(msg,"corr");
if (value)
code = ifc->device()->setFpgaCorr(tx,c,value.toInteger());
if (!code)
code = ifc->device()->getFpgaCorr(tx,c,crt);
}
if (code)
return retValFailure(msg,code);
msg.setParam(YSTRING("value"),String(crt));
msg.retValue() = crt;
return true;
}
// control ifc_name lmswrite addr= value= [resetmask=]
bool BrfModule::onCmdLmsWrite(BrfInterface* ifc, Message& msg)
{
if (!ifc->device())
return retMsgError(msg,"No device");
int addr = msg.getIntValue(YSTRING("addr"),-1);
if (addr < 0 || addr > 127)
return retParamError(msg,"addr");
int val = msg.getIntValue(YSTRING("value"),-1);
if (val < 0 || val > 255)
return retParamError(msg,"value");
const String& rstStr = msg[YSTRING("resetmask")];
unsigned int code = 0;
if (rstStr) {
uint8_t rst = (uint8_t)rstStr.toInteger();
code = ifc->device()->writeLMS(addr,val,&rst);
}
else
code = ifc->device()->writeLMS(addr,val);
if (!code)
return true;
return retValFailure(msg,code);
}
// control ifc_name bufoutput tx=boolean [count=value] [nodata=false]
bool BrfModule::onCmdBufOutput(BrfInterface* ifc, Message& msg)
{
if (!ifc->device())
return retMsgError(msg,"No device");
bool tx = true;
BRF_GET_BOOL_PARAM(tx,"tx");
ifc->device()->showBuf(tx,msg.getIntValue(YSTRING("count")),
msg.getBoolValue(YSTRING("nodata")));
return true;
}
// control ifc_name show [info=status|statistics|boardstatus|peripheral] [peripheral=all|list(lms,gpio,vctcxo,si5338)] [addr=] [len=]
bool BrfModule::onCmdShow(BrfInterface* ifc, Message& msg)
{
if (!ifc->device())
return retMsgError(msg,"No device");
String info = msg.getValue(YSTRING("info"),"status");
String str;
if (info == YSTRING("status"))
ifc->device()->dumpDev(str,true,true,"\r\n");
else if (info == YSTRING("boardstatus"))
ifc->device()->dumpBoardStatus(str,"\r\n");
else if (info == YSTRING("statistics"))
ifc->device()->dumpStats(str,"\r\n");
else if (info == YSTRING("peripheral")) {
String what = msg.getValue(YSTRING("peripheral"),"all");
if (what == YSTRING("all"))
what = "lms,gpio,vctcxo,si5338";
uint8_t addr = (uint8_t)msg.getIntValue("addr",0,0);
uint8_t len = (uint8_t)msg.getIntValue("len",128,1);
ObjList* lst = what.split(',');
for (ObjList* o = lst->skipNull(); o; o = o->skipNext()) {
String* s = static_cast<String*>(o->get());
s->toUpper();
for (uint8_t i = 0; i < BrfLibUsbDevice::UartDevCount; i++) {
if (*s != s_uartDevName[i])
continue;
String tmp;
ifc->device()->dumpPeripheral(i,addr,len,&tmp);
if (tmp)
str.append(s_uartDevName[i],"\r\n") << tmp;
break;
}
}
TelEngine::destruct(lst);
}
else
return retParamError(msg,"info");
if (str)
Output("Interface '%s' info=%s [%p]\r\n-----\r\n%s\r\n-----",
ifc->debugName(),info.c_str(),ifc,str.c_str());
return true;
}
// control module_name test oper={start|stop|.....} params...
bool BrfModule::test(const String& cmd, NamedList& list)
{
static bool s_exec = false;
lock();
while (s_exec) {
unlock();
Thread::idle();
if (Thread::check(false))
return false;
lock();
}
s_exec = true;
RefPointer<BrfTest> crt = m_test;
unlock();
bool ok = true;
bool start = (cmd == YSTRING("start"));
if (start || cmd == YSTRING("stop")) {
const String& name = start ? list[YSTRING("name")] : String::empty();
if (start && !name) {
s_exec = false;
return retParamError(list,"name");
}
lock();
m_test = 0;
unlock();
// Start / Stop
bool haveTest = (crt != 0);
if (crt) {
crt->stop();
crt = 0;
}
if (start && !Thread::check(false)) {
Lock lck(this);
// Reload config
loadCfg();
NamedList* sect = s_cfg.getSection(name);
ok = (sect != 0);
if (ok) {
NamedList p(*s_cfg.createSection(sect->getValue("init_section",*sect)));
NamedList cmds(*s_cfg.createSection(sect->getValue("cmds_section",*sect)));
NamedList devOpen(*s_cfg.createSection(sect->getValue("dev_section","general")));
BrfTest* ifc = new BrfTest(this->name() + "/" + name,p,devOpen,cmds);
m_ifaces.append(ifc)->setDelete(false);
BrfLibUsbDevice* dev = ifc->init();
lck.drop();
ok = dev && ifc->start();
if (ok)
setTest(true,ifc);
TelEngine::destruct(ifc);
Debug(this,ok ? DebugInfo : DebugNote,"Test '%s' %s",
name.c_str(),(ok ? "started" : "failed to start"));
}
else
Debug(this,DebugConf,"Can't test '%s': no config section",name.c_str());
}
else if (!start && haveTest)
Debug(this,DebugInfo,"Test stopped");
}
else {
String error;
if (crt) {
if (cmd == YSTRING("pause"))
crt->pause();
else if (cmd == YSTRING("resume"))
crt->resume();
else if (cmd == YSTRING("exec")) {
const String& c = list[YSTRING("command")];
if (c)
ok = crt->execute(c,list[YSTRING("value")],error,true);
else {
ok = false;
error = "Empty command";
}
}
else {
ok = false;
error = "Unknown test command";
}
}
else
ok = false;
if (error)
list.setParam("error",error);
}
s_exec = false;
return ok;
}
// Format: [tx|rx|both] [all|list(gpio,lms,vctcxo,si5338)]
void BrfModule::setDebugPeripheral(const String& value)
{
String line = value;
String s;
uint8_t val = 0;
if (getFirstStr(s,line)) {
if ((s == YSTRING("tx")))
val = 0x80;
else if (s == YSTRING("rx"))
val = 0x40;
else
val = 0xc0;
if (val != 0xc0 || s == YSTRING("both"))
getFirstStr(s,line);
uint8_t mask = 0;
if (s && s != YSTRING("all")) {
ObjList* lst = s.split(',');
for (ObjList* o = lst->skipNull(); o; o = o->skipNext()) {
String* str = static_cast<String*>(o->get());
str->toUpper();
uint8_t i = 0;
while (i < BrfLibUsbDevice::UartDevCount && s_uartDevName[i] != *str)
i++;
if (i < BrfLibUsbDevice::UartDevCount)
mask |= (uint8_t)(1 << i);
}
TelEngine::destruct(lst);
}
val |= (mask ? mask : 0x0f);
}
if (val == s_debugPeripheral)
return;
s_debugPeripheral = val;
String state;
if ((val & 0x80) != 0)
state.append("TX","/");
if ((val & 0x40) != 0)
state.append("RX","/");
String dev;
if (state)
for (uint8_t i = 0; i < BrfLibUsbDevice::UartDevCount; i++) {
if ((val & (1 << i)) != 0)
dev.append(s_uartDevName[i],",");
}
String tmp;
tmp << "Peripheral access debug is " << state.safe("disabled");
tmp.append(dev," ");
Debug(this,DebugInfo,"%s",tmp.c_str());
}
void BrfModule::setSampleEnergize(const String& value)
{
Lock lck(this);
int val = value.toInteger(2047);
if (val == (int)s_sampleEnergize)
return;
if (val < 1 || val > 2047) {
Debug(this,DebugConf,"Invalid sampleenergize=%s",value.c_str());
return;
}
Debug(this,DebugInfo,"sampleenergize changed %u -> %d",s_sampleEnergize,val);
s_sampleEnergize = val;
// Notify devices
ListIterator iter(m_ifaces);
for (GenObject* gen = 0; (gen = iter.get()) != 0; ) {
RefPointer<BrfInterface> ifc = static_cast<BrfInterface*>(gen);
if (!ifc)
continue;
lck.drop();
// Just set flags used by the device to update data using sample energize value
if (ifc->device()) {
ifc->device()->m_txPowerBalanceChanged = true;
ifc->device()->m_txPatternChanged = true;
}
ifc = 0;
lck.acquire(this);
}
}
//
// BrfThread
//
// Start this thread. Set destination pointer on success. Delete object on failure
BrfThread* BrfThread::start()
{
if (startup())
return this;
Debug(m_iface,DebugNote,"Failed to start worker '%s' [%p]",name(),(void*)m_iface);
delete this;
return 0;
}
void BrfThread::cancelThread(BrfThread*& th, Mutex* mtx, unsigned int waitMs,
DebugEnabler* dbg, void* ptr)
{
if (!th)
return;
Lock lck(mtx);
if (!th)
return;
//Debugger d(DebugAll,"BrfThread::cancelThread()"," [%p]",ptr);
th->cancel();
lck.drop();
unsigned int intervals = (waitMs / Thread::idleMsec()) + 1;
bool cancelled = Thread::check(false);
while (th && intervals-- && (cancelled || !Thread::check(false)))
Thread::idle();
if (!th)
return;
lck.acquire(mtx);
if (!th)
return;
Debug(dbg,DebugWarn,"Hard cancelling (%p) '%s' worker [%p]",th,th->name(),ptr);
th->cancel(true);
th = 0;
}
void BrfThread::run()
{
BrfInterface* i = m_iface ? (BrfInterface*)m_iface :
(m_device ? m_device->owner() : 0);
if (!i)
return;
Debug(i,DebugAll,"Worker (%p) '%s' started [%p]",this,name(),i);
BrfTest* test = YOBJECT(BrfTest,m_iface);
if (test)
test->run();
else if (m_device) {
unsigned int samples = m_device->bufSamples(true);
DataBlock buf(0,samplesf2bytes(samples));
float* b = (float*)buf.data(0);
uint64_t ts = 0;
m_device->internalGetTimestamp(true,ts);
Debug(m_device->owner(),DebugAll,"Start sending at ts=" FMT64U " [%p]",
ts,m_device->owner());
unsigned int status = 0;
while (!status) {
BRF_FUNC_CALL_BREAK(m_device->send(ts,b,samples));
BRF_FUNC_CALL_BREAK(m_device->cancelled());
ts += samples;
}
}
notify();
}
void BrfThread::notify()
{
RefPointer<BrfInterface> ifc = m_iface;
m_iface = 0;
BrfLibUsbDevice* dev = m_device;
m_device = 0;
if (!(ifc || dev))
return;
BrfInterface* i = ifc ? (BrfInterface*)ifc : dev->owner();
bool ok = (m_name == Thread::currentName());
Debug(i,ok ? DebugAll : DebugWarn,"Worker (%p) '%s' terminated [%p]",this,name(),i);
BrfTest* test = YOBJECT(BrfTest,ifc);
if (test) {
if (test->workerTerminated(this))
test->stop();
}
else if (dev) {
Lock lck(dev->m_sendThreadMutex);
if (dev->m_sendThread == this)
dev->m_sendThread = 0;
}
ifc = 0;
}
//
// BrfTest
//
BrfTest::BrfTest(const char* name, const NamedList& params, const NamedList& devOpen,
const NamedList& cmds)
: BrfInterface(name), Mutex(false,"BrfTest"),
m_state(Idle),
m_pause(false),
m_pauseSend(false),
m_pauseRead(false),
m_sendBufCount(0),
m_sendOnly(false),
m_worker(0),
m_sentSamples(0),
m_sendTs(0),
m_sendBuf(0),
m_sendBufSamples(0),
m_readTs(0),
m_skippedBuffs(0),
m_params(params),
m_devOpen(devOpen),
m_cmds(cmds)
{
}
bool BrfTest::start()
{
Lock lck(this);
if (m_state == Running)
return true;
if (m_state != Idle)
return false;
Debug(this,DebugInfo,"Starting ... [%p]",this);
String e;
while (true) {
m_worker = (new BrfThread(this,"BrfTest"))->start();
if (!m_worker)
e = "Failed to start worker(s)";
break;
}
if (e)
Debug(this,DebugNote,"Start failure: %s [%p]",e.c_str(),this);
else
m_state = Running;
return m_state == Running;
}
void BrfTest::stop()
{
if (device())
device()->exiting(true);
__plugin.setTest(false,this);
if (m_state == Stopping || m_state == Idle)
return;
Lock lck(this);
if (m_state == Stopping || m_state == Idle)
return;
m_state = Stopping;
Debug(this,DebugInfo,"Stopping ... [%p]",this);
lck.drop();
BrfThread::cancelThread(m_worker,this,1000,this,this);
lck.acquire(this);
m_state = Idle;
Debug(this,DebugInfo,"Stopped [%p]",this);
}
bool BrfTest::execute(const String& cmd, const String& param, String& error,
bool fatal, const NamedList* params)
{
XDebug(this,DebugAll,"execute(%s,%s) [%p]",cmd.c_str(),param.c_str(),this);
String e;
unsigned int c = RadioInterface::Failure;
if (cmd == YSTRING("loopback"))
c = device()->setLoopback(param);
else if (cmd == YSTRING("samplerate"))
c = setSampleRate(param.toInteger());
else if (cmd == YSTRING("filter"))
c = setFilter(param.toInteger());
else if (cmd == YSTRING("txfrequency"))
c = setFrequency(param.toInteger(),true);
else if (cmd == YSTRING("rxfrequency"))
c = setFrequency(param.toInteger(),false);
else if (cmd == YSTRING("calibrate"))
c = device()->calibrate();
else if (cmd == YSTRING("powerbalance")) {
if (param)
c = device()->setTxIQBalance((float)param.toDouble(-2));
else
e = "Missing required parameter";
}
else if (cmd == YSTRING("txpattern"))
c = device()->setTxPattern(param);
else {
Debug(this,DebugNote,"Unhandled command '%s' [%p]",cmd.c_str(),this);
return true;
}
if (c == 0 || !fatal)
return true;
error.printf("'%s' failed with %u '%s'",cmd.c_str(),c,errorName(c));
error.append(e," - ");
return false;
}
bool BrfTest::runInit()
{
String e;
int level = DebugNote;
while (true) {
m_sendBufCount = m_params.getIntValue("send_count",0,0);
m_sendOnly = m_params.getBoolValue("send_only",true);
// Open device
if (!device()->open(m_devOpen)) {
e = "Device open failed";
break;
}
if (!execute(m_cmds,"init:",e))
break;
if (initialize()) {
e = "Initialize failure";
break;
}
if (!execute(m_cmds,"cmd:",e))
break;
m_sendBufSamples = m_params.getIntValue("send_samples",0,0,5000);
if (!m_sendBufSamples)
m_sendBufSamples = device()->bufSamples(true);
if (!m_sendBufSamples) {
e = "Send buf samples is 0";
break;
}
m_sendBufData.resize(samplesf2bytes(m_sendBufSamples));
m_sendBuf = (float*)m_sendBufData.data(0);
break;
}
if (e)
Debug(this,level,"Init failure: %s [%p]",e.c_str(),this);
return e.null();
}
bool BrfTest::execute(const NamedList& cmds, const char* prefix, String& error)
{
for (const ObjList* o = cmds.paramList()->skipNull(); o; o = o->skipNext()) {
const NamedString* ns = static_cast<const NamedString*>(o->get());
String s = ns->name();
if (s.startSkip(prefix,false) &&
!execute(s,*ns,error,cmds.getBoolValue(s + "_fatal",true),&cmds))
return false;
}
return true;
}
void BrfTest::run()
{
Debug(this,DebugInfo,"Running [%p]",this);
if (!runInit())
return;
if (m_sendOnly)
runSendOnly();
else
runSendRecv();
}
void BrfTest::runSendOnly()
{
Debug(this,DebugInfo,"Running send only test (send %u samples) [%p]",
m_sendBufSamples,this);
m_sendTs = 0;
m_sentSamples = 0;
while (!Thread::check(false) && write())
;
dumpStats();
}
void BrfTest::runSendRecv()
{
String s;
s << "\r\nsend_count=" << m_sendBufCount;
Debug(this,DebugInfo,"Running send/recv test [%p]\r\n-----%s\r\n-----",this,s.c_str());
m_sentSamples = 0;
// Set RX buf
// Read multiple of device RX samples, at least sent samples
unsigned int rxSamples = device()->bufSamples(false);
if (rxSamples < m_sendBufSamples) {
unsigned int rest = m_sendBufSamples % rxSamples;
rxSamples = m_sendBufSamples + rxSamples - rest;
}
resetBufs(rxSamples);
while (!Thread::check(false) && write() && read()) {
if (m_sendBufCount) {
m_sendBufCount--;
if (!m_sendBufCount)
break;
}
}
dumpStats();
}
void BrfTest::dumpStats()
{
String s;
s << "\r\nsent=" << m_sentSamples << " samples";
Debug(this,DebugInfo,"Terminated [%p]\r\n-----%s\r\n-----",this,s.c_str());
}
bool BrfTest::workerTerminated(BrfThread* th)
{
Lock lck(this);
if (m_worker == th) {
m_worker = 0;
return true;
}
return false;
}
bool BrfTest::checkPause(bool tx)
{
bool& paused = tx ? m_pauseSend : m_pauseRead;
if (m_pause) {
if (!paused) {
paused = true;
Debug(this,DebugInfo,"%s paused [%p]",brfDir(tx),this);
}
Thread::idle();
return false;
}
if (paused) {
paused = false;
Debug(this,DebugInfo,"%s resumed [%p]",brfDir(tx),this);
}
return true;
}
bool BrfTest::write()
{
if (!checkPause(true)) {
if (device()->cancelled())
return false;
return true;
}
if (!m_sendTs)
updateTs(true);
unsigned int code = device()->syncTx(m_sendTs,m_sendBuf,m_sendBufSamples);
if (!code)
code = device()->cancelled();
if (!code) {
m_sendTs += m_sendBufSamples;
m_sentSamples += m_sendBufSamples;
}
else if (code != Cancelled)
Debug(this,DebugNote,"Send error: %u '%s' [%p]",code,errorName(code),this);
return code == 0;
}
bool BrfTest::read()
{
if (!checkPause(false)) {
if (device()->cancelled())
return false;
return true;
}
if (!m_readTs)
updateTs(false);
m_skippedBuffs = 0;
unsigned int code = RadioInterface::read(m_readTs,m_bufs,m_skippedBuffs);
if (!code)
code = device()->cancelled();
if (code && code != Cancelled)
Debug(this,DebugNote,"Device read error: %u '%s' [%p]",code,errorName(code),this);
return code == 0;
}
}; // anonymous namespace
/* vi: set ts=8 sw=4 sts=4 noet enc=utf-8: */