mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-05-30 05:52:24 -04:00
LimeSDR Windows build: build an internal LimeSuite
This commit is contained in:
parent
ac7b5a9e6f
commit
2dd97f589e
@ -10,7 +10,6 @@ set(limesuite_SOURCES
|
|||||||
src/BuiltinConnections.cpp
|
src/BuiltinConnections.cpp
|
||||||
${LIBLIMESUITESRC}/src/ConnectionRegistry/ConnectionHandle.cpp
|
${LIBLIMESUITESRC}/src/ConnectionRegistry/ConnectionHandle.cpp
|
||||||
${LIBLIMESUITESRC}/src/ConnectionRegistry/ConnectionRegistry.cpp
|
${LIBLIMESUITESRC}/src/ConnectionRegistry/ConnectionRegistry.cpp
|
||||||
${LIBLIMESUITESRC}/src/ConnectionRegistry/ConnectionRegistry.cpp
|
|
||||||
${LIBLIMESUITESRC}/src/ConnectionRegistry/IConnection.cpp
|
${LIBLIMESUITESRC}/src/ConnectionRegistry/IConnection.cpp
|
||||||
${LIBLIMESUITESRC}/src/ConnectionSTREAM/ConnectionSTREAM.cpp
|
${LIBLIMESUITESRC}/src/ConnectionSTREAM/ConnectionSTREAM.cpp
|
||||||
${LIBLIMESUITESRC}/src/ConnectionSTREAM/ConnectionSTREAMEntry.cpp
|
${LIBLIMESUITESRC}/src/ConnectionSTREAM/ConnectionSTREAMEntry.cpp
|
||||||
|
124
liblimesuite/liblimesuite.pro
Normal file
124
liblimesuite/liblimesuite.pro
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#--------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Pro file for Android and Windows builds with Qt Creator
|
||||||
|
#
|
||||||
|
#--------------------------------------------------------
|
||||||
|
|
||||||
|
QT += core
|
||||||
|
|
||||||
|
TEMPLATE = lib
|
||||||
|
TARGET = liblimesuite
|
||||||
|
|
||||||
|
DEFINES += ENOLINK=21
|
||||||
|
|
||||||
|
CONFIG(MINGW32):LIBLIMESUITESRC = "D:\softs\LimeSuite"
|
||||||
|
CONFIG(MINGW64):LIBLIMESUITESRC = "D:\softs\LimeSuite"
|
||||||
|
|
||||||
|
CONFIG(MINGW32):INCLUDEPATH += "D:\softs\libusb-1.0.20\include"
|
||||||
|
CONFIG(MINGW64):INCLUDEPATH += "D:\softs\libusb-1.0.20\include"
|
||||||
|
|
||||||
|
CONFIG(MINGW32):INCLUDEPATH += "..\libsqlite3\src"
|
||||||
|
CONFIG(MINGW64):INCLUDEPATH += "..\libsqlite3\src"
|
||||||
|
|
||||||
|
INCLUDEPATH += srcmw
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src/ADF4002
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src/ConnectionRegistry
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src/FPGA_common
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src/GFIR
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src/lms7002m
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src/lms7002m_mcu
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src/Si5351C
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/src/protocols
|
||||||
|
INCLUDEPATH += $$LIBLIMESUITESRC/external/cpp-feather-ini-parser
|
||||||
|
|
||||||
|
SOURCES = $$LIBLIMESUITESRC/src/ADF4002/ADF4002.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/API/lms7_api.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/API/lms7_device.cpp\
|
||||||
|
src/BuiltinConnections.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionRegistry/ConnectionHandle.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionRegistry/ConnectionRegistry.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionRegistry/IConnection.cpp\
|
||||||
|
srcmw/ConnectionSTREAM/ConnectionSTREAM.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionSTREAM/ConnectionSTREAMEntry.cpp\
|
||||||
|
srcmw/ConnectionSTREAM/ConnectionSTREAMImages.cpp\
|
||||||
|
srcmw/ConnectionSTREAM/ConnectionSTREAMing.cpp\
|
||||||
|
srcmw/Connection_uLimeSDR/Connection_uLimeSDR.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/Connection_uLimeSDREntry.cpp\
|
||||||
|
srcmw/Connection_uLimeSDR/Connection_uLimeSDRing.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionXillybus/ConnectionXillybus.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionXillybus/ConnectionXillybusEntry.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionXillybus/ConnectionXillybusing.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/FPGA_common/FPGA_common.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/GFIR/corrections.c\
|
||||||
|
$$LIBLIMESUITESRC/src/GFIR/gfir_lms.c\
|
||||||
|
$$LIBLIMESUITESRC/src/GFIR/lms.c\
|
||||||
|
$$LIBLIMESUITESRC/src/GFIR/recipes.c\
|
||||||
|
$$LIBLIMESUITESRC/src/GFIR/rounding.c\
|
||||||
|
$$LIBLIMESUITESRC/src/kissFFT/kiss_fft.c\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/CalibrationCache.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/goert.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_BaseCalibrations.cpp\
|
||||||
|
srcmw/lms7002m/LMS7002M.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_filtersCalibration.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_gainCalibrations.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_parameters.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_RegistersMap.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_RxTxCalibrations.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/mcu_dc_iq_calibration.cpp\
|
||||||
|
srcmw/lms7002m_mcu/MCU_BD.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/ILimeSDRStreaming.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/LMS64CProtocol.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/Si5351C/Si5351C.cpp\
|
||||||
|
srcmw/ErrorReporting.cpp\
|
||||||
|
$$LIBLIMESUITESRC/src/Logger.cpp\
|
||||||
|
src/SystemResources.cpp\
|
||||||
|
src/VersionInfo.cpp
|
||||||
|
|
||||||
|
HEADERS = $$LIBLIMESUITESRC/src/ADF4002/ADF4002.h\
|
||||||
|
$$LIBLIMESUITESRC/src/API/lms7_device.h\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionRegistry/ConnectionHandle.h\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionRegistry/ConnectionRegistry.h\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionRegistry/IConnection.h\
|
||||||
|
srcmw/ConnectionSTREAM/ConnectionSTREAM.h\
|
||||||
|
srcmw/Connection_uLimeSDR/Connection_uLimeSDR.h\
|
||||||
|
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/DRV_DriverInterface.h\
|
||||||
|
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/FTD3XXLibrary/FTD3XX.h\
|
||||||
|
$$LIBLIMESUITESRC/src/ConnectionXillybus/ConnectionXillybus.h\
|
||||||
|
$$LIBLIMESUITESRC/src/FPGA_common/FPGA_common.h\
|
||||||
|
$$LIBLIMESUITESRC/src/GFIR/dfilter.h\
|
||||||
|
$$LIBLIMESUITESRC/src/GFIR/lms_gfir.h\
|
||||||
|
$$LIBLIMESUITESRC/src/GFIR/lms.h\
|
||||||
|
$$LIBLIMESUITESRC/src/kissFFT/_kiss_fft_guts.h\
|
||||||
|
$$LIBLIMESUITESRC/src/kissFFT/kiss_fft.h\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/CalibrationCache.h\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/goertzel.h\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M.h\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_parameters.h\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_RegistersMap.h\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m/mcu_programs.h\
|
||||||
|
$$LIBLIMESUITESRC/src/lms7002m_mcu/MCU_BD.h\
|
||||||
|
srcmw/lms7002m_mcu/MCU_File.h\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/ADCUnits.h\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/dataTypes.h\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/fifo.h\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/ILimeSDRStreaming.h\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/LMS64CCommands.h\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/LMS64CProtocol.h\
|
||||||
|
$$LIBLIMESUITESRC/src/protocols/LMSBoards.h\
|
||||||
|
$$LIBLIMESUITESRC/src/Si5351C/Si5351C.h\
|
||||||
|
srcmw/ErrorReporting.h\
|
||||||
|
$$LIBLIMESUITESRC/src/Logger.h\
|
||||||
|
$$LIBLIMESUITESRC/src/SystemResources.h\
|
||||||
|
$$LIBLIMESUITESRC/src/VersionInfo.h\
|
||||||
|
$$LIBLIMESUITESRC/src/lime/LimeSuite.h
|
||||||
|
|
||||||
|
CONFIG(MINGW32):LIBS += -LD:\softs\libusb-1.0.20\MinGW32\dll -llibusb-1.0
|
||||||
|
CONFIG(MINGW64):LIBS += -LD:\softs\libusb-1.0.20\MinGW64\dll -llibusb-1.0
|
||||||
|
|
||||||
|
CONFIG(MINGW32):LIBS += -L../../../libsqlite3/$${build_subdir} -llibsqlite3
|
||||||
|
CONFIG(MINGW64):LIBS += -L../../../libsqlite3/$${build_subdir} -llibsqlite3
|
||||||
|
|
||||||
|
CONFIG(ANDROID):CONFIG += mobility
|
||||||
|
CONFIG(ANDROID):MOBILITY =
|
||||||
|
|
926
liblimesuite/srcmw/ConnectionSTREAM/ConnectionSTREAM.cpp
Normal file
926
liblimesuite/srcmw/ConnectionSTREAM/ConnectionSTREAM.cpp
Normal file
@ -0,0 +1,926 @@
|
|||||||
|
/**
|
||||||
|
@file ConnectionSTREAM.cpp
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief Implementation of STREAM board connection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ConnectionSTREAM.h"
|
||||||
|
#include "ErrorReporting.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include "Si5351C.h"
|
||||||
|
#include "FPGA_common.h"
|
||||||
|
#include "LMS7002M.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
#include <ciso646>
|
||||||
|
#include <fstream>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define CTR_W_REQCODE 0xC1
|
||||||
|
#define CTR_W_VALUE 0x0000
|
||||||
|
#define CTR_W_INDEX 0x0000
|
||||||
|
|
||||||
|
#define CTR_R_REQCODE 0xC0
|
||||||
|
#define CTR_R_VALUE 0x0000
|
||||||
|
#define CTR_R_INDEX 0x0000
|
||||||
|
|
||||||
|
using namespace lime;
|
||||||
|
|
||||||
|
const uint8_t ConnectionSTREAM::streamBulkOutAddr = 0x01;
|
||||||
|
const uint8_t ConnectionSTREAM::streamBulkInAddr = 0x81;
|
||||||
|
const uint8_t ConnectionSTREAM::ctrlBulkOutAddr = 0x0F;
|
||||||
|
const uint8_t ConnectionSTREAM::ctrlBulkInAddr = 0x8F;
|
||||||
|
|
||||||
|
//control commands to be send via bulk port for boards v1.1 and earlier
|
||||||
|
const std::set<uint8_t> ConnectionSTREAM::commandsToBulkCtrlHw1 =
|
||||||
|
{
|
||||||
|
CMD_BRDSPI_WR, CMD_BRDSPI_RD,
|
||||||
|
CMD_LMS7002_WR, CMD_LMS7002_RD,
|
||||||
|
CMD_LMS7002_RST,
|
||||||
|
};
|
||||||
|
//control commands to be send via bulk port for boards v1.2 and later
|
||||||
|
const std::set<uint8_t> ConnectionSTREAM::commandsToBulkCtrlHw2 =
|
||||||
|
{
|
||||||
|
CMD_BRDSPI_WR, CMD_BRDSPI_RD,
|
||||||
|
CMD_LMS7002_WR, CMD_LMS7002_RD,
|
||||||
|
CMD_ANALOG_VAL_WR, CMD_ANALOG_VAL_RD,
|
||||||
|
CMD_ADF4002_WR,
|
||||||
|
CMD_LMS7002_RST,
|
||||||
|
CMD_GPIO_DIR_WR, CMD_GPIO_DIR_RD,
|
||||||
|
CMD_GPIO_WR, CMD_GPIO_RD,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Initializes port type and object necessary to communicate to usb device.
|
||||||
|
*/
|
||||||
|
ConnectionSTREAM::ConnectionSTREAM(void *arg, const std::string &vidpid, const std::string &serial, const unsigned index)
|
||||||
|
{
|
||||||
|
bulkCtrlAvailable = false;
|
||||||
|
bulkCtrlInProgress = false;
|
||||||
|
RxLoopFunction = bind(&ConnectionSTREAM::ReceivePacketsLoop, this, std::placeholders::_1);
|
||||||
|
TxLoopFunction = bind(&ConnectionSTREAM::TransmitPacketsLoop, this, std::placeholders::_1);
|
||||||
|
isConnected = false;
|
||||||
|
#ifndef __unix__
|
||||||
|
if(arg == nullptr)
|
||||||
|
USBDevicePrimary = new CCyFX3Device();
|
||||||
|
else
|
||||||
|
USBDevicePrimary = new CCyFX3Device(*(CCyFX3Device*)arg);
|
||||||
|
InCtrlEndPt3 = nullptr;
|
||||||
|
OutCtrlEndPt3 = nullptr;
|
||||||
|
InCtrlBulkEndPt = nullptr;
|
||||||
|
OutCtrlBulkEndPt = nullptr;
|
||||||
|
#else
|
||||||
|
dev_handle = nullptr;
|
||||||
|
ctx = (libusb_context *)arg;
|
||||||
|
#endif
|
||||||
|
if (this->Open(vidpid, serial, index) != 0)
|
||||||
|
lime::error(GetLastErrorMessage());
|
||||||
|
|
||||||
|
commandsToBulkCtrl = commandsToBulkCtrlHw2;
|
||||||
|
|
||||||
|
LMSinfo info = this->GetInfo();
|
||||||
|
|
||||||
|
if (info.hardware <= 1)
|
||||||
|
{
|
||||||
|
commandsToBulkCtrl = commandsToBulkCtrlHw1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->VersionCheck();
|
||||||
|
|
||||||
|
if (info.device == LMS_DEV_LIMESDR || info.device == LMS_DEV_LIMESDR_USB_SP || info.device == LMS_DEV_LMS7002M_ULTIMATE_EVB)
|
||||||
|
DetectRefClk();
|
||||||
|
|
||||||
|
GetChipVersion();
|
||||||
|
//must configure synthesizer before using LimeSDR
|
||||||
|
if (info.device == LMS_DEV_LIMESDR && info.hardware < 4)
|
||||||
|
{
|
||||||
|
std::shared_ptr<Si5351C> si5351module(new Si5351C());
|
||||||
|
si5351module->Initialize(this);
|
||||||
|
si5351module->SetPLL(0, 25000000, 0);
|
||||||
|
si5351module->SetPLL(1, 25000000, 0);
|
||||||
|
si5351module->SetClock(0, 27000000, true, false);
|
||||||
|
si5351module->SetClock(1, 27000000, true, false);
|
||||||
|
for (int i = 2; i < 8; ++i)
|
||||||
|
si5351module->SetClock(i, 27000000, false, false);
|
||||||
|
Si5351C::Status status = si5351module->ConfigureClocks();
|
||||||
|
if (status != Si5351C::SUCCESS)
|
||||||
|
{
|
||||||
|
lime::warning("Failed to configure Si5351C");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
status = si5351module->UploadConfiguration();
|
||||||
|
if (status != Si5351C::SUCCESS)
|
||||||
|
lime::warning("Failed to upload Si5351C configuration");
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10)); //some settle time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double ConnectionSTREAM::DetectRefClk(void)
|
||||||
|
{
|
||||||
|
const double fx3Clk = 100e6 * 1.008; //fx3 clock 100MHz (adjusted to 100.8 MHz based on measurement on multiple boards)
|
||||||
|
const double fx3Cnt = 16777210; //fixed fx3 counter in FPGA
|
||||||
|
const double clkTbl[] = { 30.72e6, 38.4e6, 40e6, 52e6 };
|
||||||
|
const uint32_t addr[] = { 0x61, 0x63 };
|
||||||
|
const uint32_t vals[] = { 0x0, 0x0 };
|
||||||
|
if (this->WriteRegisters(addr, vals, 2) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
if (this->WriteRegister(0x61, 0x4) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) //wait for test to finish
|
||||||
|
{
|
||||||
|
unsigned completed;
|
||||||
|
if (this->ReadRegister(0x65, completed) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (completed & 0x4)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto end = std::chrono::steady_clock::now();
|
||||||
|
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||||
|
if (elapsed_seconds.count() > 0.5) //timeout
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t addr2[] = { 0x72, 0x73 };
|
||||||
|
uint32_t vals2[2];
|
||||||
|
if (this->ReadRegisters(addr2, vals2, 2) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
double count = (vals2[0] | (vals2[1] << 16)); //cock counter
|
||||||
|
count *= fx3Clk / fx3Cnt; //estimate ref clock based on FX3 Clock
|
||||||
|
lime::info("Estimated reference clock %1.4f MHz", count/1e6);
|
||||||
|
unsigned i = 0;
|
||||||
|
double delta = 100e6;
|
||||||
|
|
||||||
|
while (i < sizeof(clkTbl) / sizeof(*clkTbl))
|
||||||
|
if (delta < fabs(count - clkTbl[i]))
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
delta = fabs(count - clkTbl[i++]);
|
||||||
|
|
||||||
|
this->SetReferenceClockRate(clkTbl[i-1]);
|
||||||
|
lime::info("Selected reference clock %1.3f MHz", clkTbl[i - 1] / 1e6);
|
||||||
|
return clkTbl[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Closes connection to chip and deallocates used memory.
|
||||||
|
*/
|
||||||
|
ConnectionSTREAM::~ConnectionSTREAM()
|
||||||
|
{
|
||||||
|
for(auto i : mTxStreams)
|
||||||
|
ControlStream((size_t)i, false);
|
||||||
|
for(auto i : mRxStreams)
|
||||||
|
ControlStream((size_t)i, false);
|
||||||
|
for(auto i : mTxStreams)
|
||||||
|
CloseStream((size_t)i);
|
||||||
|
for(auto i : mRxStreams)
|
||||||
|
CloseStream((size_t)i);
|
||||||
|
UpdateThreads();
|
||||||
|
Close();
|
||||||
|
#ifndef __unix__
|
||||||
|
delete USBDevicePrimary;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Tries to open connected USB device and find communication endpoints.
|
||||||
|
@return Returns 0-Success, other-EndPoints not found or device didn't connect.
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::Open(const std::string &vidpid, const std::string &serial, const unsigned index)
|
||||||
|
{
|
||||||
|
bulkCtrlAvailable = false;
|
||||||
|
#ifndef __unix__
|
||||||
|
if(index > USBDevicePrimary->DeviceCount())
|
||||||
|
return ReportError(ERANGE, "ConnectionSTREAM: Device index out of range");
|
||||||
|
|
||||||
|
if(USBDevicePrimary->Open(index) == false)
|
||||||
|
return ReportError(-1, "ConnectionSTREAM: Failed to open device");
|
||||||
|
|
||||||
|
if (InCtrlEndPt3)
|
||||||
|
{
|
||||||
|
delete InCtrlEndPt3;
|
||||||
|
InCtrlEndPt3 = nullptr;
|
||||||
|
}
|
||||||
|
InCtrlEndPt3 = new CCyControlEndPoint(*USBDevicePrimary->ControlEndPt);
|
||||||
|
|
||||||
|
if (OutCtrlEndPt3)
|
||||||
|
{
|
||||||
|
delete OutCtrlEndPt3;
|
||||||
|
OutCtrlEndPt3 = nullptr;
|
||||||
|
}
|
||||||
|
OutCtrlEndPt3 = new CCyControlEndPoint(*USBDevicePrimary->ControlEndPt);
|
||||||
|
|
||||||
|
InCtrlEndPt3->ReqCode = CTR_R_REQCODE;
|
||||||
|
InCtrlEndPt3->Value = CTR_R_VALUE;
|
||||||
|
InCtrlEndPt3->Index = CTR_R_INDEX;
|
||||||
|
InCtrlEndPt3->TimeOut = 3000;
|
||||||
|
|
||||||
|
OutCtrlEndPt3->ReqCode = CTR_W_REQCODE;
|
||||||
|
OutCtrlEndPt3->Value = CTR_W_VALUE;
|
||||||
|
OutCtrlEndPt3->Index = CTR_W_INDEX;
|
||||||
|
OutCtrlEndPt3->TimeOut = 3000;
|
||||||
|
|
||||||
|
for (int i=0; i<USBDevicePrimary->EndPointCount(); i++)
|
||||||
|
if(USBDevicePrimary->EndPoints[i]->Address == streamBulkOutAddr)
|
||||||
|
{
|
||||||
|
OutEndPt = USBDevicePrimary->EndPoints[i];
|
||||||
|
long len = OutEndPt->MaxPktSize * 64;
|
||||||
|
OutEndPt->SetXferSize(len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int i=0; i<USBDevicePrimary->EndPointCount(); i++)
|
||||||
|
if(USBDevicePrimary->EndPoints[i]->Address == streamBulkInAddr)
|
||||||
|
{
|
||||||
|
InEndPt = USBDevicePrimary->EndPoints[i];
|
||||||
|
long len = InEndPt->MaxPktSize * 64;
|
||||||
|
InEndPt->SetXferSize(len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
InCtrlBulkEndPt = nullptr;
|
||||||
|
for (int i=0; i<USBDevicePrimary->EndPointCount(); i++)
|
||||||
|
if(USBDevicePrimary->EndPoints[i]->Address == ctrlBulkInAddr)
|
||||||
|
{
|
||||||
|
InCtrlBulkEndPt = USBDevicePrimary->EndPoints[i];
|
||||||
|
InCtrlBulkEndPt->TimeOut = 1000;
|
||||||
|
bulkCtrlAvailable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
OutCtrlBulkEndPt = nullptr;
|
||||||
|
for (int i=0; i<USBDevicePrimary->EndPointCount(); i++)
|
||||||
|
if(USBDevicePrimary->EndPoints[i]->Address == ctrlBulkOutAddr)
|
||||||
|
{
|
||||||
|
OutCtrlBulkEndPt = USBDevicePrimary->EndPoints[i];
|
||||||
|
OutCtrlBulkEndPt->TimeOut = 1000;
|
||||||
|
bulkCtrlAvailable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
isConnected = true;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
const auto splitPos = vidpid.find(":");
|
||||||
|
const auto vid = std::stoi(vidpid.substr(0, splitPos), nullptr, 16);
|
||||||
|
const auto pid = std::stoi(vidpid.substr(splitPos+1), nullptr, 16);
|
||||||
|
|
||||||
|
libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
|
||||||
|
int usbDeviceCount = libusb_get_device_list(ctx, &devs);
|
||||||
|
|
||||||
|
if (usbDeviceCount < 0) {
|
||||||
|
return ReportError(-1, "ConnectionSTREAM: libusb_get_device_list failed: %s", libusb_strerror(libusb_error(usbDeviceCount)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<usbDeviceCount; ++i)
|
||||||
|
{
|
||||||
|
libusb_device_descriptor desc;
|
||||||
|
int r = libusb_get_device_descriptor(devs[i], &desc);
|
||||||
|
if(r<0) {
|
||||||
|
lime::error("failed to get device description");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (desc.idProduct != pid) continue;
|
||||||
|
if (desc.idVendor != vid) continue;
|
||||||
|
if(libusb_open(devs[i], &dev_handle) != 0) continue;
|
||||||
|
|
||||||
|
std::string foundSerial;
|
||||||
|
if (desc.iSerialNumber > 0)
|
||||||
|
{
|
||||||
|
char data[255];
|
||||||
|
r = libusb_get_string_descriptor_ascii(dev_handle,desc.iSerialNumber,(unsigned char*)data, sizeof(data));
|
||||||
|
if(r<0)
|
||||||
|
lime::error("failed to get serial number");
|
||||||
|
else
|
||||||
|
foundSerial = std::string(data, size_t(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial == foundSerial) break; //found it
|
||||||
|
libusb_close(dev_handle);
|
||||||
|
dev_handle = nullptr;
|
||||||
|
}
|
||||||
|
libusb_free_device_list(devs, 1);
|
||||||
|
|
||||||
|
if(dev_handle == nullptr)
|
||||||
|
return ReportError(-1, "ConnectionSTREAM: libusb_open failed");
|
||||||
|
if(libusb_kernel_driver_active(dev_handle, 0) == 1) //find out if kernel driver is attached
|
||||||
|
{
|
||||||
|
lime::info("Kernel Driver Active");
|
||||||
|
if(libusb_detach_kernel_driver(dev_handle, 0) == 0) //detach it
|
||||||
|
lime::info("Kernel Driver Detached!");
|
||||||
|
}
|
||||||
|
int r = libusb_claim_interface(dev_handle, 0); //claim interface 0 (the first) of device
|
||||||
|
if(r < 0)
|
||||||
|
return ReportError(-1, "ConnectionSTREAM: Cannot claim interface - %s", libusb_strerror(libusb_error(r)));
|
||||||
|
|
||||||
|
libusb_device* device = libusb_get_device(dev_handle);
|
||||||
|
libusb_config_descriptor* descriptor = nullptr;
|
||||||
|
if(libusb_get_active_config_descriptor(device, &descriptor) < 0)
|
||||||
|
{
|
||||||
|
lime::error("failed to get config descriptor");
|
||||||
|
}
|
||||||
|
//check for 0x0F and 0x8F endpoints
|
||||||
|
if(descriptor->bNumInterfaces > 0)
|
||||||
|
{
|
||||||
|
libusb_interface_descriptor iface = descriptor->interface[0].altsetting[0];
|
||||||
|
for(int i=0; i<iface.bNumEndpoints; ++i)
|
||||||
|
if(iface.endpoint[i].bEndpointAddress == ctrlBulkOutAddr ||
|
||||||
|
iface.endpoint[i].bEndpointAddress == ctrlBulkInAddr)
|
||||||
|
{
|
||||||
|
bulkCtrlAvailable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libusb_free_config_descriptor(descriptor);
|
||||||
|
isConnected = true;
|
||||||
|
if(bulkCtrlAvailable)
|
||||||
|
{
|
||||||
|
LMS64CProtocol::GenericPacket ctrPkt;
|
||||||
|
ctrPkt.cmd = CMD_USB_FIFO_RST;
|
||||||
|
ctrPkt.outBuffer.push_back(0x01); //reset bulk endpoints
|
||||||
|
if(TransferPacket(ctrPkt) != 0)
|
||||||
|
lime::error("Failed to reset USB bulk endpoints");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/** @brief Closes communication to device.
|
||||||
|
*/
|
||||||
|
void ConnectionSTREAM::Close()
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
USBDevicePrimary->Close();
|
||||||
|
InEndPt = nullptr;
|
||||||
|
OutEndPt = nullptr;
|
||||||
|
InCtrlBulkEndPt = nullptr;
|
||||||
|
OutCtrlBulkEndPt = nullptr;
|
||||||
|
if (InCtrlEndPt3)
|
||||||
|
{
|
||||||
|
delete InCtrlEndPt3;
|
||||||
|
InCtrlEndPt3 = nullptr;
|
||||||
|
}
|
||||||
|
if (OutCtrlEndPt3)
|
||||||
|
{
|
||||||
|
delete OutCtrlEndPt3;
|
||||||
|
OutCtrlEndPt3 = nullptr;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(dev_handle != 0)
|
||||||
|
{
|
||||||
|
libusb_release_interface(dev_handle, 0);
|
||||||
|
libusb_close(dev_handle);
|
||||||
|
dev_handle = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
isConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Returns connection status
|
||||||
|
@return 1-connection open, 0-connection closed.
|
||||||
|
*/
|
||||||
|
bool ConnectionSTREAM::IsOpen()
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
return USBDevicePrimary->IsOpen() && isConnected;
|
||||||
|
#else
|
||||||
|
return isConnected;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Sends given data buffer to chip through USB port.
|
||||||
|
@param buffer data buffer, must not be longer than 64 bytes.
|
||||||
|
@param length given buffer size.
|
||||||
|
@param timeout_ms timeout limit for operation in milliseconds
|
||||||
|
@return number of bytes sent.
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::Write(const unsigned char *buffer, const int length, int timeout_ms)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mExtraUsbMutex);
|
||||||
|
long len = length;
|
||||||
|
if(IsOpen() == false)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned char* wbuffer = new unsigned char[length];
|
||||||
|
memcpy(wbuffer, buffer, length);
|
||||||
|
bulkCtrlInProgress = false;
|
||||||
|
#ifndef __unix__
|
||||||
|
if(bulkCtrlAvailable
|
||||||
|
&& commandsToBulkCtrl.find(buffer[0]) != commandsToBulkCtrl.end())
|
||||||
|
{
|
||||||
|
bulkCtrlInProgress = true;
|
||||||
|
OutCtrlBulkEndPt->XferData(wbuffer, len);
|
||||||
|
}
|
||||||
|
else if(OutCtrlEndPt3)
|
||||||
|
OutCtrlEndPt3->Write(wbuffer, len);
|
||||||
|
else
|
||||||
|
len = 0;
|
||||||
|
#else
|
||||||
|
if(bulkCtrlAvailable
|
||||||
|
&& commandsToBulkCtrl.find(buffer[0]) != commandsToBulkCtrl.end())
|
||||||
|
{
|
||||||
|
bulkCtrlInProgress = true;
|
||||||
|
int actual = 0;
|
||||||
|
libusb_bulk_transfer(dev_handle, ctrlBulkOutAddr, wbuffer, length, &actual, timeout_ms);
|
||||||
|
len = actual;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
len = libusb_control_transfer(dev_handle, LIBUSB_REQUEST_TYPE_VENDOR,CTR_W_REQCODE ,CTR_W_VALUE, CTR_W_INDEX, wbuffer, length, timeout_ms);
|
||||||
|
#endif
|
||||||
|
delete[] wbuffer;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Reads data coming from the chip through USB port.
|
||||||
|
@param buffer pointer to array where received data will be copied, array must be
|
||||||
|
big enough to fit received data.
|
||||||
|
@param length number of bytes to read from chip.
|
||||||
|
@param timeout_ms timeout limit for operation in milliseconds
|
||||||
|
@return number of bytes received.
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::Read(unsigned char *buffer, const int length, int timeout_ms)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mExtraUsbMutex);
|
||||||
|
long len = length;
|
||||||
|
if(IsOpen() == false)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#ifndef __unix__
|
||||||
|
if(bulkCtrlAvailable && bulkCtrlInProgress)
|
||||||
|
{
|
||||||
|
InCtrlBulkEndPt->XferData(buffer, len);
|
||||||
|
bulkCtrlInProgress = false;
|
||||||
|
}
|
||||||
|
else if(InCtrlEndPt3)
|
||||||
|
InCtrlEndPt3->Read(buffer, len);
|
||||||
|
else
|
||||||
|
len = 0;
|
||||||
|
#else
|
||||||
|
if(bulkCtrlAvailable && bulkCtrlInProgress)
|
||||||
|
{
|
||||||
|
int actual = 0;
|
||||||
|
libusb_bulk_transfer(dev_handle, ctrlBulkInAddr, buffer, len, &actual, timeout_ms);
|
||||||
|
len = actual;
|
||||||
|
bulkCtrlInProgress = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
len = libusb_control_transfer(dev_handle, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN ,CTR_R_REQCODE ,CTR_R_VALUE, CTR_R_INDEX, buffer, len, timeout_ms);
|
||||||
|
#endif
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __unix__
|
||||||
|
/** @brief Function for handling libusb callbacks
|
||||||
|
*/
|
||||||
|
void callback_libusbtransfer(libusb_transfer *trans)
|
||||||
|
{
|
||||||
|
USBTransferContext *context = reinterpret_cast<USBTransferContext*>(trans->user_data);
|
||||||
|
std::unique_lock<std::mutex> lck(context->transferLock);
|
||||||
|
switch(trans->status)
|
||||||
|
{
|
||||||
|
case LIBUSB_TRANSFER_CANCELLED:
|
||||||
|
//lime::error("Transfer %i canceled", context->id);
|
||||||
|
context->bytesXfered = trans->actual_length;
|
||||||
|
context->done.store(true);
|
||||||
|
//context->used = false;
|
||||||
|
//context->reset();
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_COMPLETED:
|
||||||
|
//if(trans->actual_length == context->bytesExpected)
|
||||||
|
{
|
||||||
|
context->bytesXfered = trans->actual_length;
|
||||||
|
context->done.store(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_ERROR:
|
||||||
|
lime::error("TRANSFER ERRROR");
|
||||||
|
context->bytesXfered = trans->actual_length;
|
||||||
|
context->done.store(true);
|
||||||
|
//context->used = false;
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_TIMED_OUT:
|
||||||
|
//lime::error("transfer timed out %i", context->id);
|
||||||
|
context->bytesXfered = trans->actual_length;
|
||||||
|
context->done.store(true);
|
||||||
|
//context->used = false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_OVERFLOW:
|
||||||
|
lime::error("transfer overflow");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_STALL:
|
||||||
|
lime::error("transfer stalled");
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_NO_DEVICE:
|
||||||
|
lime::error("transfer no device");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lck.unlock();
|
||||||
|
context->cv.notify_one();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Starts asynchronous data reading from board
|
||||||
|
@param *buffer buffer where to store received data
|
||||||
|
@param length number of bytes to read
|
||||||
|
@return handle of transfer context
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::BeginDataReading(char *buffer, uint32_t length)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
bool contextFound = false;
|
||||||
|
//find not used context
|
||||||
|
for(i = 0; i<USB_MAX_CONTEXTS; i++)
|
||||||
|
{
|
||||||
|
if(!contexts[i].used)
|
||||||
|
{
|
||||||
|
contextFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!contextFound)
|
||||||
|
{
|
||||||
|
lime::error("No contexts left for reading data");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
contexts[i].used = true;
|
||||||
|
#ifndef __unix__
|
||||||
|
if(InEndPt)
|
||||||
|
contexts[i].context = InEndPt->BeginDataXfer((unsigned char*)buffer, length, contexts[i].inOvLap);
|
||||||
|
return i;
|
||||||
|
#else
|
||||||
|
unsigned int Timeout = 500;
|
||||||
|
libusb_transfer *tr = contexts[i].transfer;
|
||||||
|
libusb_fill_bulk_transfer(tr, dev_handle, streamBulkInAddr, (unsigned char*)buffer, length, callback_libusbtransfer, &contexts[i], Timeout);
|
||||||
|
contexts[i].done = false;
|
||||||
|
contexts[i].bytesXfered = 0;
|
||||||
|
contexts[i].bytesExpected = length;
|
||||||
|
int status = libusb_submit_transfer(tr);
|
||||||
|
if(status != 0)
|
||||||
|
{
|
||||||
|
lime::error("BEGIN DATA READING %s", libusb_error_name(status));
|
||||||
|
contexts[i].used = false;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Waits for asynchronous data reception
|
||||||
|
@param contextHandle handle of which context data to wait
|
||||||
|
@param timeout_ms number of miliseconds to wait
|
||||||
|
@return 1-data received, 0-data not received
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::WaitForReading(int contextHandle, unsigned int timeout_ms)
|
||||||
|
{
|
||||||
|
if(contextHandle >= 0 && contexts[contextHandle].used == true)
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
int status = 0;
|
||||||
|
if(InEndPt)
|
||||||
|
status = InEndPt->WaitForXfer(contexts[contextHandle].inOvLap, timeout_ms);
|
||||||
|
return status;
|
||||||
|
#else
|
||||||
|
auto t1 = chrono::high_resolution_clock::now();
|
||||||
|
auto t2 = chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lck(contexts[contextHandle].transferLock);
|
||||||
|
while(contexts[contextHandle].done.load() == false && std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() < timeout_ms)
|
||||||
|
{
|
||||||
|
//blocking not to waste CPU
|
||||||
|
contexts[contextHandle].cv.wait_for(lck, chrono::milliseconds(timeout_ms));
|
||||||
|
t2 = chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
return contexts[contextHandle].done.load() == true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Finishes asynchronous data reading from board
|
||||||
|
@param buffer array where to store received data
|
||||||
|
@param length number of bytes to read
|
||||||
|
@param contextHandle handle of which context to finish
|
||||||
|
@return negative values failure, positive number of bytes received
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::FinishDataReading(char *buffer, uint32_t length, int contextHandle)
|
||||||
|
{
|
||||||
|
if(contextHandle >= 0 && contexts[contextHandle].used == true)
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
int status = 0;
|
||||||
|
long len = length;
|
||||||
|
if(InEndPt)
|
||||||
|
status = InEndPt->FinishDataXfer((unsigned char*)buffer, len, contexts[contextHandle].inOvLap, contexts[contextHandle].context);
|
||||||
|
contexts[contextHandle].used = false;
|
||||||
|
contexts[contextHandle].reset();
|
||||||
|
return len;
|
||||||
|
#else
|
||||||
|
length = contexts[contextHandle].bytesXfered;
|
||||||
|
contexts[contextHandle].used = false;
|
||||||
|
contexts[contextHandle].reset();
|
||||||
|
return length;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Aborts reading operations
|
||||||
|
*/
|
||||||
|
void ConnectionSTREAM::AbortReading()
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
InEndPt->Abort();
|
||||||
|
#else
|
||||||
|
for(int i=0; i<USB_MAX_CONTEXTS; ++i)
|
||||||
|
{
|
||||||
|
if(contexts[i].used)
|
||||||
|
libusb_cancel_transfer( contexts[i].transfer );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Starts asynchronous data Sending to board
|
||||||
|
@param *buffer buffer to send
|
||||||
|
@param length number of bytes to send
|
||||||
|
@return handle of transfer context
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::BeginDataSending(const char *buffer, uint32_t length)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
//find not used context
|
||||||
|
bool contextFound = false;
|
||||||
|
for(i = 0; i<USB_MAX_CONTEXTS; i++)
|
||||||
|
{
|
||||||
|
if(!contextsToSend[i].used)
|
||||||
|
{
|
||||||
|
contextFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!contextFound)
|
||||||
|
return -1;
|
||||||
|
contextsToSend[i].used = true;
|
||||||
|
#ifndef __unix__
|
||||||
|
if(OutEndPt)
|
||||||
|
contextsToSend[i].context = OutEndPt->BeginDataXfer((unsigned char*)buffer, length, contextsToSend[i].inOvLap);
|
||||||
|
return i;
|
||||||
|
#else
|
||||||
|
unsigned int Timeout = 500;
|
||||||
|
libusb_transfer *tr = contextsToSend[i].transfer;
|
||||||
|
libusb_fill_bulk_transfer(tr, dev_handle, streamBulkOutAddr, (unsigned char*)buffer, length, callback_libusbtransfer, &contextsToSend[i], Timeout);
|
||||||
|
contextsToSend[i].done = false;
|
||||||
|
contextsToSend[i].bytesXfered = 0;
|
||||||
|
contextsToSend[i].bytesExpected = length;
|
||||||
|
int status = libusb_submit_transfer(tr);
|
||||||
|
if(status != 0)
|
||||||
|
{
|
||||||
|
lime::error("BEGIN DATA SENDING %s", libusb_error_name(status));
|
||||||
|
contextsToSend[i].used = false;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Waits for asynchronous data sending
|
||||||
|
@param contextHandle handle of which context data to wait
|
||||||
|
@param timeout_ms number of miliseconds to wait
|
||||||
|
@return 1-data received, 0-data not received
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::WaitForSending(int contextHandle, unsigned int timeout_ms)
|
||||||
|
{
|
||||||
|
if( contextsToSend[contextHandle].used == true )
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
int status = 0;
|
||||||
|
if(OutEndPt)
|
||||||
|
status = OutEndPt->WaitForXfer(contextsToSend[contextHandle].inOvLap, timeout_ms);
|
||||||
|
return status;
|
||||||
|
#else
|
||||||
|
auto t1 = chrono::high_resolution_clock::now();
|
||||||
|
auto t2 = chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lck(contextsToSend[contextHandle].transferLock);
|
||||||
|
while(contextsToSend[contextHandle].done.load() == false && std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() < timeout_ms)
|
||||||
|
{
|
||||||
|
//blocking not to waste CPU
|
||||||
|
contextsToSend[contextHandle].cv.wait_for(lck, chrono::milliseconds(timeout_ms));
|
||||||
|
t2 = chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
return contextsToSend[contextHandle].done == true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Finishes asynchronous data sending to board
|
||||||
|
@param buffer array where to store received data
|
||||||
|
@param length number of bytes to read, function changes this value to number of bytes acctually received
|
||||||
|
@param contextHandle handle of which context to finish
|
||||||
|
@return false failure, true number of bytes sent
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::FinishDataSending(const char *buffer, uint32_t length, int contextHandle)
|
||||||
|
{
|
||||||
|
if( contextsToSend[contextHandle].used == true)
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
long len = length;
|
||||||
|
if(OutEndPt)
|
||||||
|
OutEndPt->FinishDataXfer((unsigned char*)buffer, len, contextsToSend[contextHandle].inOvLap, contextsToSend[contextHandle].context);
|
||||||
|
contextsToSend[contextHandle].used = false;
|
||||||
|
contextsToSend[contextHandle].reset();
|
||||||
|
return len;
|
||||||
|
#else
|
||||||
|
length = contextsToSend[contextHandle].bytesXfered;
|
||||||
|
contextsToSend[contextHandle].used = false;
|
||||||
|
contextsToSend[contextHandle].reset();
|
||||||
|
return length;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Aborts sending operations
|
||||||
|
*/
|
||||||
|
void ConnectionSTREAM::AbortSending()
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
OutEndPt->Abort();
|
||||||
|
#else
|
||||||
|
for (int i = 0; i<USB_MAX_CONTEXTS; ++i)
|
||||||
|
{
|
||||||
|
if(contextsToSend[i].used)
|
||||||
|
libusb_cancel_transfer(contextsToSend[i].transfer);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectionSTREAM::ProgramWrite(const char *buffer, const size_t length, const int programmingMode, const int device, ProgrammingCallback callback)
|
||||||
|
{
|
||||||
|
if (device == LMS64CProtocol::FX3 && programmingMode == 1)
|
||||||
|
{
|
||||||
|
#ifdef __unix__
|
||||||
|
libusb_device_descriptor desc;
|
||||||
|
int ret = libusb_get_device_descriptor(libusb_get_device(dev_handle), &desc);
|
||||||
|
if(ret<0)
|
||||||
|
lime::error("failed to get device description");
|
||||||
|
else if (desc.idProduct == 243)
|
||||||
|
#else
|
||||||
|
if (USBDevicePrimary->ProductID == 243)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#ifdef __unix__
|
||||||
|
return fx3_usbboot_download((unsigned char*)buffer,length);
|
||||||
|
#else
|
||||||
|
char* filename = "fx3fw_image_tmp.img";
|
||||||
|
int ret = 0;
|
||||||
|
std::ofstream myfile(filename, ios::out | ios::binary | ios::trunc);
|
||||||
|
if (!myfile.is_open())
|
||||||
|
{
|
||||||
|
ReportError("FX3 FW:Unable to create temporary file");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
myfile.write(buffer,length);
|
||||||
|
if (myfile.fail())
|
||||||
|
{
|
||||||
|
ReportError("FX3 FW:Unable to write to temporary file");
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
myfile.close();
|
||||||
|
|
||||||
|
if (ret != -1)
|
||||||
|
{
|
||||||
|
if ((ret=USBDevicePrimary->DownloadFw(filename, FX3_FWDWNLOAD_MEDIA_TYPE::RAM))!=0)
|
||||||
|
ReportError("FX3: Failed to upload FW to RAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::remove(filename);
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReportError("FX3 bootloader NOT detected");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LMS64CProtocol::ProgramWrite(buffer,length,programmingMode,device,callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __unix__
|
||||||
|
|
||||||
|
#define MAX_FWIMG_SIZE (512 * 1024) // Maximum size of the firmware binary.
|
||||||
|
#define GET_LSW(v) ((unsigned short)((v) & 0xFFFF))
|
||||||
|
#define GET_MSW(v) ((unsigned short)((v) >> 16))
|
||||||
|
|
||||||
|
#define VENDORCMD_TIMEOUT (5000) // Timeout for each vendor command is set to 5 seconds.
|
||||||
|
|
||||||
|
|
||||||
|
int ConnectionSTREAM::ram_write(unsigned char *buf, unsigned int ramAddress, int len)
|
||||||
|
{
|
||||||
|
const int MAX_WRITE_SIZE = (2 * 1024); // Max. size of data that can be written through one vendor command.
|
||||||
|
int r;
|
||||||
|
int index = 0;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
while ( len > 0 )
|
||||||
|
{
|
||||||
|
size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len;
|
||||||
|
r = libusb_control_transfer(dev_handle, 0x40, 0xA0, GET_LSW(ramAddress), GET_MSW(ramAddress),&buf[index], size, VENDORCMD_TIMEOUT);
|
||||||
|
if ( r != size )
|
||||||
|
{
|
||||||
|
lime::error("Vendor write to FX3 RAM failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ramAddress += size;
|
||||||
|
index += size;
|
||||||
|
len -= size;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectionSTREAM::fx3_usbboot_download(unsigned char *fwBuf, int filesize)
|
||||||
|
{
|
||||||
|
unsigned int *data_p;
|
||||||
|
unsigned int i, checksum;
|
||||||
|
unsigned int address, length;
|
||||||
|
int r, index;
|
||||||
|
|
||||||
|
if ( filesize > MAX_FWIMG_SIZE ) {
|
||||||
|
ReportError("File size exceeds maximum firmware image size\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( strncmp((char *)fwBuf,"CY",2) ) {
|
||||||
|
ReportError("Image does not have 'CY' at start. aborting\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( fwBuf[2] & 0x01 ) {
|
||||||
|
ReportError("Image does not contain executable code\n");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !(fwBuf[3] == 0xB0) ) {
|
||||||
|
ReportError("Not a normal FW binary with checksum\n");
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run through each section of code, and use vendor commands to download them to RAM.
|
||||||
|
index = 4;
|
||||||
|
checksum = 0;
|
||||||
|
while ( index < filesize )
|
||||||
|
{
|
||||||
|
data_p = (unsigned int *)(fwBuf + index);
|
||||||
|
length = data_p[0];
|
||||||
|
address = data_p[1];
|
||||||
|
if (length != 0)
|
||||||
|
{
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
checksum += data_p[2 + i];
|
||||||
|
r = ram_write(fwBuf + index + 8, address, length * 4);
|
||||||
|
if (r != 0)
|
||||||
|
{
|
||||||
|
ReportError("Failed to download data to FX3 RAM\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (checksum != data_p[2]) {
|
||||||
|
ReportError ("Checksum error in firmware binary\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = libusb_control_transfer(dev_handle, 0x40, 0xA0, GET_LSW(address), GET_MSW(address), NULL,0, VENDORCMD_TIMEOUT);
|
||||||
|
if ( r != 0 )
|
||||||
|
lime::error("Ignored error in control transfer: %d", r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index += (8 + length * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
192
liblimesuite/srcmw/ConnectionSTREAM/ConnectionSTREAM.h
Normal file
192
liblimesuite/srcmw/ConnectionSTREAM/ConnectionSTREAM.h
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/**
|
||||||
|
@file ConnectionSTREAM.h
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief Implementation of STREAM board connection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <ConnectionRegistry.h>
|
||||||
|
#include <IConnection.h>
|
||||||
|
#include <ILimeSDRStreaming.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include "fifo.h"
|
||||||
|
#include <ciso646>
|
||||||
|
|
||||||
|
#define __unix__
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
#ifndef __unix__
|
||||||
|
#include "windows.h"
|
||||||
|
#include "CyAPI.h"
|
||||||
|
#else
|
||||||
|
#include <libusb-1.0/libusb.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <chrono>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace lime
|
||||||
|
{
|
||||||
|
|
||||||
|
#define USB_MAX_CONTEXTS 64 //maximum number of contexts for asynchronous transfers
|
||||||
|
|
||||||
|
/** @brief Wrapper class for holding USB asynchronous transfers contexts
|
||||||
|
*/
|
||||||
|
class USBTransferContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
USBTransferContext() : used(false)
|
||||||
|
{
|
||||||
|
id = idCounter++;
|
||||||
|
#ifndef __unix__
|
||||||
|
inOvLap = new OVERLAPPED;
|
||||||
|
memset(inOvLap, 0, sizeof(OVERLAPPED));
|
||||||
|
inOvLap->hEvent = CreateEvent(NULL, false, false, NULL);
|
||||||
|
context = NULL;
|
||||||
|
#else
|
||||||
|
transfer = libusb_alloc_transfer(0);
|
||||||
|
bytesXfered = 0;
|
||||||
|
bytesExpected = 0;
|
||||||
|
done = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
~USBTransferContext()
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
CloseHandle(inOvLap->hEvent);
|
||||||
|
delete inOvLap;
|
||||||
|
#else
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bool reset()
|
||||||
|
{
|
||||||
|
if(used)
|
||||||
|
return false;
|
||||||
|
#ifndef __unix__
|
||||||
|
CloseHandle(inOvLap->hEvent);
|
||||||
|
memset(inOvLap, 0, sizeof(OVERLAPPED));
|
||||||
|
inOvLap->hEvent = CreateEvent(NULL, false, false, NULL);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool used;
|
||||||
|
int id;
|
||||||
|
static int idCounter;
|
||||||
|
#ifndef __unix__
|
||||||
|
PUCHAR context;
|
||||||
|
OVERLAPPED* inOvLap;
|
||||||
|
#else
|
||||||
|
libusb_transfer* transfer;
|
||||||
|
long bytesXfered;
|
||||||
|
long bytesExpected;
|
||||||
|
std::atomic<bool> done;
|
||||||
|
std::mutex transferLock;
|
||||||
|
std::condition_variable cv;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConnectionSTREAM : public ILimeSDRStreaming
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConnectionSTREAM(void* arg, const std::string &vidpid, const std::string &serial, const unsigned index);
|
||||||
|
~ConnectionSTREAM(void);
|
||||||
|
void VersionCheck(void);
|
||||||
|
|
||||||
|
int Open(const std::string &vidpid, const std::string &serial, const unsigned index);
|
||||||
|
void Close();
|
||||||
|
bool IsOpen();
|
||||||
|
int GetOpenedIndex();
|
||||||
|
|
||||||
|
virtual int Write(const unsigned char* buffer, int length, int timeout_ms = 100) override;
|
||||||
|
virtual int Read(unsigned char* buffer, int length, int timeout_ms = 100) override;
|
||||||
|
|
||||||
|
virtual int UploadWFM(const void* const* samples, uint8_t chCount, size_t sample_count, StreamConfig::StreamDataFormat format) override;
|
||||||
|
|
||||||
|
//hooks to update FPGA plls when baseband interface data rate is changed
|
||||||
|
virtual int UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate) override;
|
||||||
|
virtual int UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate, const double txPhase, const double rxPhase) override;
|
||||||
|
virtual int ProgramWrite(const char *buffer, const size_t length, const int programmingMode, const int device, ProgrammingCallback callback) override;
|
||||||
|
int ProgramUpdate(const bool download, ProgrammingCallback callback);
|
||||||
|
int ReadRawStreamData(char* buffer, unsigned length, int timeout_ms = 100)override;
|
||||||
|
protected:
|
||||||
|
virtual void ReceivePacketsLoop(const ThreadData args) override;
|
||||||
|
virtual void TransmitPacketsLoop(const ThreadData args) override;
|
||||||
|
|
||||||
|
virtual int BeginDataReading(char* buffer, uint32_t length);
|
||||||
|
virtual int WaitForReading(int contextHandle, unsigned int timeout_ms);
|
||||||
|
virtual int FinishDataReading(char* buffer, uint32_t length, int contextHandle);
|
||||||
|
virtual void AbortReading();
|
||||||
|
|
||||||
|
virtual int BeginDataSending(const char* buffer, uint32_t length);
|
||||||
|
virtual int WaitForSending(int contextHandle, uint32_t timeout_ms);
|
||||||
|
virtual int FinishDataSending(const char* buffer, uint32_t length, int contextHandle);
|
||||||
|
virtual void AbortSending();
|
||||||
|
|
||||||
|
int ResetStreamBuffers() override;
|
||||||
|
eConnectionType GetType(void) {return USB_PORT;}
|
||||||
|
|
||||||
|
double DetectRefClk(void);
|
||||||
|
|
||||||
|
USBTransferContext contexts[USB_MAX_CONTEXTS];
|
||||||
|
USBTransferContext contextsToSend[USB_MAX_CONTEXTS];
|
||||||
|
|
||||||
|
bool isConnected;
|
||||||
|
|
||||||
|
#ifndef __unix__
|
||||||
|
CCyFX3Device* USBDevicePrimary;
|
||||||
|
//control endpoints
|
||||||
|
CCyControlEndPoint* InCtrlEndPt3;
|
||||||
|
CCyControlEndPoint* OutCtrlEndPt3;
|
||||||
|
|
||||||
|
//end points for samples reading and writing
|
||||||
|
CCyUSBEndPoint* InEndPt;
|
||||||
|
CCyUSBEndPoint* OutEndPt;
|
||||||
|
|
||||||
|
CCyUSBEndPoint* InCtrlBulkEndPt;
|
||||||
|
CCyUSBEndPoint* OutCtrlBulkEndPt;
|
||||||
|
#else
|
||||||
|
libusb_device_handle* dev_handle; //a device handle
|
||||||
|
libusb_context* ctx; //a libusb session
|
||||||
|
int read_firmware_image(unsigned char *buf, int len);
|
||||||
|
int fx3_usbboot_download(unsigned char *buf, int len);
|
||||||
|
int ram_write(unsigned char *buf, unsigned int ramAddress, int len);
|
||||||
|
#endif
|
||||||
|
static const uint8_t streamBulkOutAddr;
|
||||||
|
static const uint8_t streamBulkInAddr;
|
||||||
|
static const uint8_t ctrlBulkOutAddr;
|
||||||
|
static const uint8_t ctrlBulkInAddr;
|
||||||
|
static const std::set<uint8_t> commandsToBulkCtrlHw1;
|
||||||
|
static const std::set<uint8_t> commandsToBulkCtrlHw2;
|
||||||
|
std::set<uint8_t> commandsToBulkCtrl;
|
||||||
|
bool bulkCtrlInProgress;
|
||||||
|
bool bulkCtrlAvailable;
|
||||||
|
std::mutex mExtraUsbMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConnectionSTREAMEntry : public ConnectionRegistryEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConnectionSTREAMEntry(void);
|
||||||
|
ConnectionSTREAMEntry(const std::string entryName);
|
||||||
|
virtual ~ConnectionSTREAMEntry(void);
|
||||||
|
virtual std::vector<ConnectionHandle> enumerate(const ConnectionHandle& hint);
|
||||||
|
virtual IConnection* make(const ConnectionHandle& handle);
|
||||||
|
protected:
|
||||||
|
#ifndef __unix__
|
||||||
|
std::string DeviceName(unsigned int index);
|
||||||
|
void *ctx; //not used, just for mirroring unix
|
||||||
|
#else
|
||||||
|
libusb_context* ctx; //a libusb session
|
||||||
|
std::thread mUSBProcessingThread;
|
||||||
|
void handle_libusb_events();
|
||||||
|
std::atomic<bool> mProcessUSBEvents;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
184
liblimesuite/srcmw/ConnectionSTREAM/ConnectionSTREAMImages.cpp
Normal file
184
liblimesuite/srcmw/ConnectionSTREAM/ConnectionSTREAMImages.cpp
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
@file ConnectionSTREAMImages.cpp
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief Image updating and version checking
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ConnectionSTREAM.h"
|
||||||
|
#include "ErrorReporting.h"
|
||||||
|
#include "SystemResources.h"
|
||||||
|
#include "LMS64CProtocol.h"
|
||||||
|
#include "LMSBoards.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <ciso646>
|
||||||
|
|
||||||
|
using namespace lime;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The entry structure that describes a board revision and its fw/gw images
|
||||||
|
*/
|
||||||
|
struct ConnectionSTREAMImageEntry
|
||||||
|
{
|
||||||
|
eLMS_DEV dev;
|
||||||
|
int hw_rev;
|
||||||
|
|
||||||
|
int fw_ver;
|
||||||
|
const char *fw_img;
|
||||||
|
|
||||||
|
int gw_ver;
|
||||||
|
int gw_rev;
|
||||||
|
const char *gw_rbf;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Lookup the board information given hardware type and revision.
|
||||||
|
* Edit each entry for supported hardware and image updates.
|
||||||
|
*/
|
||||||
|
static const ConnectionSTREAMImageEntry &lookupImageEntry(const LMS64CProtocol::LMSinfo &info)
|
||||||
|
{
|
||||||
|
static const std::vector<ConnectionSTREAMImageEntry> imageEntries = {
|
||||||
|
ConnectionSTREAMImageEntry({LMS_DEV_UNKNOWN, -1, -1, "Unknown-USB.img", -1, -1, "Unknown-USB.rbf"}),
|
||||||
|
ConnectionSTREAMImageEntry({LMS_DEV_LIMESDR, 4, 3, "LimeSDR-USB_HW_1.3_r3.0.img", 2, 8, "LimeSDR-USB_HW_1.4_r2.8.rbf"}),
|
||||||
|
ConnectionSTREAMImageEntry({LMS_DEV_LIMESDR, 3, 3, "LimeSDR-USB_HW_1.3_r3.0.img", 1, 20, "LimeSDR-USB_HW_1.1_r1.20.rbf"}),
|
||||||
|
ConnectionSTREAMImageEntry({LMS_DEV_LIMESDR, 2, 3, "LimeSDR-USB_HW_1.2_r3.0.img", 1, 20, "LimeSDR-USB_HW_1.1_r1.20.rbf"}),
|
||||||
|
ConnectionSTREAMImageEntry({LMS_DEV_LIMESDR, 1, 7, "LimeSDR-USB_HW_1.1_r7.0.img", 1, 20, "LimeSDR-USB_HW_1.1_r1.20.rbf"}),
|
||||||
|
ConnectionSTREAMImageEntry({LMS_DEV_STREAM, 3, 8, "STREAM-USB_HW_1.1_r8.0.img", 1, 2, "STREAM-USB_HW_1.3_r1.2.rbf"})};
|
||||||
|
|
||||||
|
for(const auto &iter : imageEntries)
|
||||||
|
{
|
||||||
|
if (info.device == iter.dev and info.hardware == iter.hw_rev)
|
||||||
|
{
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageEntries.front(); //the -1 unknown entry
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSTREAM::VersionCheck(void)
|
||||||
|
{
|
||||||
|
const auto info = this->GetInfo();
|
||||||
|
const auto &entry = lookupImageEntry(info);
|
||||||
|
|
||||||
|
//an entry match was not found
|
||||||
|
if (entry.dev == LMS_DEV_UNKNOWN)
|
||||||
|
{
|
||||||
|
lime::error("Unsupported hardware connected: %s[HW=%d]", GetDeviceName(info.device), info.hardware);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check and warn about firmware mismatch problems
|
||||||
|
if (info.firmware != entry.fw_ver) lime::warning(
|
||||||
|
"Firmware version mismatch!\n"
|
||||||
|
" Expected firmware version %d, but found version %d\n"
|
||||||
|
" Follow the FW and FPGA upgrade instructions:\n"
|
||||||
|
" http://wiki.myriadrf.org/Lime_Suite#Flashing_images\n"
|
||||||
|
" Or run update on the command line: LimeUtil --update\n",
|
||||||
|
entry.fw_ver, info.firmware);
|
||||||
|
|
||||||
|
//check and warn about gateware mismatch problems
|
||||||
|
const auto fpgaInfo = this->GetFPGAInfo();
|
||||||
|
if (fpgaInfo.gatewareVersion != entry.gw_ver
|
||||||
|
|| fpgaInfo.gatewareRevision != entry.gw_rev) lime::warning(
|
||||||
|
"Gateware version mismatch!\n"
|
||||||
|
" Expected gateware version %d, revision %d\n"
|
||||||
|
" But found version %d, revision %d\n"
|
||||||
|
" Follow the FW and FPGA upgrade instructions:\n"
|
||||||
|
" http://wiki.myriadrf.org/Lime_Suite#Flashing_images\n"
|
||||||
|
" Or run update on the command line: LimeUtil --update\n",
|
||||||
|
entry.gw_ver, entry.gw_rev, fpgaInfo.gatewareVersion, fpgaInfo.gatewareRevision);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool programmingCallbackStream(
|
||||||
|
int bsent, int btotal, const char* progressMsg,
|
||||||
|
const std::string &image,
|
||||||
|
IConnection::ProgrammingCallback callback)
|
||||||
|
{
|
||||||
|
const auto msg = std::string(progressMsg) + " (" + image + ")";
|
||||||
|
return callback(bsent, btotal, msg.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectionSTREAM::ProgramUpdate(const bool download, IConnection::ProgrammingCallback callback)
|
||||||
|
{
|
||||||
|
const auto info = this->GetInfo();
|
||||||
|
const auto &entry = lookupImageEntry(info);
|
||||||
|
|
||||||
|
//an entry match was not found
|
||||||
|
if (entry.dev == LMS_DEV_UNKNOWN)
|
||||||
|
{
|
||||||
|
return lime::ReportError("Unsupported hardware connected: %s[HW=%d]", GetDeviceName(info.device), info.hardware);
|
||||||
|
}
|
||||||
|
|
||||||
|
//download images when missing
|
||||||
|
if (download)
|
||||||
|
{
|
||||||
|
const std::vector<std::string> images = {entry.fw_img, entry.gw_rbf};
|
||||||
|
for (const auto &image : images)
|
||||||
|
{
|
||||||
|
if (not lime::locateImageResource(image).empty()) continue;
|
||||||
|
const std::string msg("Downloading: " + image);
|
||||||
|
if (callback) callback(0, 1, msg.c_str());
|
||||||
|
int ret = lime::downloadImageResource(image);
|
||||||
|
if (ret != 0) return ret; //error set by download call
|
||||||
|
if (callback) callback(1, 1, "Done!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//load firmware into flash
|
||||||
|
{
|
||||||
|
//open file
|
||||||
|
std::ifstream file;
|
||||||
|
const auto path = lime::locateImageResource(entry.fw_img);
|
||||||
|
file.open(path.c_str(), std::ios::in | std::ios::binary);
|
||||||
|
if (not file.good()) return lime::ReportError("Error opening %s", path.c_str());
|
||||||
|
|
||||||
|
//read file
|
||||||
|
std::streampos fileSize;
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
std::vector<char> progData(fileSize, 0);
|
||||||
|
file.read(progData.data(), fileSize);
|
||||||
|
|
||||||
|
int device = LMS64CProtocol::FX3; //FX3
|
||||||
|
int progMode = 2; //Firmware to FLASH
|
||||||
|
using namespace std::placeholders;
|
||||||
|
const auto cb = std::bind(&programmingCallbackStream, _1, _2, _3, path, callback);
|
||||||
|
auto status = this->ProgramWrite(progData.data(), progData.size(), progMode, device, cb);
|
||||||
|
if (status != 0) return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
//load gateware into flash
|
||||||
|
{
|
||||||
|
//open file
|
||||||
|
std::ifstream file;
|
||||||
|
const auto path = lime::locateImageResource(entry.gw_rbf);
|
||||||
|
file.open(path.c_str(), std::ios::in | std::ios::binary);
|
||||||
|
if (not file.good()) return lime::ReportError("Error opening %s", path.c_str());
|
||||||
|
|
||||||
|
//read file
|
||||||
|
std::streampos fileSize;
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
std::vector<char> progData(fileSize, 0);
|
||||||
|
file.read(progData.data(), fileSize);
|
||||||
|
|
||||||
|
int device = LMS64CProtocol::FPGA; //Altera FPGA
|
||||||
|
int progMode = 1; //Bitstream to FLASH
|
||||||
|
using namespace std::placeholders;
|
||||||
|
const auto cb = std::bind(&programmingCallbackStream, _1, _2, _3, path, callback);
|
||||||
|
auto status = this->ProgramWrite(progData.data(), progData.size(), progMode, device, cb);
|
||||||
|
if (status != 0) return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reset FX3, FPGA should be reloaded on boot
|
||||||
|
{
|
||||||
|
int device = LMS64CProtocol::FX3; //FX3
|
||||||
|
auto status = this->ProgramWrite(nullptr, 0, 0, device, nullptr);
|
||||||
|
if (status != 0) return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
654
liblimesuite/srcmw/ConnectionSTREAM/ConnectionSTREAMing.cpp
Normal file
654
liblimesuite/srcmw/ConnectionSTREAM/ConnectionSTREAMing.cpp
Normal file
@ -0,0 +1,654 @@
|
|||||||
|
/**
|
||||||
|
@file ConnectionSTREAMing.cpp
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief Implementation of STREAM board connection (streaming API)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ConnectionSTREAM.h"
|
||||||
|
#include "fifo.h"
|
||||||
|
#include <LMS7002M.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <complex>
|
||||||
|
#include <ciso646>
|
||||||
|
#include <FPGA_common.h>
|
||||||
|
#include "ErrorReporting.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
|
using namespace lime;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int ConnectionSTREAM::UploadWFM(const void* const* samples, uint8_t chCount, size_t sample_count, StreamConfig::StreamDataFormat format)
|
||||||
|
{
|
||||||
|
WriteRegister(0x000C, 0x3); //channels 0,1
|
||||||
|
WriteRegister(0x000E, 0x2); //12bit samples
|
||||||
|
WriteRegister(0x000D, 0x0004); //WFM_LOAD
|
||||||
|
|
||||||
|
lime::FPGA_DataPacket pkt;
|
||||||
|
size_t samplesUsed = 0;
|
||||||
|
|
||||||
|
const complex16_t* const* src = (const complex16_t* const*)samples;
|
||||||
|
int cnt = sample_count;
|
||||||
|
|
||||||
|
const lime::complex16_t** batch = new const lime::complex16_t*[chCount];
|
||||||
|
while(cnt > 0)
|
||||||
|
{
|
||||||
|
pkt.counter = 0;
|
||||||
|
pkt.reserved[0] = 0;
|
||||||
|
int samplesToSend = cnt > 1360/chCount ? 1360/chCount : cnt;
|
||||||
|
cnt -= samplesToSend;
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<chCount; ++i)
|
||||||
|
batch[i] = &src[i][samplesUsed];
|
||||||
|
samplesUsed += samplesToSend;
|
||||||
|
|
||||||
|
size_t bufPos = 0;
|
||||||
|
lime::fpga::Samples2FPGAPacketPayload(batch, samplesToSend, chCount, format, pkt.data, &bufPos);
|
||||||
|
int payloadSize = (bufPos / 4) * 4;
|
||||||
|
if(bufPos % 4 != 0)
|
||||||
|
lime::error("Packet samples count not multiple of 4");
|
||||||
|
pkt.reserved[2] = (payloadSize >> 8) & 0xFF; //WFM loading
|
||||||
|
pkt.reserved[1] = payloadSize & 0xFF; //WFM loading
|
||||||
|
pkt.reserved[0] = 0x1 << 5; //WFM loading
|
||||||
|
|
||||||
|
long bToSend = 16+payloadSize;
|
||||||
|
int context = BeginDataSending((char*)&pkt, bToSend );
|
||||||
|
if(WaitForSending(context, 250) == false)
|
||||||
|
{
|
||||||
|
FinishDataSending((char*)&pkt, bToSend , context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FinishDataSending((char*)&pkt, bToSend , context);
|
||||||
|
}
|
||||||
|
delete[] batch;
|
||||||
|
/*Give FX3 some time to load samples to FPGA*/
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
||||||
|
if(cnt == 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return ReportError(-1, "Failed to upload waveform");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Configures FPGA PLLs to LimeLight interface frequency
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::UpdateExternalDataRate(const size_t channel, const double txRate_Hz, const double rxRate_Hz, const double txPhase, const double rxPhase)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
lime::debug("ConnectionSTREAM::ConfigureFPGA_PLL(tx=%gMHz, rx=%gMHz)", txRate_Hz/1e6, rxRate_Hz/1e6);
|
||||||
|
#endif
|
||||||
|
const float txInterfaceClk = 2 * txRate_Hz;
|
||||||
|
const float rxInterfaceClk = 2 * rxRate_Hz;
|
||||||
|
mExpectedSampleRate = rxRate_Hz;
|
||||||
|
|
||||||
|
lime::fpga::FPGA_PLL_clock clocks[2];
|
||||||
|
clocks[0].bypass = false;
|
||||||
|
clocks[0].index = 0;
|
||||||
|
clocks[0].outFrequency = rxInterfaceClk;
|
||||||
|
clocks[0].phaseShift_deg = 0;
|
||||||
|
clocks[0].findPhase = false;
|
||||||
|
clocks[1].bypass = false;
|
||||||
|
clocks[1].index = 1;
|
||||||
|
clocks[1].outFrequency = rxInterfaceClk;
|
||||||
|
clocks[1].phaseShift_deg = rxPhase;
|
||||||
|
clocks[1].findPhase = false;
|
||||||
|
if (lime::fpga::SetPllFrequency(this, 1, rxInterfaceClk, clocks, 2)!=0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
clocks[0].bypass = false;
|
||||||
|
clocks[0].index = 0;
|
||||||
|
clocks[0].outFrequency = txInterfaceClk;
|
||||||
|
clocks[0].phaseShift_deg = 0;
|
||||||
|
clocks[0].findPhase = false;
|
||||||
|
clocks[1].bypass = false;
|
||||||
|
clocks[1].index = 1;
|
||||||
|
clocks[1].outFrequency = txInterfaceClk;
|
||||||
|
clocks[1].phaseShift_deg = txPhase;
|
||||||
|
clocks[1].findPhase = false;
|
||||||
|
if (lime::fpga::SetPllFrequency(this, 0, txInterfaceClk, clocks, 2)!=0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Configures FPGA PLLs to LimeLight interface frequency
|
||||||
|
*/
|
||||||
|
int ConnectionSTREAM::UpdateExternalDataRate(const size_t channel, const double txRate_Hz, const double rxRate_Hz)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
lime::debug("ConnectionSTREAM::ConfigureFPGA_PLL(tx=%gMHz, rx=%gMHz)", txRate_Hz/1e6, rxRate_Hz/1e6);
|
||||||
|
#endif
|
||||||
|
const float txInterfaceClk = 2 * txRate_Hz;
|
||||||
|
const float rxInterfaceClk = 2 * rxRate_Hz;
|
||||||
|
int status = 0;
|
||||||
|
uint32_t reg20;
|
||||||
|
const double rxPhC1[] = { 91.08, 89.46 };
|
||||||
|
const double rxPhC2[] = { -1 / 6e6, 1.24e-6 };
|
||||||
|
const double txPhC1[] = { 89.75, 89.61 };
|
||||||
|
const double txPhC2[] = { -3.0e-7, 2.71e-7 };
|
||||||
|
|
||||||
|
const std::vector<uint32_t> spiAddr = {0x0021, 0x0022, 0x0023, 0x0024,
|
||||||
|
0x0027, 0x002A, 0x0400, 0x040C,
|
||||||
|
0x040B, 0x0400, 0x040B, 0x0400};
|
||||||
|
const int bakRegCnt = spiAddr.size() - 4;
|
||||||
|
auto info = GetDeviceInfo();
|
||||||
|
const int addrLMS7002M = info.addrsLMS7002M.at(0);
|
||||||
|
bool phaseSearch = false;
|
||||||
|
if (this->chipVersion == 0x3841 && stoi(info.gatewareRevision) >= 7 && stoi(info.gatewareVersion) >= 2) //0x3840 LMS7002Mr2, 0x3841 LMS7002Mr3
|
||||||
|
if(rxInterfaceClk >= 5e6 || txInterfaceClk >= 5e6)
|
||||||
|
phaseSearch = true;
|
||||||
|
mExpectedSampleRate = rxRate_Hz;
|
||||||
|
|
||||||
|
std::vector<uint32_t> dataWr;
|
||||||
|
std::vector<uint32_t> dataRd;
|
||||||
|
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
dataWr.resize(spiAddr.size());
|
||||||
|
dataRd.resize(spiAddr.size());
|
||||||
|
//backup registers
|
||||||
|
dataWr[0] = (uint32_t(0x0020) << 16);
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), ®20, 1);
|
||||||
|
|
||||||
|
dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFD; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, 1);
|
||||||
|
|
||||||
|
for (int i = 0; i < bakRegCnt; ++i)
|
||||||
|
dataWr[i] = (spiAddr[i] << 16);
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), dataRd.data(), bakRegCnt);
|
||||||
|
UpdateThreads(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rxInterfaceClk >= 5e6)
|
||||||
|
{
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
const std::vector<uint32_t> spiData = { 0x0E9F, 0x07FF, 0x5550, 0xE4E4,
|
||||||
|
0xE4E4, 0x0086, 0x028D, 0x00FF,
|
||||||
|
0x5555, 0x02CD, 0xAAAA, 0x02ED};
|
||||||
|
//Load test config
|
||||||
|
const int setRegCnt = spiData.size();
|
||||||
|
for (int i = 0; i < setRegCnt; ++i)
|
||||||
|
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | spiData[i]; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, setRegCnt);
|
||||||
|
}
|
||||||
|
lime::fpga::FPGA_PLL_clock clocks[2];
|
||||||
|
clocks[0].bypass = false;
|
||||||
|
clocks[0].index = 0;
|
||||||
|
clocks[0].outFrequency = rxInterfaceClk;
|
||||||
|
clocks[0].phaseShift_deg = 0;
|
||||||
|
clocks[0].findPhase = false;
|
||||||
|
clocks[1].bypass = false;
|
||||||
|
clocks[1].index = 1;
|
||||||
|
clocks[1].outFrequency = rxInterfaceClk;
|
||||||
|
if (this->chipVersion == 0x3841)
|
||||||
|
clocks[1].phaseShift_deg = rxPhC1[1] + rxPhC2[1] * rxInterfaceClk;
|
||||||
|
else
|
||||||
|
clocks[1].phaseShift_deg = rxPhC1[0] + rxPhC2[0] * rxInterfaceClk;
|
||||||
|
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
clocks[1].findPhase = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clocks[1].findPhase = false;
|
||||||
|
}
|
||||||
|
status = lime::fpga::SetPllFrequency(this, 1, rxInterfaceClk, clocks, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
status = lime::fpga::SetDirectClocking(this, 1, rxInterfaceClk, 90);
|
||||||
|
|
||||||
|
if(txInterfaceClk >= 5e6)
|
||||||
|
{
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
const std::vector<uint32_t> spiData = {0x0E9F, 0x07FF, 0x5550, 0xE4E4,
|
||||||
|
0xE4E4, 0x0484};
|
||||||
|
WriteRegister(0x000A, 0x0000);
|
||||||
|
//Load test config
|
||||||
|
const int setRegCnt = spiData.size();
|
||||||
|
for (int i = 0; i < setRegCnt; ++i)
|
||||||
|
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | spiData[i]; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, setRegCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
lime::fpga::FPGA_PLL_clock clocks[2];
|
||||||
|
clocks[0].bypass = false;
|
||||||
|
clocks[0].index = 0;
|
||||||
|
clocks[0].outFrequency = txInterfaceClk;
|
||||||
|
clocks[0].phaseShift_deg = 0;
|
||||||
|
clocks[0].findPhase = false;
|
||||||
|
clocks[1].bypass = false;
|
||||||
|
clocks[1].index = 1;
|
||||||
|
clocks[1].outFrequency = txInterfaceClk;
|
||||||
|
if (this->chipVersion == 0x3841)
|
||||||
|
clocks[1].phaseShift_deg = txPhC1[1] + txPhC2[1] * txInterfaceClk;
|
||||||
|
else
|
||||||
|
clocks[1].phaseShift_deg = txPhC1[0] + txPhC2[0] * txInterfaceClk;
|
||||||
|
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
clocks[1].findPhase = true;
|
||||||
|
WriteRegister(0x000A, 0x0200);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clocks[1].findPhase = false;
|
||||||
|
}
|
||||||
|
status = lime::fpga::SetPllFrequency(this, 0, txInterfaceClk, clocks, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
status = lime::fpga::SetDirectClocking(this, 0, txInterfaceClk, 90);
|
||||||
|
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
//Restore registers
|
||||||
|
for (int i = 0; i < bakRegCnt; ++i)
|
||||||
|
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | dataRd[i]; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, bakRegCnt);
|
||||||
|
dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | reg20; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, 1);
|
||||||
|
WriteRegister(0x000A, 0);
|
||||||
|
UpdateThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectionSTREAM::ResetStreamBuffers()
|
||||||
|
{
|
||||||
|
//USB FIFO reset
|
||||||
|
LMS64CProtocol::GenericPacket ctrPkt;
|
||||||
|
ctrPkt.cmd = CMD_USB_FIFO_RST;
|
||||||
|
ctrPkt.outBuffer.push_back(0x00);
|
||||||
|
return TransferPacket(ctrPkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectionSTREAM::ReadRawStreamData(char* buffer, unsigned length, int timeout_ms)
|
||||||
|
{
|
||||||
|
fpga::StopStreaming(this);
|
||||||
|
|
||||||
|
ResetStreamBuffers();
|
||||||
|
WriteRegister(0x0008, 0x0100 | 0x2);
|
||||||
|
WriteRegister(0x0007, 1);
|
||||||
|
|
||||||
|
fpga::StartStreaming(this);
|
||||||
|
|
||||||
|
int handle = BeginDataReading(buffer, length);
|
||||||
|
if (WaitForReading(handle, timeout_ms) == false)
|
||||||
|
{
|
||||||
|
AbortReading();
|
||||||
|
}
|
||||||
|
|
||||||
|
fpga::StopStreaming(this);
|
||||||
|
|
||||||
|
int totalBytesReceived = FinishDataReading(buffer, length, handle);
|
||||||
|
|
||||||
|
return totalBytesReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Function dedicated for receiving data samples from board
|
||||||
|
@param rxFIFO FIFO to store received data
|
||||||
|
@param terminate periodically pooled flag to terminate thread
|
||||||
|
@param dataRate_Bps (optional) if not NULL periodically returns data rate in bytes per second
|
||||||
|
*/
|
||||||
|
void ConnectionSTREAM::ReceivePacketsLoop(const ThreadData args)
|
||||||
|
{
|
||||||
|
//auto dataPort = args.dataPort;
|
||||||
|
auto terminate = args.terminate;
|
||||||
|
auto dataRate_Bps = args.dataRate_Bps;
|
||||||
|
auto generateData = args.generateData;
|
||||||
|
auto safeToConfigInterface = args.safeToConfigInterface;
|
||||||
|
|
||||||
|
//at this point FPGA has to be already configured to output samples
|
||||||
|
const uint8_t chCount = args.channels.size();
|
||||||
|
const auto link = args.channels[0]->config.linkFormat;
|
||||||
|
const uint32_t samplesInPacket = (link == StreamConfig::STREAM_12_BIT_COMPRESSED ? 1360 : 1020)/chCount;
|
||||||
|
|
||||||
|
double latency=0;
|
||||||
|
for (int i = 0; i < chCount; i++)
|
||||||
|
{
|
||||||
|
latency += args.channels[i]->config.performanceLatency/chCount;
|
||||||
|
}
|
||||||
|
const unsigned tmp_cnt = (latency * 6)+0.5;
|
||||||
|
|
||||||
|
const uint8_t packetsToBatch = (1<<tmp_cnt);
|
||||||
|
const uint32_t bufferSize = packetsToBatch*sizeof(FPGA_DataPacket);
|
||||||
|
const uint8_t buffersCount = (tmp_cnt < 3) ? 32 : 16; // must be power of 2
|
||||||
|
vector<int> handles(buffersCount, 0);
|
||||||
|
vector<char>buffers(buffersCount*bufferSize, 0);
|
||||||
|
vector<StreamChannel::Frame> chFrames;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
chFrames.resize(chCount);
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc &ex)
|
||||||
|
{
|
||||||
|
ReportError("Error allocating Rx buffers, not enough memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t activeTransfers = 0;
|
||||||
|
for (int i = 0; i<buffersCount; ++i)
|
||||||
|
{
|
||||||
|
handles[i] = this->BeginDataReading(&buffers[i*bufferSize], bufferSize);
|
||||||
|
++activeTransfers;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bi = 0;
|
||||||
|
unsigned long totalBytesReceived = 0; //for data rate calculation
|
||||||
|
int m_bufferFailures = 0;
|
||||||
|
int32_t droppedSamples = 0;
|
||||||
|
int32_t packetLoss = 0;
|
||||||
|
|
||||||
|
vector<uint32_t> samplesCollected(chCount, 0);
|
||||||
|
vector<uint32_t> samplesReceived(chCount, 0);
|
||||||
|
|
||||||
|
auto t1 = chrono::high_resolution_clock::now();
|
||||||
|
auto t2 = chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
std::mutex txFlagsLock;
|
||||||
|
condition_variable resetTxFlags;
|
||||||
|
//worker thread for reseting late Tx packet flags
|
||||||
|
std::thread txReset([](ILimeSDRStreaming* port,
|
||||||
|
atomic<bool> *terminate,
|
||||||
|
mutex *spiLock,
|
||||||
|
condition_variable *doWork)
|
||||||
|
{
|
||||||
|
uint32_t reg9;
|
||||||
|
port->ReadRegister(0x0009, reg9);
|
||||||
|
const uint32_t addr[] = {0x0009, 0x0009};
|
||||||
|
const uint32_t data[] = {reg9 | (1 << 1), reg9 & ~(1 << 1)};
|
||||||
|
while (not terminate->load())
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lck(*spiLock);
|
||||||
|
doWork->wait(lck);
|
||||||
|
port->WriteRegisters(addr, data, 2);
|
||||||
|
}
|
||||||
|
}, this, terminate, &txFlagsLock, &resetTxFlags);
|
||||||
|
|
||||||
|
int resetFlagsDelay = 128;
|
||||||
|
uint64_t prevTs = 0;
|
||||||
|
while (terminate->load() == false)
|
||||||
|
{
|
||||||
|
if(generateData->load())
|
||||||
|
{
|
||||||
|
if(activeTransfers == 0) //stop FPGA when last transfer completes
|
||||||
|
fpga::StopStreaming(this);
|
||||||
|
safeToConfigInterface->notify_all(); //notify that it's safe to change chip config
|
||||||
|
const int batchSize = (this->mExpectedSampleRate/chFrames[0].samplesCount)/10;
|
||||||
|
IStreamChannel::Metadata meta;
|
||||||
|
for(int i=0; i<batchSize; ++i)
|
||||||
|
{
|
||||||
|
for(int ch=0; ch<chCount; ++ch)
|
||||||
|
{
|
||||||
|
meta.timestamp = chFrames[ch].timestamp;
|
||||||
|
for(int j=0; j<chFrames[ch].samplesCount; ++j)
|
||||||
|
{
|
||||||
|
chFrames[ch].samples[j].i = 0;
|
||||||
|
chFrames[ch].samples[j].q = 0;
|
||||||
|
}
|
||||||
|
uint32_t samplesPushed = args.channels[ch]->Write((const void*)chFrames[ch].samples, chFrames[ch].samplesCount, &meta);
|
||||||
|
samplesReceived[ch] += chFrames[ch].samplesCount;
|
||||||
|
if(samplesPushed != chFrames[ch].samplesCount)
|
||||||
|
lime::warning("Rx samples pushed %i/%i", samplesPushed, chFrames[ch].samplesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this_thread::sleep_for(chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
int32_t bytesReceived = 0;
|
||||||
|
if(handles[bi] >= 0)
|
||||||
|
{
|
||||||
|
if (this->WaitForReading(handles[bi], 1000) == false)
|
||||||
|
++m_bufferFailures;
|
||||||
|
bytesReceived = this->FinishDataReading(&buffers[bi*bufferSize], bufferSize, handles[bi]);
|
||||||
|
--activeTransfers;
|
||||||
|
totalBytesReceived += bytesReceived;
|
||||||
|
if (bytesReceived != int32_t(bufferSize)) //data should come in full sized packets
|
||||||
|
++m_bufferFailures;
|
||||||
|
}
|
||||||
|
bool txLate=false;
|
||||||
|
for (uint8_t pktIndex = 0; pktIndex < bytesReceived / sizeof(FPGA_DataPacket); ++pktIndex)
|
||||||
|
{
|
||||||
|
const FPGA_DataPacket* pkt = (FPGA_DataPacket*)&buffers[bi*bufferSize];
|
||||||
|
const uint8_t byte0 = pkt[pktIndex].reserved[0];
|
||||||
|
if ((byte0 & (1 << 3)) != 0 && !txLate) //report only once per batch
|
||||||
|
{
|
||||||
|
txLate = true;
|
||||||
|
if(resetFlagsDelay > 0)
|
||||||
|
--resetFlagsDelay;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lime::info("L %llu", (unsigned long long)pkt[pktIndex].counter);
|
||||||
|
resetTxFlags.notify_one();
|
||||||
|
resetFlagsDelay = packetsToBatch*buffersCount;
|
||||||
|
if (args.reportLateTx) args.reportLateTx(pkt[pktIndex].counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t* pktStart = (uint8_t*)pkt[pktIndex].data;
|
||||||
|
if(pkt[pktIndex].counter - prevTs != samplesInPacket && pkt[pktIndex].counter != prevTs)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
lime::debug("\tRx pktLoss@%i - ts diff: %li pktLoss: %.1f", pktIndex, pkt[pktIndex].counter - prevTs, float(pkt[pktIndex].counter - prevTs)/samplesInPacket);
|
||||||
|
#endif
|
||||||
|
packetLoss += (pkt[pktIndex].counter - prevTs)/samplesInPacket;
|
||||||
|
}
|
||||||
|
prevTs = pkt[pktIndex].counter;
|
||||||
|
if(args.lastTimestamp)
|
||||||
|
args.lastTimestamp->store(pkt[pktIndex].counter);
|
||||||
|
//parse samples
|
||||||
|
vector<complex16_t*> dest(chCount);
|
||||||
|
for(uint8_t c=0; c<chCount; ++c)
|
||||||
|
dest[c] = (chFrames[c].samples);
|
||||||
|
size_t samplesCount = 0;
|
||||||
|
fpga::FPGAPacketPayload2Samples(pktStart, 4080, chCount, link, dest.data(), &samplesCount);
|
||||||
|
|
||||||
|
for(int ch=0; ch<chCount; ++ch)
|
||||||
|
{
|
||||||
|
IStreamChannel::Metadata meta;
|
||||||
|
meta.timestamp = pkt[pktIndex].counter;
|
||||||
|
meta.flags = RingFIFO::OVERWRITE_OLD;
|
||||||
|
uint32_t samplesPushed = args.channels[ch]->Write((const void*)chFrames[ch].samples, samplesCount, &meta, 100);
|
||||||
|
if(samplesPushed != samplesCount)
|
||||||
|
droppedSamples += samplesCount-samplesPushed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Re-submit this request to keep the queue full
|
||||||
|
if(not generateData->load())
|
||||||
|
{
|
||||||
|
if(activeTransfers == 0) //reactivate FPGA and USB transfers
|
||||||
|
fpga::StartStreaming(this);
|
||||||
|
for(int i=0; i<buffersCount-activeTransfers; ++i)
|
||||||
|
{
|
||||||
|
handles[bi] = this->BeginDataReading(&buffers[bi*bufferSize], bufferSize);
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
++activeTransfers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handles[bi] = -1;
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
}
|
||||||
|
t2 = chrono::high_resolution_clock::now();
|
||||||
|
auto timePeriod = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
|
||||||
|
if (timePeriod >= 1000)
|
||||||
|
{
|
||||||
|
t1 = t2;
|
||||||
|
//total number of bytes sent per second
|
||||||
|
double dataRate = 1000.0*totalBytesReceived / timePeriod;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
//each channel sample rate
|
||||||
|
float samplingRate = 1000.0*samplesReceived[0] / timePeriod;
|
||||||
|
lime::debug("Rx: %.3f MB/s, Fs: %.3f MHz, overrun: %i, loss: %i", dataRate / 1000000.0, samplingRate / 1000000.0, droppedSamples, packetLoss);
|
||||||
|
#endif
|
||||||
|
samplesReceived[0] = 0;
|
||||||
|
totalBytesReceived = 0;
|
||||||
|
m_bufferFailures = 0;
|
||||||
|
droppedSamples = 0;
|
||||||
|
packetLoss = 0;
|
||||||
|
|
||||||
|
if (dataRate_Bps)
|
||||||
|
dataRate_Bps->store((uint32_t)dataRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->AbortReading();
|
||||||
|
for (int j = 0; j<buffersCount; j++)
|
||||||
|
{
|
||||||
|
if(handles[bi] >= 0)
|
||||||
|
{
|
||||||
|
this->WaitForReading(handles[bi], 1000);
|
||||||
|
this->FinishDataReading(&buffers[bi*bufferSize], bufferSize, handles[bi]);
|
||||||
|
}
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
}
|
||||||
|
resetTxFlags.notify_one();
|
||||||
|
txReset.join();
|
||||||
|
if (dataRate_Bps)
|
||||||
|
dataRate_Bps->store(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Functions dedicated for transmitting packets to board
|
||||||
|
@param txFIFO data source FIFO
|
||||||
|
@param terminate periodically pooled flag to terminate thread
|
||||||
|
@param dataRate_Bps (optional) if not NULL periodically returns data rate in bytes per second
|
||||||
|
*/
|
||||||
|
void ConnectionSTREAM::TransmitPacketsLoop(const ThreadData args)
|
||||||
|
{
|
||||||
|
//auto dataPort = args.dataPort;
|
||||||
|
auto terminate = args.terminate;
|
||||||
|
auto dataRate_Bps = args.dataRate_Bps;
|
||||||
|
|
||||||
|
//at this point FPGA has to be already configured to output samples
|
||||||
|
const uint8_t maxChannelCount = 2;
|
||||||
|
const uint8_t chCount = args.channels.size();
|
||||||
|
const auto link = args.channels[0]->config.linkFormat;
|
||||||
|
|
||||||
|
double latency=0;
|
||||||
|
for (int i = 0; i < chCount; i++)
|
||||||
|
{
|
||||||
|
latency += args.channels[i]->config.performanceLatency/chCount;
|
||||||
|
}
|
||||||
|
const unsigned tmp_cnt = (latency * 6)+0.5;
|
||||||
|
|
||||||
|
const uint8_t buffersCount = 16; // must be power of 2
|
||||||
|
assert(buffersCount % 2 == 0);
|
||||||
|
const uint8_t packetsToBatch = (1<<tmp_cnt); //packets in single USB transfer
|
||||||
|
const uint32_t bufferSize = packetsToBatch*4096;
|
||||||
|
const uint32_t popTimeout_ms = 100;
|
||||||
|
|
||||||
|
const int maxSamplesBatch = (link==StreamConfig::STREAM_12_BIT_COMPRESSED?1360:1020)/chCount;
|
||||||
|
vector<int> handles(buffersCount, 0);
|
||||||
|
vector<bool> bufferUsed(buffersCount, 0);
|
||||||
|
vector<uint32_t> bytesToSend(buffersCount, 0);
|
||||||
|
vector<complex16_t> samples[maxChannelCount];
|
||||||
|
vector<char> buffers;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for(int i=0; i<chCount; ++i)
|
||||||
|
samples[i].resize(maxSamplesBatch);
|
||||||
|
buffers.resize(buffersCount*bufferSize, 0);
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc& ex) //not enough memory for buffers
|
||||||
|
{
|
||||||
|
lime::error("Error allocating Tx buffers, not enough memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_bufferFailures = 0;
|
||||||
|
long totalBytesSent = 0;
|
||||||
|
|
||||||
|
uint32_t samplesSent = 0;
|
||||||
|
|
||||||
|
auto t1 = chrono::high_resolution_clock::now();
|
||||||
|
auto t2 = chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
uint8_t bi = 0; //buffer index
|
||||||
|
while (terminate->load() != true)
|
||||||
|
{
|
||||||
|
if (bufferUsed[bi])
|
||||||
|
{
|
||||||
|
if (this->WaitForSending(handles[bi], 1000) == false)
|
||||||
|
++m_bufferFailures;
|
||||||
|
uint32_t bytesSent = this->FinishDataSending(&buffers[bi*bufferSize], bytesToSend[bi], handles[bi]);
|
||||||
|
totalBytesSent += bytesSent;
|
||||||
|
if (bytesSent != bytesToSend[bi])
|
||||||
|
++m_bufferFailures;
|
||||||
|
bufferUsed[bi] = false;
|
||||||
|
}
|
||||||
|
int i=0;
|
||||||
|
|
||||||
|
while(i<packetsToBatch && terminate->load() != true)
|
||||||
|
{
|
||||||
|
IStreamChannel::Metadata meta;
|
||||||
|
FPGA_DataPacket* pkt = reinterpret_cast<FPGA_DataPacket*>(&buffers[bi*bufferSize]);
|
||||||
|
for(int ch=0; ch<chCount; ++ch)
|
||||||
|
{
|
||||||
|
int samplesPopped = args.channels[ch]->Read(samples[ch].data(), maxSamplesBatch, &meta, popTimeout_ms);
|
||||||
|
if (samplesPopped != maxSamplesBatch)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
lime::warning("popping from TX, samples popped %i/%i", samplesPopped, maxSamplesBatch);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if(terminate->load() == true) //early termination
|
||||||
|
break;
|
||||||
|
pkt[i].counter = meta.timestamp;
|
||||||
|
pkt[i].reserved[0] = 0;
|
||||||
|
//by default ignore timestamps
|
||||||
|
const int ignoreTimestamp = !(meta.flags & IStreamChannel::Metadata::SYNC_TIMESTAMP);
|
||||||
|
pkt[i].reserved[0] |= ((int)ignoreTimestamp << 4); //ignore timestamp
|
||||||
|
|
||||||
|
vector<complex16_t*> src(chCount);
|
||||||
|
for(uint8_t c=0; c<chCount; ++c)
|
||||||
|
src[c] = (samples[c].data());
|
||||||
|
uint8_t* const dataStart = (uint8_t*)pkt[i].data;
|
||||||
|
fpga::Samples2FPGAPacketPayload(src.data(), maxSamplesBatch, chCount, link, dataStart, nullptr);
|
||||||
|
samplesSent += maxSamplesBatch;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesToSend[bi] = bufferSize;
|
||||||
|
handles[bi] = this->BeginDataSending(&buffers[bi*bufferSize], bytesToSend[bi]);
|
||||||
|
bufferUsed[bi] = true;
|
||||||
|
|
||||||
|
t2 = chrono::high_resolution_clock::now();
|
||||||
|
auto timePeriod = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
|
||||||
|
if (timePeriod >= 1000)
|
||||||
|
{
|
||||||
|
//total number of bytes sent per second
|
||||||
|
float dataRate = 1000.0*totalBytesSent / timePeriod;
|
||||||
|
if(dataRate_Bps)
|
||||||
|
dataRate_Bps->store(dataRate);
|
||||||
|
m_bufferFailures = 0;
|
||||||
|
samplesSent = 0;
|
||||||
|
totalBytesSent = 0;
|
||||||
|
t1 = t2;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
//total number of samples from all channels per second
|
||||||
|
float sampleRate = 1000.0*samplesSent / timePeriod;
|
||||||
|
lime::debug("Tx: %.3f MB/s, Fs: %.3f MHz, failures: %i", dataRate / 1000000.0, sampleRate / 1000000.0, m_bufferFailures);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all the queued requests to be cancelled
|
||||||
|
this->AbortSending();
|
||||||
|
for (int j = 0; j<buffersCount; j++)
|
||||||
|
{
|
||||||
|
if (bufferUsed[bi])
|
||||||
|
{
|
||||||
|
this->WaitForSending(handles[bi], 1000);
|
||||||
|
this->FinishDataSending(&buffers[bi*bufferSize], bufferSize, handles[bi]);
|
||||||
|
}
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
}
|
||||||
|
if (dataRate_Bps)
|
||||||
|
dataRate_Bps->store(0);
|
||||||
|
}
|
||||||
|
|
717
liblimesuite/srcmw/Connection_uLimeSDR/Connection_uLimeSDR.cpp
Normal file
717
liblimesuite/srcmw/Connection_uLimeSDR/Connection_uLimeSDR.cpp
Normal file
@ -0,0 +1,717 @@
|
|||||||
|
/**
|
||||||
|
@file Connection_uLimeSDR.cpp
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief Implementation of uLimeSDR board connection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Connection_uLimeSDR.h"
|
||||||
|
#include "ErrorReporting.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <FPGA_common.h>
|
||||||
|
#include <LMS7002M.h>
|
||||||
|
#include <ciso646>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace lime;
|
||||||
|
|
||||||
|
#define __unix__
|
||||||
|
|
||||||
|
Connection_uLimeSDR::Connection_uLimeSDR(void *arg)
|
||||||
|
{
|
||||||
|
RxLoopFunction = bind(&Connection_uLimeSDR::ReceivePacketsLoop, this, std::placeholders::_1);
|
||||||
|
TxLoopFunction = bind(&Connection_uLimeSDR::TransmitPacketsLoop, this, std::placeholders::_1);
|
||||||
|
|
||||||
|
isConnected = false;
|
||||||
|
|
||||||
|
mStreamWrEndPtAddr = 0x03;
|
||||||
|
mStreamRdEndPtAddr = 0x83;
|
||||||
|
isConnected = false;
|
||||||
|
txSize = 0;
|
||||||
|
rxSize = 0;
|
||||||
|
#ifndef __unix__
|
||||||
|
mFTHandle = NULL;
|
||||||
|
#else
|
||||||
|
dev_handle = 0;
|
||||||
|
devs = 0;
|
||||||
|
mUsbCounter = 0;
|
||||||
|
ctx = (libusb_context *)arg;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Initializes port type and object necessary to communicate to usb device.
|
||||||
|
*/
|
||||||
|
Connection_uLimeSDR::Connection_uLimeSDR(void *arg, const unsigned index, const int vid, const int pid)
|
||||||
|
{
|
||||||
|
RxLoopFunction = bind(&Connection_uLimeSDR::ReceivePacketsLoop, this, std::placeholders::_1);
|
||||||
|
TxLoopFunction = bind(&Connection_uLimeSDR::TransmitPacketsLoop, this, std::placeholders::_1);
|
||||||
|
mTimestampOffset = 0;
|
||||||
|
rxLastTimestamp.store(0);
|
||||||
|
mExpectedSampleRate = 0;
|
||||||
|
generateData.store(false);
|
||||||
|
rxRunning.store(false);
|
||||||
|
txRunning.store(false);
|
||||||
|
isConnected = false;
|
||||||
|
terminateRx.store(false);
|
||||||
|
terminateTx.store(false);
|
||||||
|
rxDataRate_Bps.store(0);
|
||||||
|
txDataRate_Bps.store(0);
|
||||||
|
|
||||||
|
mStreamWrEndPtAddr = 0x03;
|
||||||
|
mStreamRdEndPtAddr = 0x83;
|
||||||
|
isConnected = false;
|
||||||
|
txSize = 0;
|
||||||
|
rxSize = 0;
|
||||||
|
#ifndef __unix__
|
||||||
|
mFTHandle = NULL;
|
||||||
|
#else
|
||||||
|
dev_handle = 0;
|
||||||
|
devs = 0;
|
||||||
|
mUsbCounter = 0;
|
||||||
|
ctx = (libusb_context *)arg;
|
||||||
|
#endif
|
||||||
|
if (this->Open(index, vid, pid) != 0)
|
||||||
|
std::cerr << GetLastErrorMessage() << std::endl;
|
||||||
|
this->SetReferenceClockRate(52e6);
|
||||||
|
GetChipVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Closes connection to chip and deallocates used memory.
|
||||||
|
*/
|
||||||
|
Connection_uLimeSDR::~Connection_uLimeSDR()
|
||||||
|
{
|
||||||
|
for(auto i : mTxStreams)
|
||||||
|
ControlStream((size_t)i, false);
|
||||||
|
for(auto i : mRxStreams)
|
||||||
|
ControlStream((size_t)i, false);
|
||||||
|
for(auto i : mTxStreams)
|
||||||
|
CloseStream((size_t)i);
|
||||||
|
for(auto i : mRxStreams)
|
||||||
|
CloseStream((size_t)i);
|
||||||
|
UpdateThreads();
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
#ifdef __unix__
|
||||||
|
int Connection_uLimeSDR::FT_FlushPipe(unsigned char ep)
|
||||||
|
{
|
||||||
|
int actual = 0;
|
||||||
|
unsigned char wbuffer[20]={0};
|
||||||
|
|
||||||
|
mUsbCounter++;
|
||||||
|
wbuffer[0] = (mUsbCounter)&0xFF;
|
||||||
|
wbuffer[1] = (mUsbCounter>>8)&0xFF;
|
||||||
|
wbuffer[2] = (mUsbCounter>>16)&0xFF;
|
||||||
|
wbuffer[3] = (mUsbCounter>>24)&0xFF;
|
||||||
|
wbuffer[4] = ep;
|
||||||
|
libusb_bulk_transfer(dev_handle, 0x01, wbuffer, 20, &actual, 1000);
|
||||||
|
if (actual != 20)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mUsbCounter++;
|
||||||
|
wbuffer[0] = (mUsbCounter)&0xFF;
|
||||||
|
wbuffer[1] = (mUsbCounter>>8)&0xFF;
|
||||||
|
wbuffer[2] = (mUsbCounter>>16)&0xFF;
|
||||||
|
wbuffer[3] = (mUsbCounter>>24)&0xFF;
|
||||||
|
wbuffer[4] = ep;
|
||||||
|
wbuffer[5] = 0x03;
|
||||||
|
libusb_bulk_transfer(dev_handle, 0x01, wbuffer, 20, &actual, 1000);
|
||||||
|
if (actual != 20)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Connection_uLimeSDR::FT_SetStreamPipe(unsigned char ep, size_t size)
|
||||||
|
{
|
||||||
|
|
||||||
|
int actual = 0;
|
||||||
|
unsigned char wbuffer[20]={0};
|
||||||
|
|
||||||
|
mUsbCounter++;
|
||||||
|
wbuffer[0] = (mUsbCounter)&0xFF;
|
||||||
|
wbuffer[1] = (mUsbCounter>>8)&0xFF;
|
||||||
|
wbuffer[2] = (mUsbCounter>>16)&0xFF;
|
||||||
|
wbuffer[3] = (mUsbCounter>>24)&0xFF;
|
||||||
|
wbuffer[4] = ep;
|
||||||
|
libusb_bulk_transfer(dev_handle, 0x01, wbuffer, 20, &actual, 1000);
|
||||||
|
if (actual != 20)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mUsbCounter++;
|
||||||
|
wbuffer[0] = (mUsbCounter)&0xFF;
|
||||||
|
wbuffer[1] = (mUsbCounter>>8)&0xFF;
|
||||||
|
wbuffer[2] = (mUsbCounter>>16)&0xFF;
|
||||||
|
wbuffer[3] = (mUsbCounter>>24)&0xFF;
|
||||||
|
wbuffer[5] = 0x02;
|
||||||
|
wbuffer[8] = (size)&0xFF;
|
||||||
|
wbuffer[9] = (size>>8)&0xFF;
|
||||||
|
wbuffer[10] = (size>>16)&0xFF;
|
||||||
|
wbuffer[11] = (size>>24)&0xFF;
|
||||||
|
libusb_bulk_transfer(dev_handle, 0x01, wbuffer, 20, &actual, 1000);
|
||||||
|
if (actual != 20)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Tries to open connected USB device and find communication endpoints.
|
||||||
|
@return Returns 0-Success, other-EndPoints not found or device didn't connect.
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::Open(const unsigned index, const int vid, const int pid)
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
DWORD devCount;
|
||||||
|
FT_STATUS ftStatus = FT_OK;
|
||||||
|
DWORD dwNumDevices = 0;
|
||||||
|
// Open a device
|
||||||
|
ftStatus = FT_Create(0, FT_OPEN_BY_INDEX, &mFTHandle);
|
||||||
|
if (FT_FAILED(ftStatus))
|
||||||
|
{
|
||||||
|
ReportError(ENODEV, "Failed to list USB Devices");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
FT_AbortPipe(mFTHandle, mStreamRdEndPtAddr);
|
||||||
|
FT_AbortPipe(mFTHandle, 0x82);
|
||||||
|
FT_AbortPipe(mFTHandle, 0x02);
|
||||||
|
FT_AbortPipe(mFTHandle, mStreamWrEndPtAddr);
|
||||||
|
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, 0x82, 64);
|
||||||
|
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, 0x02, 64);
|
||||||
|
FT_SetPipeTimeout(mFTHandle, 0x02, 500);
|
||||||
|
FT_SetPipeTimeout(mFTHandle, 0x82, 500);
|
||||||
|
isConnected = true;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
dev_handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
|
||||||
|
|
||||||
|
if(dev_handle == nullptr)
|
||||||
|
return ReportError(ENODEV, "libusb_open failed");
|
||||||
|
libusb_reset_device(dev_handle);
|
||||||
|
if(libusb_kernel_driver_active(dev_handle, 1) == 1) //find out if kernel driver is attached
|
||||||
|
{
|
||||||
|
printf("Kernel Driver Active\n");
|
||||||
|
if(libusb_detach_kernel_driver(dev_handle, 1) == 0) //detach it
|
||||||
|
printf("Kernel Driver Detached!\n");
|
||||||
|
}
|
||||||
|
int r = libusb_claim_interface(dev_handle, 1); //claim interface 0 (the first) of device
|
||||||
|
if(r < 0)
|
||||||
|
{
|
||||||
|
printf("Cannot Claim Interface\n");
|
||||||
|
return ReportError(-1, "Cannot claim interface - %s", libusb_strerror(libusb_error(r)));
|
||||||
|
}
|
||||||
|
r = libusb_claim_interface(dev_handle, 1); //claim interface 0 (the first) of device
|
||||||
|
if(r < 0)
|
||||||
|
{
|
||||||
|
printf("Cannot Claim Interface\n");
|
||||||
|
return ReportError(-1, "Cannot claim interface - %s", libusb_strerror(libusb_error(r)));
|
||||||
|
}
|
||||||
|
printf("Claimed Interface\n");
|
||||||
|
|
||||||
|
FT_SetStreamPipe(0x82,64);
|
||||||
|
FT_SetStreamPipe(0x02,64);
|
||||||
|
isConnected = true;
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Closes communication to device.
|
||||||
|
*/
|
||||||
|
void Connection_uLimeSDR::Close()
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
FT_Close(mFTHandle);
|
||||||
|
#else
|
||||||
|
if(dev_handle != 0)
|
||||||
|
{
|
||||||
|
FT_FlushPipe(mStreamRdEndPtAddr);
|
||||||
|
FT_FlushPipe(0x82);
|
||||||
|
libusb_release_interface(dev_handle, 1);
|
||||||
|
libusb_close(dev_handle);
|
||||||
|
dev_handle = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
isConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Returns connection status
|
||||||
|
@return 1-connection open, 0-connection closed.
|
||||||
|
*/
|
||||||
|
bool Connection_uLimeSDR::IsOpen()
|
||||||
|
{
|
||||||
|
return isConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __unix__
|
||||||
|
int Connection_uLimeSDR::ReinitPipe(unsigned char ep)
|
||||||
|
{
|
||||||
|
FT_AbortPipe(mFTHandle, ep);
|
||||||
|
FT_FlushPipe(mFTHandle, ep);
|
||||||
|
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, ep, 64);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Sends given data buffer to chip through USB port.
|
||||||
|
@param buffer data buffer, must not be longer than 64 bytes.
|
||||||
|
@param length given buffer size.
|
||||||
|
@param timeout_ms timeout limit for operation in milliseconds
|
||||||
|
@return number of bytes sent.
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::Write(const unsigned char *buffer, const int length, int timeout_ms)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mExtraUsbMutex);
|
||||||
|
long len = 0;
|
||||||
|
if (IsOpen() == false)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#ifndef __unix__
|
||||||
|
// Write to channel 1 ep 0x02
|
||||||
|
ULONG ulBytesWrite = 0;
|
||||||
|
FT_STATUS ftStatus = FT_OK;
|
||||||
|
OVERLAPPED vOverlapped = { 0 };
|
||||||
|
FT_InitializeOverlapped(mFTHandle, &vOverlapped);
|
||||||
|
ftStatus = FT_WritePipe(mFTHandle, 0x02, (unsigned char*)buffer, length, &ulBytesWrite, &vOverlapped);
|
||||||
|
if (ftStatus != FT_IO_PENDING)
|
||||||
|
{
|
||||||
|
FT_ReleaseOverlapped(mFTHandle, &vOverlapped);
|
||||||
|
ReinitPipe(0x02);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD dwRet = WaitForSingleObject(vOverlapped.hEvent, timeout_ms);
|
||||||
|
if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_TIMEOUT)
|
||||||
|
{
|
||||||
|
if (GetOverlappedResult(mFTHandle, &vOverlapped, &ulBytesWrite, FALSE) == FALSE)
|
||||||
|
{
|
||||||
|
ReinitPipe(0x02);
|
||||||
|
ulBytesWrite = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReinitPipe(0x02);
|
||||||
|
ulBytesWrite = -1;
|
||||||
|
}
|
||||||
|
FT_ReleaseOverlapped(mFTHandle, &vOverlapped);
|
||||||
|
return ulBytesWrite;
|
||||||
|
#else
|
||||||
|
unsigned char* wbuffer = new unsigned char[length];
|
||||||
|
memcpy(wbuffer, buffer, length);
|
||||||
|
int actual = 0;
|
||||||
|
libusb_bulk_transfer(dev_handle, 0x02, wbuffer, length, &actual, timeout_ms);
|
||||||
|
len = actual;
|
||||||
|
delete[] wbuffer;
|
||||||
|
return len;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Reads data coming from the chip through USB port.
|
||||||
|
@param buffer pointer to array where received data will be copied, array must be
|
||||||
|
big enough to fit received data.
|
||||||
|
@param length number of bytes to read from chip.
|
||||||
|
@param timeout_ms timeout limit for operation in milliseconds
|
||||||
|
@return number of bytes received.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int Connection_uLimeSDR::Read(unsigned char *buffer, const int length, int timeout_ms)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mExtraUsbMutex);
|
||||||
|
long len = length;
|
||||||
|
if(IsOpen() == false)
|
||||||
|
return 0;
|
||||||
|
#ifndef __unix__
|
||||||
|
//
|
||||||
|
// Read from channel 1 ep 0x82
|
||||||
|
//
|
||||||
|
ULONG ulBytesRead = 0;
|
||||||
|
FT_STATUS ftStatus = FT_OK;
|
||||||
|
OVERLAPPED vOverlapped = { 0 };
|
||||||
|
FT_InitializeOverlapped(mFTHandle, &vOverlapped);
|
||||||
|
ftStatus = FT_ReadPipe(mFTHandle, 0x82, buffer, length, &ulBytesRead, &vOverlapped);
|
||||||
|
if (ftStatus != FT_IO_PENDING)
|
||||||
|
{
|
||||||
|
FT_ReleaseOverlapped(mFTHandle, &vOverlapped);
|
||||||
|
ReinitPipe(0x82);
|
||||||
|
return -1;;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD dwRet = WaitForSingleObject(vOverlapped.hEvent, timeout_ms);
|
||||||
|
if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_TIMEOUT)
|
||||||
|
{
|
||||||
|
if (GetOverlappedResult(mFTHandle, &vOverlapped, &ulBytesRead, FALSE)==FALSE)
|
||||||
|
{
|
||||||
|
ReinitPipe(0x82);
|
||||||
|
ulBytesRead = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReinitPipe(0x82);
|
||||||
|
ulBytesRead = -1;
|
||||||
|
}
|
||||||
|
FT_ReleaseOverlapped(mFTHandle, &vOverlapped);
|
||||||
|
return ulBytesRead;
|
||||||
|
#else
|
||||||
|
int actual = 0;
|
||||||
|
libusb_bulk_transfer(dev_handle, 0x82, buffer, len, &actual, timeout_ms);
|
||||||
|
len = actual;
|
||||||
|
#endif
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __unix__
|
||||||
|
/** @brief Function for handling libusb callbacks
|
||||||
|
*/
|
||||||
|
static void callback_libusbtransfer(libusb_transfer *trans)
|
||||||
|
{
|
||||||
|
Connection_uLimeSDR::USBTransferContext *context = reinterpret_cast<Connection_uLimeSDR::USBTransferContext*>(trans->user_data);
|
||||||
|
std::unique_lock<std::mutex> lck(context->transferLock);
|
||||||
|
switch(trans->status)
|
||||||
|
{
|
||||||
|
case LIBUSB_TRANSFER_CANCELLED:
|
||||||
|
//printf("Transfer %i canceled\n", context->id);
|
||||||
|
context->bytesXfered = trans->actual_length;
|
||||||
|
context->done.store(true);
|
||||||
|
//context->used = false;
|
||||||
|
//context->reset();
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_COMPLETED:
|
||||||
|
//if(trans->actual_length == context->bytesExpected)
|
||||||
|
{
|
||||||
|
context->bytesXfered = trans->actual_length;
|
||||||
|
context->done.store(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_ERROR:
|
||||||
|
printf("TRANSFER ERRRO\n");
|
||||||
|
context->bytesXfered = trans->actual_length;
|
||||||
|
context->done.store(true);
|
||||||
|
//context->used = false;
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_TIMED_OUT:
|
||||||
|
//printf("transfer timed out %i\n", context->id);
|
||||||
|
context->bytesXfered = trans->actual_length;
|
||||||
|
context->done.store(true);
|
||||||
|
//context->used = false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_OVERFLOW:
|
||||||
|
printf("transfer overflow\n");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_STALL:
|
||||||
|
printf("transfer stalled\n");
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_NO_DEVICE:
|
||||||
|
printf("transfer no device\n");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lck.unlock();
|
||||||
|
context->cv.notify_one();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Starts asynchronous data reading from board
|
||||||
|
@param *buffer buffer where to store received data
|
||||||
|
@param length number of bytes to read
|
||||||
|
@return handle of transfer context
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::BeginDataReading(char *buffer, uint32_t length)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
bool contextFound = false;
|
||||||
|
//find not used context
|
||||||
|
for(i = 0; i<USB_MAX_CONTEXTS; i++)
|
||||||
|
{
|
||||||
|
if(!contexts[i].used)
|
||||||
|
{
|
||||||
|
contextFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!contextFound)
|
||||||
|
{
|
||||||
|
printf("No contexts left for reading data\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
contexts[i].used = true;
|
||||||
|
|
||||||
|
#ifndef __unix__
|
||||||
|
if (length != rxSize)
|
||||||
|
{
|
||||||
|
rxSize = length;
|
||||||
|
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, mStreamRdEndPtAddr, rxSize);
|
||||||
|
}
|
||||||
|
FT_InitializeOverlapped(mFTHandle, &contexts[i].inOvLap);
|
||||||
|
ULONG ulActual;
|
||||||
|
FT_STATUS ftStatus = FT_OK;
|
||||||
|
ftStatus = FT_ReadPipe(mFTHandle, mStreamRdEndPtAddr, (unsigned char*)buffer, length, &ulActual, &contexts[i].inOvLap);
|
||||||
|
if (ftStatus != FT_IO_PENDING)
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
if (length != rxSize)
|
||||||
|
{
|
||||||
|
rxSize = length;
|
||||||
|
FT_SetStreamPipe(mStreamRdEndPtAddr,rxSize);
|
||||||
|
}
|
||||||
|
unsigned int Timeout = 500;
|
||||||
|
libusb_transfer *tr = contexts[i].transfer;
|
||||||
|
libusb_fill_bulk_transfer(tr, dev_handle, mStreamRdEndPtAddr, (unsigned char*)buffer, length, callback_libusbtransfer, &contexts[i], Timeout);
|
||||||
|
contexts[i].done = false;
|
||||||
|
contexts[i].bytesXfered = 0;
|
||||||
|
contexts[i].bytesExpected = length;
|
||||||
|
int status = libusb_submit_transfer(tr);
|
||||||
|
if(status != 0)
|
||||||
|
{
|
||||||
|
printf("ERROR BEGIN DATA READING %s\n", libusb_error_name(status));
|
||||||
|
contexts[i].used = false;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Waits for asynchronous data reception
|
||||||
|
@param contextHandle handle of which context data to wait
|
||||||
|
@param timeout_ms number of miliseconds to wait
|
||||||
|
@return 1-data received, 0-data not received
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::WaitForReading(int contextHandle, unsigned int timeout_ms)
|
||||||
|
{
|
||||||
|
if(contextHandle >= 0 && contexts[contextHandle].used == true)
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
DWORD dwRet = WaitForSingleObject(contexts[contextHandle].inOvLap.hEvent, timeout_ms);
|
||||||
|
if (dwRet == WAIT_OBJECT_0)
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
auto t1 = chrono::high_resolution_clock::now();
|
||||||
|
auto t2 = chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lck(contexts[contextHandle].transferLock);
|
||||||
|
while(contexts[contextHandle].done.load() == false && std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() < timeout_ms)
|
||||||
|
{
|
||||||
|
//blocking not to waste CPU
|
||||||
|
contexts[contextHandle].cv.wait_for(lck, chrono::milliseconds(timeout_ms));
|
||||||
|
t2 = chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
return contexts[contextHandle].done.load() == true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Finishes asynchronous data reading from board
|
||||||
|
@param buffer array where to store received data
|
||||||
|
@param length number of bytes to read
|
||||||
|
@param contextHandle handle of which context to finish
|
||||||
|
@return false failure, true number of bytes received
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::FinishDataReading(char *buffer, uint32_t length, int contextHandle)
|
||||||
|
{
|
||||||
|
if(contextHandle >= 0 && contexts[contextHandle].used == true)
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
ULONG ulActualBytesTransferred;
|
||||||
|
FT_STATUS ftStatus = FT_OK;
|
||||||
|
|
||||||
|
ftStatus = FT_GetOverlappedResult(mFTHandle, &contexts[contextHandle].inOvLap, &ulActualBytesTransferred, FALSE);
|
||||||
|
if (ftStatus != FT_OK)
|
||||||
|
length = 0;
|
||||||
|
else
|
||||||
|
length = ulActualBytesTransferred;
|
||||||
|
FT_ReleaseOverlapped(mFTHandle, &contexts[contextHandle].inOvLap);
|
||||||
|
contexts[contextHandle].used = false;
|
||||||
|
return length;
|
||||||
|
#else
|
||||||
|
length = contexts[contextHandle].bytesXfered;
|
||||||
|
contexts[contextHandle].used = false;
|
||||||
|
contexts[contextHandle].reset();
|
||||||
|
return length;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Aborts reading operations
|
||||||
|
*/
|
||||||
|
void Connection_uLimeSDR::AbortReading()
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
FT_AbortPipe(mFTHandle, mStreamRdEndPtAddr);
|
||||||
|
for (int i = 0; i < USB_MAX_CONTEXTS; ++i)
|
||||||
|
{
|
||||||
|
if (contexts[i].used == true)
|
||||||
|
{
|
||||||
|
FT_ReleaseOverlapped(mFTHandle, &contexts[i].inOvLap);
|
||||||
|
contexts[i].used = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FT_FlushPipe(mFTHandle, mStreamRdEndPtAddr);
|
||||||
|
rxSize = 0;
|
||||||
|
#else
|
||||||
|
|
||||||
|
for(int i = 0; i<USB_MAX_CONTEXTS; ++i)
|
||||||
|
{
|
||||||
|
if(contexts[i].used)
|
||||||
|
libusb_cancel_transfer(contexts[i].transfer);
|
||||||
|
}
|
||||||
|
FT_FlushPipe(mStreamRdEndPtAddr);
|
||||||
|
rxSize = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Starts asynchronous data Sending to board
|
||||||
|
@param *buffer buffer to send
|
||||||
|
@param length number of bytes to send
|
||||||
|
@return handle of transfer context
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::BeginDataSending(const char *buffer, uint32_t length)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
//find not used context
|
||||||
|
bool contextFound = false;
|
||||||
|
for(i = 0; i<USB_MAX_CONTEXTS; i++)
|
||||||
|
{
|
||||||
|
if(!contextsToSend[i].used)
|
||||||
|
{
|
||||||
|
contextFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!contextFound)
|
||||||
|
return -1;
|
||||||
|
contextsToSend[i].used = true;
|
||||||
|
|
||||||
|
#ifndef __unix__
|
||||||
|
FT_STATUS ftStatus = FT_OK;
|
||||||
|
ULONG ulActualBytesSend;
|
||||||
|
if (length != txSize)
|
||||||
|
{
|
||||||
|
txSize = length;
|
||||||
|
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, mStreamWrEndPtAddr, txSize);
|
||||||
|
}
|
||||||
|
FT_InitializeOverlapped(mFTHandle, &contextsToSend[i].inOvLap);
|
||||||
|
ftStatus = FT_WritePipe(mFTHandle, mStreamWrEndPtAddr, (unsigned char*)buffer, length, &ulActualBytesSend, &contextsToSend[i].inOvLap);
|
||||||
|
if (ftStatus != FT_IO_PENDING)
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
if (length != txSize)
|
||||||
|
{
|
||||||
|
txSize = length;
|
||||||
|
FT_SetStreamPipe(mStreamWrEndPtAddr,txSize);
|
||||||
|
}
|
||||||
|
unsigned int Timeout = 500;
|
||||||
|
libusb_transfer *tr = contextsToSend[i].transfer;
|
||||||
|
libusb_fill_bulk_transfer(tr, dev_handle, mStreamWrEndPtAddr, (unsigned char*)buffer, length, callback_libusbtransfer, &contextsToSend[i], Timeout);
|
||||||
|
contextsToSend[i].done = false;
|
||||||
|
contextsToSend[i].bytesXfered = 0;
|
||||||
|
contextsToSend[i].bytesExpected = length;
|
||||||
|
int status = libusb_submit_transfer(tr);
|
||||||
|
if(status != 0)
|
||||||
|
{
|
||||||
|
printf("ERROR BEGIN DATA SENDING %s\n", libusb_error_name(status));
|
||||||
|
contextsToSend[i].used = false;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Waits for asynchronous data sending
|
||||||
|
@param contextHandle handle of which context data to wait
|
||||||
|
@param timeout_ms number of miliseconds to wait
|
||||||
|
@return 1-data received, 0-data not received
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::WaitForSending(int contextHandle, unsigned int timeout_ms)
|
||||||
|
{
|
||||||
|
if(contextsToSend[contextHandle].used == true)
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
DWORD dwRet = WaitForSingleObject(contextsToSend[contextHandle].inOvLap.hEvent, timeout_ms);
|
||||||
|
if (dwRet == WAIT_OBJECT_0)
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
auto t1 = chrono::high_resolution_clock::now();
|
||||||
|
auto t2 = chrono::high_resolution_clock::now();
|
||||||
|
std::unique_lock<std::mutex> lck(contextsToSend[contextHandle].transferLock);
|
||||||
|
while(contextsToSend[contextHandle].done.load() == false && std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() < timeout_ms)
|
||||||
|
{
|
||||||
|
//blocking not to waste CPU
|
||||||
|
contextsToSend[contextHandle].cv.wait_for(lck, chrono::milliseconds(timeout_ms));
|
||||||
|
t2 = chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
return contextsToSend[contextHandle].done == true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Finishes asynchronous data sending to board
|
||||||
|
@param buffer array where to store received data
|
||||||
|
@param length number of bytes to read
|
||||||
|
@param contextHandle handle of which context to finish
|
||||||
|
@return false failure, true number of bytes sent
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::FinishDataSending(const char *buffer, uint32_t length, int contextHandle)
|
||||||
|
{
|
||||||
|
if(contextsToSend[contextHandle].used == true)
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
ULONG ulActualBytesTransferred ;
|
||||||
|
FT_STATUS ftStatus = FT_OK;
|
||||||
|
ftStatus = FT_GetOverlappedResult(mFTHandle, &contextsToSend[contextHandle].inOvLap, &ulActualBytesTransferred, FALSE);
|
||||||
|
if (ftStatus != FT_OK)
|
||||||
|
length = 0;
|
||||||
|
else
|
||||||
|
length = ulActualBytesTransferred;
|
||||||
|
FT_ReleaseOverlapped(mFTHandle, &contextsToSend[contextHandle].inOvLap);
|
||||||
|
contextsToSend[contextHandle].used = false;
|
||||||
|
return length;
|
||||||
|
#else
|
||||||
|
length = contextsToSend[contextHandle].bytesXfered;
|
||||||
|
contextsToSend[contextHandle].used = false;
|
||||||
|
contextsToSend[contextHandle].reset();
|
||||||
|
return length;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Aborts sending operations
|
||||||
|
*/
|
||||||
|
void Connection_uLimeSDR::AbortSending()
|
||||||
|
{
|
||||||
|
#ifndef __unix__
|
||||||
|
FT_AbortPipe(mFTHandle, mStreamWrEndPtAddr);
|
||||||
|
for (int i = 0; i < USB_MAX_CONTEXTS; ++i)
|
||||||
|
{
|
||||||
|
if (contextsToSend[i].used == true)
|
||||||
|
{
|
||||||
|
FT_ReleaseOverlapped(mFTHandle, &contextsToSend[i].inOvLap);
|
||||||
|
contextsToSend[i].used = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
txSize = 0;
|
||||||
|
#else
|
||||||
|
for(int i = 0; i<USB_MAX_CONTEXTS; ++i)
|
||||||
|
{
|
||||||
|
if(contextsToSend[i].used)
|
||||||
|
libusb_cancel_transfer(contextsToSend[i].transfer);
|
||||||
|
}
|
||||||
|
FT_FlushPipe(mStreamWrEndPtAddr);
|
||||||
|
txSize = 0;
|
||||||
|
#endif
|
||||||
|
}
|
165
liblimesuite/srcmw/Connection_uLimeSDR/Connection_uLimeSDR.h
Normal file
165
liblimesuite/srcmw/Connection_uLimeSDR/Connection_uLimeSDR.h
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
@file Connection_uLimeSDR.h
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief Implementation of STREAM board connection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <ConnectionRegistry.h>
|
||||||
|
#include <IConnection.h>
|
||||||
|
#include <ILimeSDRStreaming.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include "fifo.h"
|
||||||
|
|
||||||
|
#define __unix__
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
#ifndef __unix__
|
||||||
|
#include "windows.h"
|
||||||
|
#include "FTD3XXLibrary/FTD3XX.h"
|
||||||
|
#else
|
||||||
|
#include <libusb-1.0/libusb.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <chrono>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace lime{
|
||||||
|
|
||||||
|
#define USB_MAX_CONTEXTS 64 //maximum number of contexts for asynchronous transfers
|
||||||
|
|
||||||
|
class Connection_uLimeSDR : public ILimeSDRStreaming
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** @brief Wrapper class for holding USB asynchronous transfers contexts
|
||||||
|
*/
|
||||||
|
class USBTransferContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
USBTransferContext() : used(false)
|
||||||
|
{
|
||||||
|
id = idCounter++;
|
||||||
|
#ifndef __unix__
|
||||||
|
context = NULL;
|
||||||
|
#else
|
||||||
|
transfer = libusb_alloc_transfer(0);
|
||||||
|
bytesXfered = 0;
|
||||||
|
bytesExpected = 0;
|
||||||
|
done = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
~USBTransferContext()
|
||||||
|
{
|
||||||
|
#ifdef __unix__
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bool reset()
|
||||||
|
{
|
||||||
|
if(used)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool used;
|
||||||
|
int id;
|
||||||
|
static int idCounter;
|
||||||
|
#ifndef __unix__
|
||||||
|
PUCHAR context;
|
||||||
|
OVERLAPPED inOvLap;
|
||||||
|
#else
|
||||||
|
libusb_transfer* transfer;
|
||||||
|
long bytesXfered;
|
||||||
|
long bytesExpected;
|
||||||
|
std::atomic<bool> done;
|
||||||
|
std::mutex transferLock;
|
||||||
|
std::condition_variable cv;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection_uLimeSDR(void *arg);
|
||||||
|
Connection_uLimeSDR(void *ctx, const unsigned index, const int vid = -1, const int pid = -1);
|
||||||
|
|
||||||
|
virtual ~Connection_uLimeSDR(void);
|
||||||
|
|
||||||
|
int Open(const unsigned index, const int vid, const int pid);
|
||||||
|
void Close();
|
||||||
|
bool IsOpen();
|
||||||
|
int GetOpenedIndex();
|
||||||
|
|
||||||
|
virtual int Write(const unsigned char *buffer, int length, int timeout_ms = 100) override;
|
||||||
|
virtual int Read(unsigned char *buffer, int length, int timeout_ms = 100) override;
|
||||||
|
|
||||||
|
//hooks to update FPGA plls when baseband interface data rate is changed
|
||||||
|
virtual int UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate, const double txPhase, const double rxPhase)override;
|
||||||
|
virtual int UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate) override;
|
||||||
|
int ReadRawStreamData(char* buffer, unsigned length, int timeout_ms = 100)override;
|
||||||
|
protected:
|
||||||
|
virtual void ReceivePacketsLoop(const ThreadData args) override;
|
||||||
|
virtual void TransmitPacketsLoop(const ThreadData args) override;
|
||||||
|
|
||||||
|
virtual int BeginDataReading(char* buffer, uint32_t length);
|
||||||
|
virtual int WaitForReading(int contextHandle, unsigned int timeout_ms);
|
||||||
|
virtual int FinishDataReading(char* buffer, uint32_t length, int contextHandle);
|
||||||
|
virtual void AbortReading();
|
||||||
|
|
||||||
|
virtual int BeginDataSending(const char* buffer, uint32_t length);
|
||||||
|
virtual int WaitForSending(int contextHandle, uint32_t timeout_ms);
|
||||||
|
virtual int FinishDataSending(const char* buffer, uint32_t length, int contextHandle);
|
||||||
|
virtual void AbortSending();
|
||||||
|
|
||||||
|
int ResetStreamBuffers() override;
|
||||||
|
|
||||||
|
eConnectionType GetType(void) {return USB_PORT;}
|
||||||
|
|
||||||
|
USBTransferContext contexts[USB_MAX_CONTEXTS];
|
||||||
|
USBTransferContext contextsToSend[USB_MAX_CONTEXTS];
|
||||||
|
|
||||||
|
bool isConnected;
|
||||||
|
|
||||||
|
int mCtrlWrEndPtAddr;
|
||||||
|
int mCtrlRdEndPtAddr;
|
||||||
|
int mStreamWrEndPtAddr;
|
||||||
|
int mStreamRdEndPtAddr;
|
||||||
|
|
||||||
|
uint32_t txSize;
|
||||||
|
uint32_t rxSize;
|
||||||
|
#ifndef __unix__
|
||||||
|
FT_HANDLE mFTHandle;
|
||||||
|
int ReinitPipe(unsigned char ep);
|
||||||
|
#else
|
||||||
|
int FT_SetStreamPipe(unsigned char ep, size_t size);
|
||||||
|
int FT_FlushPipe(unsigned char ep);
|
||||||
|
uint32_t mUsbCounter;
|
||||||
|
libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
|
||||||
|
libusb_device_handle *dev_handle; //a device handle
|
||||||
|
libusb_context *ctx; //a libusb session
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::mutex mExtraUsbMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Connection_uLimeSDREntry : public ConnectionRegistryEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Connection_uLimeSDREntry(void);
|
||||||
|
~Connection_uLimeSDREntry(void);
|
||||||
|
std::vector<ConnectionHandle> enumerate(const ConnectionHandle &hint);
|
||||||
|
IConnection *make(const ConnectionHandle &handle);
|
||||||
|
private:
|
||||||
|
#ifndef __unix__
|
||||||
|
FT_HANDLE* mFTHandle;
|
||||||
|
#else
|
||||||
|
libusb_context *ctx; //a libusb session
|
||||||
|
std::thread mUSBProcessingThread;
|
||||||
|
void handle_libusb_events();
|
||||||
|
std::atomic<bool> mProcessUSBEvents;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,594 @@
|
|||||||
|
/**
|
||||||
|
@file Connection_uLimeSDRing.cpp
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief Implementation of uLimeSDR board connection (streaming API)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Connection_uLimeSDR.h"
|
||||||
|
#include "fifo.h"
|
||||||
|
#include <LMS7002M.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <complex>
|
||||||
|
#include <ciso646>
|
||||||
|
#include <vector>
|
||||||
|
#include <FPGA_common.h>
|
||||||
|
#include "ErrorReporting.h"
|
||||||
|
|
||||||
|
using namespace lime;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define __unix__
|
||||||
|
|
||||||
|
/** @brief Configures FPGA PLLs to LimeLight interface frequency
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate, const double txPhase, const double rxPhase)
|
||||||
|
{
|
||||||
|
const float txInterfaceClk = 2 * txRate;
|
||||||
|
const float rxInterfaceClk = 2 * rxRate;
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
|
mExpectedSampleRate = rxRate;
|
||||||
|
|
||||||
|
lime::fpga::FPGA_PLL_clock clocks[4];
|
||||||
|
|
||||||
|
clocks[0].bypass = false;
|
||||||
|
clocks[0].index = 0;
|
||||||
|
clocks[0].outFrequency = txInterfaceClk;
|
||||||
|
clocks[0].phaseShift_deg = 0;
|
||||||
|
clocks[0].findPhase = false;
|
||||||
|
clocks[1].bypass = false;
|
||||||
|
clocks[1].index = 1;
|
||||||
|
clocks[1].outFrequency = txInterfaceClk;
|
||||||
|
clocks[1].findPhase = false;
|
||||||
|
clocks[1].phaseShift_deg = txPhase;
|
||||||
|
clocks[2].bypass = false;
|
||||||
|
clocks[2].index = 2;
|
||||||
|
clocks[2].outFrequency = rxInterfaceClk;
|
||||||
|
clocks[2].phaseShift_deg = 0;
|
||||||
|
clocks[2].findPhase = false;
|
||||||
|
clocks[3].bypass = false;
|
||||||
|
clocks[3].index = 3;
|
||||||
|
clocks[3].outFrequency = rxInterfaceClk;
|
||||||
|
clocks[3].findPhase = false;
|
||||||
|
clocks[3].phaseShift_deg = rxPhase;
|
||||||
|
|
||||||
|
status = lime::fpga::SetPllFrequency(this, 0, rxInterfaceClk, clocks, 4);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Configures FPGA PLLs to LimeLight interface frequency
|
||||||
|
*/
|
||||||
|
int Connection_uLimeSDR::UpdateExternalDataRate(const size_t channel, const double txRate_Hz, const double rxRate_Hz)
|
||||||
|
{
|
||||||
|
const float txInterfaceClk = 2 * txRate_Hz;
|
||||||
|
const float rxInterfaceClk = 2 * rxRate_Hz;
|
||||||
|
int status = 0;
|
||||||
|
uint32_t reg20;
|
||||||
|
const double rxPhC1[] = { 91.08, 89.46 };
|
||||||
|
const double rxPhC2[] = { -1 / 6e6, 1.24e-6 };
|
||||||
|
const double txPhC1[] = { 89.75, 89.61 };
|
||||||
|
const double txPhC2[] = { -3.0e-7, 2.71e-7 };
|
||||||
|
|
||||||
|
const std::vector<uint32_t> spiAddr = { 0x0021, 0x0022, 0x0023, 0x0024,
|
||||||
|
0x0027, 0x002A, 0x0400, 0x040C,
|
||||||
|
0x040B, 0x0400, 0x040B, 0x0400 };
|
||||||
|
const int bakRegCnt = spiAddr.size() - 4;
|
||||||
|
auto info = GetDeviceInfo();
|
||||||
|
const int addrLMS7002M = info.addrsLMS7002M.at(0);
|
||||||
|
bool phaseSearch = false;
|
||||||
|
//if (this->chipVersion == 0x3841) //0x3840 LMS7002Mr2, 0x3841 LMS7002Mr3
|
||||||
|
/*if (rxInterfaceClk >= 5e6 || txInterfaceClk >= 5e6)
|
||||||
|
phaseSearch = true;*/
|
||||||
|
mExpectedSampleRate = rxRate_Hz;
|
||||||
|
std::vector<uint32_t> dataWr;
|
||||||
|
std::vector<uint32_t> dataRd;
|
||||||
|
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
dataWr.resize(spiAddr.size());
|
||||||
|
dataRd.resize(spiAddr.size());
|
||||||
|
//backup registers
|
||||||
|
dataWr[0] = (uint32_t(0x0020) << 16);
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), ®20, 1);
|
||||||
|
|
||||||
|
dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFD; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, 1);
|
||||||
|
|
||||||
|
for (int i = 0; i < bakRegCnt; ++i)
|
||||||
|
dataWr[i] = (spiAddr[i] << 16);
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), dataRd.data(), bakRegCnt);
|
||||||
|
//UpdateThreads(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((txInterfaceClk >= 5e6) && (rxInterfaceClk >= 5e6))
|
||||||
|
{
|
||||||
|
lime::fpga::FPGA_PLL_clock clocks[4];
|
||||||
|
|
||||||
|
clocks[0].bypass = false;
|
||||||
|
clocks[0].index = 0;
|
||||||
|
clocks[0].outFrequency = txInterfaceClk;
|
||||||
|
clocks[0].phaseShift_deg = 0;
|
||||||
|
clocks[0].findPhase = false;
|
||||||
|
clocks[1].bypass = false;
|
||||||
|
clocks[1].index = 1;
|
||||||
|
clocks[1].outFrequency = txInterfaceClk;
|
||||||
|
clocks[1].findPhase = false;
|
||||||
|
if (this->chipVersion == 0x3841)
|
||||||
|
clocks[1].phaseShift_deg = txPhC1[1] + txPhC2[1] * txInterfaceClk;
|
||||||
|
else
|
||||||
|
clocks[1].phaseShift_deg = txPhC1[0] + txPhC2[0] * txInterfaceClk;
|
||||||
|
clocks[2].bypass = false;
|
||||||
|
clocks[2].index = 2;
|
||||||
|
clocks[2].outFrequency = rxInterfaceClk;
|
||||||
|
clocks[2].phaseShift_deg = 0;
|
||||||
|
clocks[2].findPhase = false;
|
||||||
|
clocks[3].bypass = false;
|
||||||
|
clocks[3].index = 3;
|
||||||
|
clocks[3].outFrequency = rxInterfaceClk;
|
||||||
|
clocks[3].findPhase = false;
|
||||||
|
if (this->chipVersion == 0x3841)
|
||||||
|
clocks[3].phaseShift_deg = rxPhC1[1] + rxPhC2[1] * rxInterfaceClk;
|
||||||
|
else
|
||||||
|
clocks[3].phaseShift_deg = rxPhC1[0] + rxPhC2[0] * rxInterfaceClk;
|
||||||
|
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
printf("RX phase config:\n");
|
||||||
|
#endif
|
||||||
|
clocks[3].findPhase = true;
|
||||||
|
const std::vector<uint32_t> spiData = { 0x0E9F, 0x07FF, 0x5550, 0xE4E4,
|
||||||
|
0xE4E4, 0x0086, 0x028D, 0x00FF, 0x5555, 0x02CD, 0xAAAA, 0x02ED };
|
||||||
|
//Load test config
|
||||||
|
const int setRegCnt = spiData.size();
|
||||||
|
for (int i = 0; i < setRegCnt; ++i)
|
||||||
|
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | spiData[i]; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, setRegCnt);
|
||||||
|
status = lime::fpga::SetPllFrequency(this, 0, rxInterfaceClk, clocks, 4);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
printf("TX phase config:\n");
|
||||||
|
#endif
|
||||||
|
clocks[3].findPhase = false;
|
||||||
|
const std::vector<uint32_t> spiData = { 0x0E9F, 0x07FF, 0x5550, 0xE4E4, 0xE4E4, 0x0484 };
|
||||||
|
WriteRegister(0x000A, 0x0000);
|
||||||
|
//Load test config
|
||||||
|
const int setRegCnt = spiData.size();
|
||||||
|
for (int i = 0; i < setRegCnt; ++i)
|
||||||
|
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | spiData[i]; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, setRegCnt);
|
||||||
|
clocks[1].findPhase = true;
|
||||||
|
WriteRegister(0x000A, 0x0200);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status = lime::fpga::SetPllFrequency(this, 0, rxInterfaceClk, clocks, 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = lime::fpga::SetDirectClocking(this, 0, rxInterfaceClk, 90);
|
||||||
|
if (status == 0)
|
||||||
|
status = lime::fpga::SetDirectClocking(this, 1, rxInterfaceClk, 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phaseSearch)
|
||||||
|
{
|
||||||
|
//Restore registers
|
||||||
|
for (int i = 0; i < bakRegCnt; ++i)
|
||||||
|
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | dataRd[i]; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, bakRegCnt);
|
||||||
|
dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | reg20; //msbit 1=SPI write
|
||||||
|
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, 1);
|
||||||
|
WriteRegister(0x000A, 0);
|
||||||
|
UpdateThreads();
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Connection_uLimeSDR::ReadRawStreamData(char* buffer, unsigned length, int timeout_ms)
|
||||||
|
{
|
||||||
|
int totalBytesReceived = 0;
|
||||||
|
fpga::StopStreaming(this);
|
||||||
|
|
||||||
|
//ResetStreamBuffers();
|
||||||
|
WriteRegister(0x0008, 0x0100 | 0x2);
|
||||||
|
WriteRegister(0x0007, 1);
|
||||||
|
|
||||||
|
fpga::StartStreaming(this);
|
||||||
|
|
||||||
|
int handle = BeginDataReading(buffer, length);
|
||||||
|
if (WaitForReading(handle, timeout_ms))
|
||||||
|
totalBytesReceived = FinishDataReading(buffer, length, handle);
|
||||||
|
|
||||||
|
AbortReading();
|
||||||
|
fpga::StopStreaming(this);
|
||||||
|
|
||||||
|
return totalBytesReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Connection_uLimeSDR::ResetStreamBuffers()
|
||||||
|
{
|
||||||
|
rxSize = 0;
|
||||||
|
txSize = 0;
|
||||||
|
#ifndef __unix__
|
||||||
|
if (FT_AbortPipe(mFTHandle, mStreamRdEndPtAddr)!=FT_OK)
|
||||||
|
return -1;
|
||||||
|
if (FT_AbortPipe(mFTHandle, mStreamWrEndPtAddr)!=FT_OK)
|
||||||
|
return -1;
|
||||||
|
if (FT_FlushPipe(mFTHandle, mStreamRdEndPtAddr)!=FT_OK)
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
return FT_FlushPipe(mStreamRdEndPtAddr);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Function dedicated for receiving data samples from board
|
||||||
|
@param rxFIFO FIFO to store received data
|
||||||
|
@param terminate periodically pooled flag to terminate thread
|
||||||
|
@param dataRate_Bps (optional) if not NULL periodically returns data rate in bytes per second
|
||||||
|
*/
|
||||||
|
void Connection_uLimeSDR::ReceivePacketsLoop(const Connection_uLimeSDR::ThreadData args)
|
||||||
|
{
|
||||||
|
//auto dataPort = args.dataPort;
|
||||||
|
auto terminate = args.terminate;
|
||||||
|
auto dataRate_Bps = args.dataRate_Bps;
|
||||||
|
auto generateData = args.generateData;
|
||||||
|
auto safeToConfigInterface = args.safeToConfigInterface;
|
||||||
|
|
||||||
|
//at this point FPGA has to be already configured to output samples
|
||||||
|
const uint8_t chCount = args.channels.size();
|
||||||
|
const auto link = args.channels[0]->config.linkFormat;
|
||||||
|
const uint32_t samplesInPacket = (link == StreamConfig::STREAM_12_BIT_COMPRESSED ? 1360 : 1020)/chCount;
|
||||||
|
|
||||||
|
double latency=0;
|
||||||
|
for (int i = 0; i < chCount; i++)
|
||||||
|
{
|
||||||
|
latency += args.channels[i]->config.performanceLatency/chCount;
|
||||||
|
}
|
||||||
|
const unsigned tmp_cnt = (latency * 4)+0.5;
|
||||||
|
|
||||||
|
const uint8_t packetsToBatch = (1<<tmp_cnt);
|
||||||
|
const uint32_t bufferSize = packetsToBatch*sizeof(FPGA_DataPacket);
|
||||||
|
const uint8_t buffersCount = 16; // must be power of 2
|
||||||
|
vector<int> handles(buffersCount, 0);
|
||||||
|
vector<char>buffers(buffersCount*bufferSize, 0);
|
||||||
|
vector<StreamChannel::Frame> chFrames;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
chFrames.resize(chCount);
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc &ex)
|
||||||
|
{
|
||||||
|
ReportError("Error allocating Rx buffers, not enough memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t activeTransfers = 0;
|
||||||
|
for (int i = 0; i<buffersCount; ++i)
|
||||||
|
{
|
||||||
|
handles[i] = this->BeginDataReading(&buffers[i*bufferSize], bufferSize);
|
||||||
|
++activeTransfers;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bi = 0;
|
||||||
|
unsigned long totalBytesReceived = 0; //for data rate calculation
|
||||||
|
int m_bufferFailures = 0;
|
||||||
|
int32_t droppedSamples = 0;
|
||||||
|
int32_t packetLoss = 0;
|
||||||
|
|
||||||
|
vector<uint32_t> samplesCollected(chCount, 0);
|
||||||
|
vector<uint32_t> samplesReceived(chCount, 0);
|
||||||
|
|
||||||
|
auto t1 = chrono::high_resolution_clock::now();
|
||||||
|
auto t2 = chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
std::mutex txFlagsLock;
|
||||||
|
condition_variable resetTxFlags;
|
||||||
|
//worker thread for reseting late Tx packet flags
|
||||||
|
std::thread txReset([](ILimeSDRStreaming* port,
|
||||||
|
atomic<bool> *terminate,
|
||||||
|
mutex *spiLock,
|
||||||
|
condition_variable *doWork)
|
||||||
|
{
|
||||||
|
uint32_t reg9;
|
||||||
|
port->ReadRegister(0x0009, reg9);
|
||||||
|
const uint32_t addr[] = {0x0009, 0x0009};
|
||||||
|
const uint32_t data[] = {reg9 | (1 << 1), reg9 & ~(1 << 1)};
|
||||||
|
while (not terminate->load())
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lck(*spiLock);
|
||||||
|
doWork->wait(lck);
|
||||||
|
port->WriteRegisters(addr, data, 2);
|
||||||
|
}
|
||||||
|
}, this, terminate, &txFlagsLock, &resetTxFlags);
|
||||||
|
|
||||||
|
int resetFlagsDelay = 128;
|
||||||
|
uint64_t prevTs = 0;
|
||||||
|
while (terminate->load() == false)
|
||||||
|
{
|
||||||
|
if(generateData->load())
|
||||||
|
{
|
||||||
|
if(activeTransfers == 0) //stop FPGA when last transfer completes
|
||||||
|
fpga::StopStreaming(this);
|
||||||
|
safeToConfigInterface->notify_all(); //notify that it's safe to change chip config
|
||||||
|
const int batchSize = (this->mExpectedSampleRate/chFrames[0].samplesCount)/10;
|
||||||
|
IStreamChannel::Metadata meta;
|
||||||
|
for(int i=0; i<batchSize; ++i)
|
||||||
|
{
|
||||||
|
for(int ch=0; ch<chCount; ++ch)
|
||||||
|
{
|
||||||
|
meta.timestamp = chFrames[ch].timestamp;
|
||||||
|
for(int j=0; j<chFrames[ch].samplesCount; ++j)
|
||||||
|
{
|
||||||
|
chFrames[ch].samples[j].i = 0;
|
||||||
|
chFrames[ch].samples[j].q = 0;
|
||||||
|
}
|
||||||
|
uint32_t samplesPushed = args.channels[ch]->Write((const void*)chFrames[ch].samples, chFrames[ch].samplesCount, &meta);
|
||||||
|
samplesReceived[ch] += chFrames[ch].samplesCount;
|
||||||
|
if(samplesPushed != chFrames[ch].samplesCount)
|
||||||
|
printf("Rx samples pushed %i/%i\n", samplesPushed, chFrames[ch].samplesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this_thread::sleep_for(chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
int32_t bytesReceived = 0;
|
||||||
|
if(handles[bi] >= 0)
|
||||||
|
{
|
||||||
|
if (this->WaitForReading(handles[bi], 1000) == false)
|
||||||
|
++m_bufferFailures;
|
||||||
|
bytesReceived = this->FinishDataReading(&buffers[bi*bufferSize], bufferSize, handles[bi]);
|
||||||
|
--activeTransfers;
|
||||||
|
totalBytesReceived += bytesReceived;
|
||||||
|
if (bytesReceived != int32_t(bufferSize)) //data should come in full sized packets
|
||||||
|
++m_bufferFailures;
|
||||||
|
}
|
||||||
|
bool txLate=false;
|
||||||
|
for (uint8_t pktIndex = 0; pktIndex < bytesReceived / sizeof(FPGA_DataPacket); ++pktIndex)
|
||||||
|
{
|
||||||
|
const FPGA_DataPacket* pkt = (FPGA_DataPacket*)&buffers[bi*bufferSize];
|
||||||
|
const uint8_t byte0 = pkt[pktIndex].reserved[0];
|
||||||
|
if ((byte0 & (1 << 3)) != 0 && !txLate) //report only once per batch
|
||||||
|
{
|
||||||
|
txLate = true;
|
||||||
|
if(resetFlagsDelay > 0)
|
||||||
|
--resetFlagsDelay;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("L");
|
||||||
|
resetTxFlags.notify_one();
|
||||||
|
resetFlagsDelay = packetsToBatch*buffersCount;
|
||||||
|
if (args.reportLateTx) args.reportLateTx(pkt[pktIndex].counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t* pktStart = (uint8_t*)pkt[pktIndex].data;
|
||||||
|
if(pkt[pktIndex].counter - prevTs != samplesInPacket && pkt[pktIndex].counter != prevTs)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
printf("\tRx pktLoss ts diff %lli\n", (long long)pkt[pktIndex].counter - prevTs);
|
||||||
|
#endif
|
||||||
|
packetLoss += (pkt[pktIndex].counter - prevTs)/samplesInPacket;
|
||||||
|
}
|
||||||
|
prevTs = pkt[pktIndex].counter;
|
||||||
|
if(args.lastTimestamp)
|
||||||
|
args.lastTimestamp->store(pkt[pktIndex].counter);
|
||||||
|
//parse samples
|
||||||
|
vector<complex16_t*> dest(chCount);
|
||||||
|
for(uint8_t c=0; c<chCount; ++c)
|
||||||
|
dest[c] = (chFrames[c].samples);
|
||||||
|
size_t samplesCount = 0;
|
||||||
|
fpga::FPGAPacketPayload2Samples(pktStart, 4080, chCount, link, dest.data(), &samplesCount);
|
||||||
|
|
||||||
|
for(int ch=0; ch<chCount; ++ch)
|
||||||
|
{
|
||||||
|
IStreamChannel::Metadata meta;
|
||||||
|
meta.timestamp = pkt[pktIndex].counter;
|
||||||
|
meta.flags = RingFIFO::OVERWRITE_OLD;
|
||||||
|
uint32_t samplesPushed = args.channels[ch]->Write((const void*)chFrames[ch].samples, samplesCount, &meta, 100);
|
||||||
|
if(samplesPushed != samplesCount)
|
||||||
|
droppedSamples += samplesCount-samplesPushed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Re-submit this request to keep the queue full
|
||||||
|
if(not generateData->load())
|
||||||
|
{
|
||||||
|
if(activeTransfers == 0) //reactivate FPGA and USB transfers
|
||||||
|
fpga::StartStreaming(this);
|
||||||
|
for(int i=0; i<buffersCount-activeTransfers; ++i)
|
||||||
|
{
|
||||||
|
handles[bi] = this->BeginDataReading(&buffers[bi*bufferSize], bufferSize);
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
++activeTransfers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handles[bi] = -1;
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
}
|
||||||
|
t2 = chrono::high_resolution_clock::now();
|
||||||
|
auto timePeriod = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
|
||||||
|
if (timePeriod >= 1000)
|
||||||
|
{
|
||||||
|
t1 = t2;
|
||||||
|
//total number of bytes sent per second
|
||||||
|
double dataRate = 1000.0*totalBytesReceived / timePeriod;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
//each channel sample rate
|
||||||
|
float samplingRate = 1000.0*samplesReceived[0] / timePeriod;
|
||||||
|
printf("Rx: %.3f MB/s, Fs: %.3f MHz, overrun: %i, loss: %i \n", dataRate / 1000000.0, samplingRate / 1000000.0, droppedSamples, packetLoss);
|
||||||
|
#endif
|
||||||
|
samplesReceived[0] = 0;
|
||||||
|
totalBytesReceived = 0;
|
||||||
|
m_bufferFailures = 0;
|
||||||
|
droppedSamples = 0;
|
||||||
|
packetLoss = 0;
|
||||||
|
|
||||||
|
if (dataRate_Bps)
|
||||||
|
dataRate_Bps->store((uint32_t)dataRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->AbortReading();
|
||||||
|
for (int j = 0; j<buffersCount; j++)
|
||||||
|
{
|
||||||
|
if(handles[bi] >= 0)
|
||||||
|
{
|
||||||
|
this->WaitForReading(handles[bi], 1000);
|
||||||
|
this->FinishDataReading(&buffers[bi*bufferSize], bufferSize, handles[bi]);
|
||||||
|
}
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
}
|
||||||
|
resetTxFlags.notify_one();
|
||||||
|
txReset.join();
|
||||||
|
if (dataRate_Bps)
|
||||||
|
dataRate_Bps->store(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Functions dedicated for transmitting packets to board
|
||||||
|
@param txFIFO data source FIFO
|
||||||
|
@param terminate periodically pooled flag to terminate thread
|
||||||
|
@param dataRate_Bps (optional) if not NULL periodically returns data rate in bytes per second
|
||||||
|
*/
|
||||||
|
void Connection_uLimeSDR::TransmitPacketsLoop(const Connection_uLimeSDR::ThreadData args)
|
||||||
|
{
|
||||||
|
//auto dataPort = args.dataPort;
|
||||||
|
auto terminate = args.terminate;
|
||||||
|
auto dataRate_Bps = args.dataRate_Bps;
|
||||||
|
|
||||||
|
//at this point FPGA has to be already configured to output samples
|
||||||
|
const uint8_t maxChannelCount = 2;
|
||||||
|
const uint8_t chCount = args.channels.size();
|
||||||
|
const auto link = args.channels[0]->config.linkFormat;
|
||||||
|
|
||||||
|
double latency=0;
|
||||||
|
for (int i = 0; i < chCount; i++)
|
||||||
|
{
|
||||||
|
latency += args.channels[i]->config.performanceLatency/chCount;
|
||||||
|
}
|
||||||
|
const unsigned tmp_cnt = (latency * 4)+0.5;
|
||||||
|
|
||||||
|
const uint8_t buffersCount = 16; // must be power of 2
|
||||||
|
assert(buffersCount % 2 == 0);
|
||||||
|
const uint8_t packetsToBatch = (1<<tmp_cnt); //packets in single USB transfer
|
||||||
|
const uint32_t bufferSize = packetsToBatch*4096;
|
||||||
|
const uint32_t popTimeout_ms = 100;
|
||||||
|
|
||||||
|
const int maxSamplesBatch = (link==StreamConfig::STREAM_12_BIT_COMPRESSED?1360:1020)/chCount;
|
||||||
|
vector<int> handles(buffersCount, 0);
|
||||||
|
vector<bool> bufferUsed(buffersCount, 0);
|
||||||
|
vector<uint32_t> bytesToSend(buffersCount, 0);
|
||||||
|
vector<complex16_t> samples[maxChannelCount];
|
||||||
|
vector<char> buffers;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for(int i=0; i<chCount; ++i)
|
||||||
|
samples[i].resize(maxSamplesBatch);
|
||||||
|
buffers.resize(buffersCount*bufferSize, 0);
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc& ex) //not enough memory for buffers
|
||||||
|
{
|
||||||
|
printf("Error allocating Tx buffers, not enough memory\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_bufferFailures = 0;
|
||||||
|
long totalBytesSent = 0;
|
||||||
|
|
||||||
|
uint32_t samplesSent = 0;
|
||||||
|
|
||||||
|
auto t1 = chrono::high_resolution_clock::now();
|
||||||
|
auto t2 = chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
uint8_t bi = 0; //buffer index
|
||||||
|
while (terminate->load() != true)
|
||||||
|
{
|
||||||
|
if (bufferUsed[bi])
|
||||||
|
{
|
||||||
|
if (this->WaitForSending(handles[bi], 1000) == false)
|
||||||
|
++m_bufferFailures;
|
||||||
|
uint32_t bytesSent = this->FinishDataSending(&buffers[bi*bufferSize], bytesToSend[bi], handles[bi]);
|
||||||
|
totalBytesSent += bytesSent;
|
||||||
|
if (bytesSent != bytesToSend[bi])
|
||||||
|
++m_bufferFailures;
|
||||||
|
bufferUsed[bi] = false;
|
||||||
|
}
|
||||||
|
int i=0;
|
||||||
|
|
||||||
|
while(i<packetsToBatch && terminate->load() != true)
|
||||||
|
{
|
||||||
|
IStreamChannel::Metadata meta;
|
||||||
|
FPGA_DataPacket* pkt = reinterpret_cast<FPGA_DataPacket*>(&buffers[bi*bufferSize]);
|
||||||
|
for(int ch=0; ch<chCount; ++ch)
|
||||||
|
{
|
||||||
|
int samplesPopped = args.channels[ch]->Read(samples[ch].data(), maxSamplesBatch, &meta, popTimeout_ms);
|
||||||
|
if (samplesPopped != maxSamplesBatch)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
printf("Warning popping from TX, samples popped %i/%i\n", samplesPopped, maxSamplesBatch);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if(terminate->load() == true) //early termination
|
||||||
|
break;
|
||||||
|
pkt[i].counter = meta.timestamp;
|
||||||
|
pkt[i].reserved[0] = 0;
|
||||||
|
//by default ignore timestamps
|
||||||
|
const int ignoreTimestamp = !(meta.flags & IStreamChannel::Metadata::SYNC_TIMESTAMP);
|
||||||
|
pkt[i].reserved[0] |= ((int)ignoreTimestamp << 4); //ignore timestamp
|
||||||
|
|
||||||
|
vector<complex16_t*> src(chCount);
|
||||||
|
for(uint8_t c=0; c<chCount; ++c)
|
||||||
|
src[c] = (samples[c].data());
|
||||||
|
uint8_t* const dataStart = (uint8_t*)pkt[i].data;
|
||||||
|
fpga::Samples2FPGAPacketPayload(src.data(), maxSamplesBatch, chCount, link, dataStart, nullptr);
|
||||||
|
samplesSent += maxSamplesBatch;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesToSend[bi] = bufferSize;
|
||||||
|
handles[bi] = this->BeginDataSending(&buffers[bi*bufferSize], bytesToSend[bi]);
|
||||||
|
bufferUsed[bi] = true;
|
||||||
|
|
||||||
|
t2 = chrono::high_resolution_clock::now();
|
||||||
|
auto timePeriod = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
|
||||||
|
if (timePeriod >= 1000)
|
||||||
|
{
|
||||||
|
//total number of bytes sent per second
|
||||||
|
float dataRate = 1000.0*totalBytesSent / timePeriod;
|
||||||
|
if(dataRate_Bps)
|
||||||
|
dataRate_Bps->store(dataRate);
|
||||||
|
m_bufferFailures = 0;
|
||||||
|
samplesSent = 0;
|
||||||
|
totalBytesSent = 0;
|
||||||
|
t1 = t2;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
//total number of samples from all channels per second
|
||||||
|
float sampleRate = 1000.0*samplesSent / timePeriod;
|
||||||
|
printf("Tx: %.3f MB/s, Fs: %.3f MHz, failures: %i\n", dataRate / 1000000.0, sampleRate / 1000000.0, m_bufferFailures);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all the queued requests to be cancelled
|
||||||
|
this->AbortSending();
|
||||||
|
for (int j = 0; j<buffersCount; j++)
|
||||||
|
{
|
||||||
|
if (bufferUsed[bi])
|
||||||
|
{
|
||||||
|
this->WaitForSending(handles[bi], 1000);
|
||||||
|
this->FinishDataSending(&buffers[bi*bufferSize], bufferSize, handles[bi]);
|
||||||
|
}
|
||||||
|
bi = (bi + 1) & (buffersCount-1);
|
||||||
|
}
|
||||||
|
if (dataRate_Bps)
|
||||||
|
dataRate_Bps->store(0);
|
||||||
|
}
|
63
liblimesuite/srcmw/ErrorReporting.cpp
Normal file
63
liblimesuite/srcmw/ErrorReporting.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
@file ErrorReporting.cpp
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief API for reporting error codes and error messages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ErrorReporting.h"
|
||||||
|
#include <cstring> //strerror
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define thread_local __declspec( thread )
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define thread_local __thread
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_MSG_LEN 1024
|
||||||
|
thread_local int _reportedErrorCode;
|
||||||
|
thread_local char _reportedErrorMessage[MAX_MSG_LEN];
|
||||||
|
|
||||||
|
static const char *errToStr(const int errnum)
|
||||||
|
{
|
||||||
|
thread_local static char buff[MAX_MSG_LEN];
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buff, sizeof(buff), NULL);
|
||||||
|
return buff;
|
||||||
|
#else
|
||||||
|
//http://linux.die.net/man/3/strerror_r
|
||||||
|
#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE) || __APPLE__
|
||||||
|
strerror_r(errnum, buff, sizeof(buff));
|
||||||
|
#else
|
||||||
|
//this version may decide to use its own internal string
|
||||||
|
//return strerror_r(errnum, buff, sizeof(buff)); MinGW
|
||||||
|
return buff;
|
||||||
|
#endif
|
||||||
|
return buff;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int lime::GetLastError(void)
|
||||||
|
{
|
||||||
|
return _reportedErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *lime::GetLastErrorMessage(void)
|
||||||
|
{
|
||||||
|
return _reportedErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lime::ReportError(const int errnum)
|
||||||
|
{
|
||||||
|
return lime::ReportError(errnum, errToStr(errnum));
|
||||||
|
}
|
||||||
|
|
||||||
|
int lime::ReportError(const int errnum, const char *format, va_list argList)
|
||||||
|
{
|
||||||
|
_reportedErrorCode = errnum;
|
||||||
|
vsnprintf(_reportedErrorMessage, MAX_MSG_LEN, format, argList);
|
||||||
|
return errnum;
|
||||||
|
}
|
85
liblimesuite/srcmw/ErrorReporting.h
Normal file
85
liblimesuite/srcmw/ErrorReporting.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
@file ErrorReporting.h
|
||||||
|
@author Lime Microsystems
|
||||||
|
@brief API for reporting error codes and error messages.
|
||||||
|
All calls are thread-safe using thread-local storage.
|
||||||
|
Code returning with an error should use:
|
||||||
|
return lime::ReportError(code, message, ...);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIMESUITE_ERROR_REPORTING_H
|
||||||
|
#define LIMESUITE_ERROR_REPORTING_H
|
||||||
|
|
||||||
|
#include <LimeSuiteConfig.h>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstdarg>
|
||||||
|
|
||||||
|
namespace lime
|
||||||
|
{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get the error code reported.
|
||||||
|
*/
|
||||||
|
LIME_API int GetLastError(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get the error code to string + any optional message reported.
|
||||||
|
*/
|
||||||
|
LIME_API const char *GetLastErrorMessage(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Report a typical errno style error.
|
||||||
|
* The resulting error message comes from strerror().
|
||||||
|
* \param errnum a recognized error code
|
||||||
|
* \return a non-zero status code to return
|
||||||
|
*/
|
||||||
|
LIME_API int ReportError(const int errnum);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Report an error as an integer code and a formatted message string.
|
||||||
|
* \param errnum a recognized error code
|
||||||
|
* \param format a format string followed by args
|
||||||
|
* \return a non-zero status code to return
|
||||||
|
*/
|
||||||
|
inline int ReportError(const int errnum, const char *format, ...);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Report an error as a formatted message string.
|
||||||
|
* The reported errnum is 0 - no relevant error code.
|
||||||
|
* \param format a format string followed by args
|
||||||
|
* \return a non-zero status code to return
|
||||||
|
*/
|
||||||
|
inline int ReportError(const char *format, ...);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Report an error as an integer code and message format arguments
|
||||||
|
* \param errnum a recognized error code
|
||||||
|
* \param format a printf-style format string
|
||||||
|
* \param argList the format string args as a va_list
|
||||||
|
* \return a non-zero status code to return
|
||||||
|
*/
|
||||||
|
LIME_API int ReportError(const int errnum, const char *format, va_list argList);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int lime::ReportError(const int errnum, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list argList;
|
||||||
|
va_start(argList, format);
|
||||||
|
int status = lime::ReportError(errnum, format, argList);
|
||||||
|
va_end(argList);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int lime::ReportError(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list argList;
|
||||||
|
va_start(argList, format);
|
||||||
|
int status = lime::ReportError(-1, format, argList);
|
||||||
|
va_end(argList);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIMESUITE_ERROR_REPORTING_H
|
2642
liblimesuite/srcmw/lms7002m/LMS7002M.cpp
Normal file
2642
liblimesuite/srcmw/lms7002m/LMS7002M.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1055
liblimesuite/srcmw/lms7002m_mcu/MCU_BD.cpp
Normal file
1055
liblimesuite/srcmw/lms7002m_mcu/MCU_BD.cpp
Normal file
File diff suppressed because it is too large
Load Diff
535
liblimesuite/srcmw/lms7002m_mcu/MCU_File.h
Normal file
535
liblimesuite/srcmw/lms7002m_mcu/MCU_File.h
Normal file
@ -0,0 +1,535 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#if !(defined(max)) && _MSC_VER
|
||||||
|
// VC fix
|
||||||
|
#define max __max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MYMAX(a, b) (a) > (b) ? (a) : (b)
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class MemBlock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
unsigned long m_startAddress;
|
||||||
|
vector<unsigned char> m_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MCU_File
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MCU_File(const char *fileName, const char *mode)
|
||||||
|
{
|
||||||
|
m_file = fopen(fileName, mode);
|
||||||
|
if (m_file != NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "Error opening";
|
||||||
|
//string errorStr = "Error opening ";
|
||||||
|
//errorStr += fileName;
|
||||||
|
//errorStr += "\n";
|
||||||
|
//throw errorStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MCU_File()
|
||||||
|
{
|
||||||
|
if (m_file)
|
||||||
|
fclose(m_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileOpened()
|
||||||
|
{
|
||||||
|
return m_file != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read binary file
|
||||||
|
void ReadBin(unsigned long limit)
|
||||||
|
{
|
||||||
|
m_top = 0;
|
||||||
|
|
||||||
|
m_chunks.push_back(MemBlock());
|
||||||
|
m_chunks.back().m_startAddress = 0;
|
||||||
|
|
||||||
|
cout << "Reading binary file\n";
|
||||||
|
|
||||||
|
int tmp = fgetc(m_file);
|
||||||
|
|
||||||
|
while (!feof(m_file))
|
||||||
|
{
|
||||||
|
m_chunks.back().m_bytes.push_back(tmp);
|
||||||
|
|
||||||
|
if (m_chunks.back().m_bytes.size() > limit + 1)
|
||||||
|
{
|
||||||
|
m_chunks.back().m_bytes.pop_back();
|
||||||
|
m_top = m_chunks.back().m_bytes.size() - 1;
|
||||||
|
cout << "Ignoring data above address space!\n";
|
||||||
|
cout << " Limit: " << limit << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = fgetc(m_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_top = m_chunks.back().m_bytes.size() - 1;
|
||||||
|
|
||||||
|
if (!m_chunks.back().m_bytes.size())
|
||||||
|
{
|
||||||
|
cout << "No data!\n";
|
||||||
|
|
||||||
|
m_chunks.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read hex file
|
||||||
|
void ReadHex(unsigned long limit)
|
||||||
|
{
|
||||||
|
char szLine[1024];
|
||||||
|
bool formatDetected = false;
|
||||||
|
bool intel = false;
|
||||||
|
bool endSeen = false;
|
||||||
|
bool linear = true; // Only used for intel hex
|
||||||
|
unsigned long addressBase = 0; // Only used for intel hex
|
||||||
|
unsigned long dataRecords = 0; // Only used for s-record
|
||||||
|
while (!feof(m_file))
|
||||||
|
{
|
||||||
|
if (fgets(szLine, 1024, m_file) == 0)
|
||||||
|
{
|
||||||
|
if (ferror(m_file))
|
||||||
|
{
|
||||||
|
throw "Error reading input!\n";
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (szLine[strlen(szLine) - 1] == 0xA || szLine[strlen(szLine) - 1] == 0xD)
|
||||||
|
{
|
||||||
|
szLine[strlen(szLine) - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (szLine[strlen(szLine) - 1] == 0xA || szLine[strlen(szLine) - 1] == 0xD)
|
||||||
|
{
|
||||||
|
szLine[strlen(szLine) - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(szLine) == 1023)
|
||||||
|
{
|
||||||
|
throw "Hex file lines to long!\n";
|
||||||
|
}
|
||||||
|
// Ignore blank lines
|
||||||
|
if (szLine[0] == '\n')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Detect format and warn if garbage lines are found
|
||||||
|
if (!formatDetected)
|
||||||
|
{
|
||||||
|
if (szLine[0] != ':' && szLine[0] != 'S')
|
||||||
|
{
|
||||||
|
cout << "Ignoring garbage line!\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (szLine[0] == 'S')
|
||||||
|
{
|
||||||
|
intel = false;
|
||||||
|
cout << "Detected S-Record\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
intel = true;
|
||||||
|
cout << "Detected intel hex file\n";
|
||||||
|
}
|
||||||
|
formatDetected = true;
|
||||||
|
}
|
||||||
|
else if ((intel && szLine[0] != ':') ||
|
||||||
|
(!intel && szLine[0] != 'S'))
|
||||||
|
{
|
||||||
|
cout << "Ignoring garbage line!\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endSeen)
|
||||||
|
{
|
||||||
|
throw "Hex line after end of file record!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intel)
|
||||||
|
{
|
||||||
|
unsigned long dataBytes;
|
||||||
|
unsigned long startAddress;
|
||||||
|
unsigned long type;
|
||||||
|
if (sscanf(&szLine[1], "%2lx%4lx%2lx", &dataBytes, &startAddress, &type) != 3)
|
||||||
|
{
|
||||||
|
throw "Hex line beginning corrupt!\n";
|
||||||
|
}
|
||||||
|
// Check line length
|
||||||
|
if (szLine[11 + dataBytes * 2] != '\n' && szLine[11 + dataBytes * 2] != 0)
|
||||||
|
{
|
||||||
|
throw "Hex line length incorrect!\n";
|
||||||
|
}
|
||||||
|
// Check line checksum
|
||||||
|
unsigned char checkSum = 0;
|
||||||
|
unsigned long tmp;
|
||||||
|
for (unsigned int i = 0; i <= dataBytes + 4; ++i)
|
||||||
|
{
|
||||||
|
if (sscanf(&szLine[1 + i * 2], "%2lx", &tmp) != 1)
|
||||||
|
{
|
||||||
|
throw "Hex line data corrupt!\n";
|
||||||
|
}
|
||||||
|
checkSum += tmp;
|
||||||
|
}
|
||||||
|
if (checkSum != 0)
|
||||||
|
{
|
||||||
|
throw "Hex line checksum error!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// Data record
|
||||||
|
if (!linear)
|
||||||
|
{
|
||||||
|
// Segmented
|
||||||
|
unsigned long test = startAddress;
|
||||||
|
test += dataBytes;
|
||||||
|
if (test > 0xffff)
|
||||||
|
{
|
||||||
|
throw "Can't handle wrapped segments!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_chunks.size() ||
|
||||||
|
m_chunks.back().m_startAddress + m_chunks.back().m_bytes.size() !=
|
||||||
|
addressBase + startAddress)
|
||||||
|
{
|
||||||
|
m_chunks.push_back(MemBlock());
|
||||||
|
m_chunks.back().m_startAddress = addressBase + startAddress;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
unsigned char i = 0;
|
||||||
|
for (i = 0; i < dataBytes; ++i)
|
||||||
|
{
|
||||||
|
sscanf(&szLine[9 + i * 2], "%2lx", &tmp);
|
||||||
|
if (addressBase + startAddress + i > limit)
|
||||||
|
{
|
||||||
|
cout << "Ignoring data above address space!\n";
|
||||||
|
cout << "Data address: " << addressBase + startAddress + i;
|
||||||
|
cout << " Limit: " << limit << "\n";
|
||||||
|
if (!m_chunks.back().m_bytes.size())
|
||||||
|
{
|
||||||
|
m_chunks.pop_back();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_chunks.back().m_bytes.push_back(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// End-of-file record
|
||||||
|
if (dataBytes != 0)
|
||||||
|
{
|
||||||
|
cout << "Warning: End of file record not zero length!\n";
|
||||||
|
}
|
||||||
|
if (startAddress != 0)
|
||||||
|
{
|
||||||
|
cout << "Warning: End of file record address not zero!\n";
|
||||||
|
}
|
||||||
|
endSeen = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// Extended segment address record
|
||||||
|
if (dataBytes != 2)
|
||||||
|
{
|
||||||
|
throw "Length field must be 2 in extended segment address record!\n";
|
||||||
|
}
|
||||||
|
if (startAddress != 0)
|
||||||
|
{
|
||||||
|
throw "Address field must be zero in extended segment address record!\n";
|
||||||
|
}
|
||||||
|
sscanf(&szLine[9], "%4lx", &startAddress);
|
||||||
|
addressBase = startAddress << 4;
|
||||||
|
linear = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
// Start segment address record
|
||||||
|
if (dataBytes != 4)
|
||||||
|
{
|
||||||
|
cout << "Warning: Length field must be 4 in start segment address record!\n";
|
||||||
|
}
|
||||||
|
if (startAddress != 0)
|
||||||
|
{
|
||||||
|
cout << "Warning: Address field must be zero in start segment address record!\n";
|
||||||
|
}
|
||||||
|
if (dataBytes == 4)
|
||||||
|
{
|
||||||
|
unsigned long ssa;
|
||||||
|
char ssaStr[16];
|
||||||
|
sscanf(&szLine[9], "%8lx", &ssa);
|
||||||
|
sprintf(ssaStr, "%08lX\n", ssa);
|
||||||
|
cout << "Segment start address (CS/IP): ";
|
||||||
|
cout << ssaStr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
// Extended linear address record
|
||||||
|
if (dataBytes != 2)
|
||||||
|
{
|
||||||
|
throw "Length field must be 2 in extended linear address record!\n";
|
||||||
|
}
|
||||||
|
if (startAddress != 0)
|
||||||
|
{
|
||||||
|
throw "Address field must be zero in extended linear address record!\n";
|
||||||
|
}
|
||||||
|
sscanf(&szLine[9], "%4lx", &startAddress);
|
||||||
|
addressBase = ((unsigned long)startAddress) << 16;
|
||||||
|
linear = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
// Start linear address record
|
||||||
|
if (dataBytes != 4)
|
||||||
|
{
|
||||||
|
cout << "Warning: Length field must be 4 in start linear address record!\n";
|
||||||
|
}
|
||||||
|
if (startAddress != 0)
|
||||||
|
{
|
||||||
|
cout << "Warning: Address field must be zero in start linear address record!\n";
|
||||||
|
}
|
||||||
|
if (dataBytes == 4)
|
||||||
|
{
|
||||||
|
unsigned long lsa;
|
||||||
|
char lsaStr[16];
|
||||||
|
sscanf(&szLine[9], "%8lx", &lsa);
|
||||||
|
sprintf(lsaStr, "%08lX\n", lsa);
|
||||||
|
cout << "Linear start address: ";
|
||||||
|
cout << lsaStr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cout << "Waring: Unknown record found!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// S-record
|
||||||
|
unsigned long count;
|
||||||
|
char type;
|
||||||
|
if (sscanf(&szLine[1], "%c%2lx", &type, &count) != 2)
|
||||||
|
{
|
||||||
|
throw "Hex line beginning corrupt!\n";
|
||||||
|
}
|
||||||
|
// Check line length
|
||||||
|
if (szLine[4 + count * 2] != '\n' && szLine[4 + count * 2] != 0)
|
||||||
|
{
|
||||||
|
throw "Hex line length incorrect!\n";
|
||||||
|
}
|
||||||
|
// Check line checksum
|
||||||
|
unsigned char checkSum = 0;
|
||||||
|
unsigned long tmp;
|
||||||
|
for (unsigned int i = 0; i < count + 1; ++i)
|
||||||
|
{
|
||||||
|
if (sscanf(&szLine[2 + i * 2], "%2lx", &tmp) != 1)
|
||||||
|
{
|
||||||
|
throw "Hex line data corrupt!\n";
|
||||||
|
}
|
||||||
|
checkSum += tmp;
|
||||||
|
}
|
||||||
|
if (checkSum != 255)
|
||||||
|
{
|
||||||
|
throw "Hex line checksum error!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case '0':
|
||||||
|
// Header record
|
||||||
|
{
|
||||||
|
char header[256];
|
||||||
|
uint8_t i = 0;
|
||||||
|
for (i = 0; uint8_t(i + 3) < count; ++i)
|
||||||
|
{
|
||||||
|
sscanf(&szLine[8 + i * 2], "%2lx", &tmp);
|
||||||
|
header[i] = tmp;
|
||||||
|
}
|
||||||
|
header[i] = 0;
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
cout << "Module name: " << header << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
// Data record
|
||||||
|
{
|
||||||
|
dataRecords++;
|
||||||
|
unsigned long startAddress;
|
||||||
|
if (type == '1')
|
||||||
|
{
|
||||||
|
sscanf(&szLine[4], "%4lx", &startAddress);
|
||||||
|
}
|
||||||
|
else if (type == '2')
|
||||||
|
{
|
||||||
|
sscanf(&szLine[4], "%6lx", &startAddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sscanf(&szLine[4], "%8lx", &startAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_chunks.size() ||
|
||||||
|
m_chunks.back().m_startAddress + m_chunks.back().m_bytes.size() !=
|
||||||
|
startAddress)
|
||||||
|
{
|
||||||
|
m_chunks.push_back(MemBlock());
|
||||||
|
m_chunks.back().m_startAddress = startAddress;
|
||||||
|
}
|
||||||
|
unsigned char i = 0;
|
||||||
|
for (i = uint8_t(type - '1'); uint8_t(i + 3) < count; ++i)
|
||||||
|
{
|
||||||
|
sscanf(&szLine[8 + i * 2], "%2lx", &tmp);
|
||||||
|
if (startAddress + i > limit)
|
||||||
|
{
|
||||||
|
cout << "Ignoring data above address space!\n";
|
||||||
|
cout << "Data address: " << startAddress + i;
|
||||||
|
cout << " Limit: " << limit << "\n";
|
||||||
|
if (!m_chunks.back().m_bytes.size())
|
||||||
|
{
|
||||||
|
m_chunks.pop_back();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_chunks.back().m_bytes.push_back(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '5':
|
||||||
|
// Count record
|
||||||
|
{
|
||||||
|
unsigned long address;
|
||||||
|
sscanf(&szLine[4], "%4lx", &address);
|
||||||
|
if (address != dataRecords)
|
||||||
|
{
|
||||||
|
throw "Wrong number of data records!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
// Start address record
|
||||||
|
cout << "Ignoring start address record!\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cout << "Unknown record found!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (intel && !endSeen)
|
||||||
|
{
|
||||||
|
cout << "No end of file record!\n";
|
||||||
|
}
|
||||||
|
if (!m_chunks.size())
|
||||||
|
{
|
||||||
|
throw "No data in file!\n";
|
||||||
|
}
|
||||||
|
vector<MemBlock>::iterator vi;
|
||||||
|
m_top = 0;
|
||||||
|
for (vi = m_chunks.begin(); vi < m_chunks.end(); vi++)
|
||||||
|
{
|
||||||
|
//m_top = m_top > vi->m_startAddress + vi->m_bytes.size() - 1 ? m_top : vi->m_startAddress + vi->m_bytes.size() - 1;
|
||||||
|
m_top = MYMAX(m_top, vi->m_startAddress + vi->m_bytes.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rather inefficient this one, fix sometime
|
||||||
|
bool GetByte(const unsigned long address, unsigned char &chr)
|
||||||
|
{
|
||||||
|
vector<MemBlock>::iterator vi;
|
||||||
|
|
||||||
|
for (vi = m_chunks.begin(); vi < m_chunks.end(); vi++)
|
||||||
|
{
|
||||||
|
if (vi->m_startAddress + vi->m_bytes.size() > address && vi->m_startAddress <= address)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vi == m_chunks.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
chr = vi->m_bytes[address - vi->m_startAddress];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BitString(const unsigned long address, const unsigned char bits, const bool lEndian, string &str)
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
long i;
|
||||||
|
unsigned char chr;
|
||||||
|
unsigned long data = 0;
|
||||||
|
unsigned long tmp;
|
||||||
|
|
||||||
|
if (lEndian)
|
||||||
|
{
|
||||||
|
for (i = 0; i < (bits + 7) / 8; ++i)
|
||||||
|
{
|
||||||
|
ok |= GetByte(address + i, chr);
|
||||||
|
tmp = chr;
|
||||||
|
data |= tmp << (8 * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < (bits + 7) / 8; ++i)
|
||||||
|
{
|
||||||
|
ok |= GetByte(address + i, chr);
|
||||||
|
tmp = chr;
|
||||||
|
data |= tmp << (8 * ((bits + 7) / 8 - i - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long mask = 1;
|
||||||
|
|
||||||
|
str = "";
|
||||||
|
for (i = 0; i < bits; i++)
|
||||||
|
{
|
||||||
|
if (data & mask)
|
||||||
|
{
|
||||||
|
str.insert(0,"1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
str.insert(0,"0");
|
||||||
|
}
|
||||||
|
mask <<= 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *Handle() { return m_file; };
|
||||||
|
vector<MemBlock> m_chunks;
|
||||||
|
unsigned long m_top;
|
||||||
|
private:
|
||||||
|
FILE *m_file;
|
||||||
|
};
|
38
liblimesuite/srcmw/vasprintf.h
Normal file
38
liblimesuite/srcmw/vasprintf.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* vasprintf.h
|
||||||
|
*
|
||||||
|
* Created on: 9 May 2017
|
||||||
|
* Author: egriffiths
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBLIMESUITE_SRCMW_VASPRINTF_H_
|
||||||
|
#define LIBLIMESUITE_SRCMW_VASPRINTF_H_
|
||||||
|
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define __CRT__NO_INLINE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
int my_vasprintf(char ** __restrict__ ret,
|
||||||
|
const char * __restrict__ format,
|
||||||
|
va_list ap) {
|
||||||
|
int len;
|
||||||
|
/* Get Length */
|
||||||
|
len = _vsnprintf(NULL,0,format,ap);
|
||||||
|
if (len < 0) return -1;
|
||||||
|
/* +1 for \0 terminator. */
|
||||||
|
*ret = (char *) malloc(len + 1);
|
||||||
|
/* Check malloc fail*/
|
||||||
|
if (!*ret) return -1;
|
||||||
|
/* Write String */
|
||||||
|
_vsnprintf(*ret,len+1,format,ap);
|
||||||
|
/* Terminate explicitly */
|
||||||
|
(*ret)[len] = '\0';
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LIBLIMESUITE_SRCMW_VASPRINTF_H_ */
|
25
libsqlite3/libsqlite3.pro
Normal file
25
libsqlite3/libsqlite3.pro
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#--------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Pro file for Android and Windows builds with Qt Creator
|
||||||
|
#
|
||||||
|
#--------------------------------------------------------
|
||||||
|
|
||||||
|
QT += core
|
||||||
|
|
||||||
|
TEMPLATE = lib
|
||||||
|
TARGET = libsqlite3
|
||||||
|
|
||||||
|
CONFIG(MINGW32):LIBSQLITE3SRC = "."
|
||||||
|
CONFIG(MINGW64):LIBSQLITE3SRC = "."
|
||||||
|
|
||||||
|
CONFIG(MINGW32):INCLUDEPATH += "src"
|
||||||
|
CONFIG(MINGW64):INCLUDEPATH += "src"
|
||||||
|
|
||||||
|
SOURCES = $$LIBSQLITE3SRC/src/sqlite3.c
|
||||||
|
|
||||||
|
HEADERS = $$LIBSQLITE3SRC/src/sqlite3.h\
|
||||||
|
$$LIBSQLITE3SRC/src/sqlite3ext.h
|
||||||
|
|
||||||
|
CONFIG(ANDROID):CONFIG += mobility
|
||||||
|
CONFIG(ANDROID):MOBILITY =
|
||||||
|
|
7388
libsqlite3/src/shell.c
Normal file
7388
libsqlite3/src/shell.c
Normal file
File diff suppressed because it is too large
Load Diff
202113
libsqlite3/src/sqlite3.c
Normal file
202113
libsqlite3/src/sqlite3.c
Normal file
File diff suppressed because it is too large
Load Diff
10491
libsqlite3/src/sqlite3.h
Normal file
10491
libsqlite3/src/sqlite3.h
Normal file
File diff suppressed because it is too large
Load Diff
564
libsqlite3/src/sqlite3ext.h
Normal file
564
libsqlite3/src/sqlite3ext.h
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
/*
|
||||||
|
** 2006 June 7
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of
|
||||||
|
** a legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** May you do good and not evil.
|
||||||
|
** May you find forgiveness for yourself and forgive others.
|
||||||
|
** May you share freely, never taking more than you give.
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** This header file defines the SQLite interface for use by
|
||||||
|
** shared libraries that want to be imported as extensions into
|
||||||
|
** an SQLite instance. Shared libraries that intend to be loaded
|
||||||
|
** as extensions by SQLite should #include this file instead of
|
||||||
|
** sqlite3.h.
|
||||||
|
*/
|
||||||
|
#ifndef SQLITE3EXT_H
|
||||||
|
#define SQLITE3EXT_H
|
||||||
|
#include "../libsqlite3/sqlite3.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following structure holds pointers to all of the SQLite API
|
||||||
|
** routines.
|
||||||
|
**
|
||||||
|
** WARNING: In order to maintain backwards compatibility, add new
|
||||||
|
** interfaces to the end of this structure only. If you insert new
|
||||||
|
** interfaces in the middle of this structure, then older different
|
||||||
|
** versions of SQLite will not be able to load each other's shared
|
||||||
|
** libraries!
|
||||||
|
*/
|
||||||
|
struct sqlite3_api_routines {
|
||||||
|
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||||
|
int (*aggregate_count)(sqlite3_context*);
|
||||||
|
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||||
|
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||||
|
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||||
|
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||||
|
int (*bind_null)(sqlite3_stmt*,int);
|
||||||
|
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||||
|
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||||
|
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||||
|
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||||
|
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||||
|
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||||
|
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||||
|
int (*busy_timeout)(sqlite3*,int ms);
|
||||||
|
int (*changes)(sqlite3*);
|
||||||
|
int (*close)(sqlite3*);
|
||||||
|
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||||
|
int eTextRep,const char*));
|
||||||
|
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||||
|
int eTextRep,const void*));
|
||||||
|
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||||
|
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||||
|
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||||
|
int (*column_count)(sqlite3_stmt*pStmt);
|
||||||
|
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||||
|
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||||
|
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||||
|
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||||
|
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||||
|
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||||
|
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||||
|
const char * (*column_name)(sqlite3_stmt*,int);
|
||||||
|
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||||
|
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||||
|
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||||
|
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||||
|
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||||
|
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||||
|
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||||
|
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||||
|
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||||
|
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||||
|
int (*complete)(const char*sql);
|
||||||
|
int (*complete16)(const void*sql);
|
||||||
|
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||||
|
int(*)(void*,int,const void*,int,const void*));
|
||||||
|
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||||
|
int(*)(void*,int,const void*,int,const void*));
|
||||||
|
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||||
|
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xFinal)(sqlite3_context*));
|
||||||
|
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||||
|
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xFinal)(sqlite3_context*));
|
||||||
|
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||||
|
int (*data_count)(sqlite3_stmt*pStmt);
|
||||||
|
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||||
|
int (*declare_vtab)(sqlite3*,const char*);
|
||||||
|
int (*enable_shared_cache)(int);
|
||||||
|
int (*errcode)(sqlite3*db);
|
||||||
|
const char * (*errmsg)(sqlite3*);
|
||||||
|
const void * (*errmsg16)(sqlite3*);
|
||||||
|
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||||
|
int (*expired)(sqlite3_stmt*);
|
||||||
|
int (*finalize)(sqlite3_stmt*pStmt);
|
||||||
|
void (*free)(void*);
|
||||||
|
void (*free_table)(char**result);
|
||||||
|
int (*get_autocommit)(sqlite3*);
|
||||||
|
void * (*get_auxdata)(sqlite3_context*,int);
|
||||||
|
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||||
|
int (*global_recover)(void);
|
||||||
|
void (*interruptx)(sqlite3*);
|
||||||
|
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||||
|
const char * (*libversion)(void);
|
||||||
|
int (*libversion_number)(void);
|
||||||
|
void *(*malloc)(int);
|
||||||
|
char * (*mprintf)(const char*,...);
|
||||||
|
int (*open)(const char*,sqlite3**);
|
||||||
|
int (*open16)(const void*,sqlite3**);
|
||||||
|
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||||
|
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||||
|
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||||
|
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||||
|
void *(*realloc)(void*,int);
|
||||||
|
int (*reset)(sqlite3_stmt*pStmt);
|
||||||
|
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||||
|
void (*result_double)(sqlite3_context*,double);
|
||||||
|
void (*result_error)(sqlite3_context*,const char*,int);
|
||||||
|
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||||
|
void (*result_int)(sqlite3_context*,int);
|
||||||
|
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||||
|
void (*result_null)(sqlite3_context*);
|
||||||
|
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||||
|
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||||
|
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||||
|
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||||
|
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||||
|
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||||
|
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||||
|
const char*,const char*),void*);
|
||||||
|
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||||
|
char * (*snprintf)(int,char*,const char*,...);
|
||||||
|
int (*step)(sqlite3_stmt*);
|
||||||
|
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||||
|
char const**,char const**,int*,int*,int*);
|
||||||
|
void (*thread_cleanup)(void);
|
||||||
|
int (*total_changes)(sqlite3*);
|
||||||
|
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||||
|
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||||
|
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||||
|
sqlite_int64),void*);
|
||||||
|
void * (*user_data)(sqlite3_context*);
|
||||||
|
const void * (*value_blob)(sqlite3_value*);
|
||||||
|
int (*value_bytes)(sqlite3_value*);
|
||||||
|
int (*value_bytes16)(sqlite3_value*);
|
||||||
|
double (*value_double)(sqlite3_value*);
|
||||||
|
int (*value_int)(sqlite3_value*);
|
||||||
|
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||||
|
int (*value_numeric_type)(sqlite3_value*);
|
||||||
|
const unsigned char * (*value_text)(sqlite3_value*);
|
||||||
|
const void * (*value_text16)(sqlite3_value*);
|
||||||
|
const void * (*value_text16be)(sqlite3_value*);
|
||||||
|
const void * (*value_text16le)(sqlite3_value*);
|
||||||
|
int (*value_type)(sqlite3_value*);
|
||||||
|
char *(*vmprintf)(const char*,va_list);
|
||||||
|
/* Added ??? */
|
||||||
|
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||||
|
/* Added by 3.3.13 */
|
||||||
|
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||||
|
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||||
|
int (*clear_bindings)(sqlite3_stmt*);
|
||||||
|
/* Added by 3.4.1 */
|
||||||
|
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||||
|
void (*xDestroy)(void *));
|
||||||
|
/* Added by 3.5.0 */
|
||||||
|
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||||
|
int (*blob_bytes)(sqlite3_blob*);
|
||||||
|
int (*blob_close)(sqlite3_blob*);
|
||||||
|
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||||
|
int,sqlite3_blob**);
|
||||||
|
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||||
|
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||||
|
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||||
|
int(*)(void*,int,const void*,int,const void*),
|
||||||
|
void(*)(void*));
|
||||||
|
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||||
|
sqlite3_int64 (*memory_highwater)(int);
|
||||||
|
sqlite3_int64 (*memory_used)(void);
|
||||||
|
sqlite3_mutex *(*mutex_alloc)(int);
|
||||||
|
void (*mutex_enter)(sqlite3_mutex*);
|
||||||
|
void (*mutex_free)(sqlite3_mutex*);
|
||||||
|
void (*mutex_leave)(sqlite3_mutex*);
|
||||||
|
int (*mutex_try)(sqlite3_mutex*);
|
||||||
|
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||||
|
int (*release_memory)(int);
|
||||||
|
void (*result_error_nomem)(sqlite3_context*);
|
||||||
|
void (*result_error_toobig)(sqlite3_context*);
|
||||||
|
int (*sleep)(int);
|
||||||
|
void (*soft_heap_limit)(int);
|
||||||
|
sqlite3_vfs *(*vfs_find)(const char*);
|
||||||
|
int (*vfs_register)(sqlite3_vfs*,int);
|
||||||
|
int (*vfs_unregister)(sqlite3_vfs*);
|
||||||
|
int (*xthreadsafe)(void);
|
||||||
|
void (*result_zeroblob)(sqlite3_context*,int);
|
||||||
|
void (*result_error_code)(sqlite3_context*,int);
|
||||||
|
int (*test_control)(int, ...);
|
||||||
|
void (*randomness)(int,void*);
|
||||||
|
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||||
|
int (*extended_result_codes)(sqlite3*,int);
|
||||||
|
int (*limit)(sqlite3*,int,int);
|
||||||
|
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||||
|
const char *(*sql)(sqlite3_stmt*);
|
||||||
|
int (*status)(int,int*,int*,int);
|
||||||
|
int (*backup_finish)(sqlite3_backup*);
|
||||||
|
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||||
|
int (*backup_pagecount)(sqlite3_backup*);
|
||||||
|
int (*backup_remaining)(sqlite3_backup*);
|
||||||
|
int (*backup_step)(sqlite3_backup*,int);
|
||||||
|
const char *(*compileoption_get)(int);
|
||||||
|
int (*compileoption_used)(const char*);
|
||||||
|
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||||
|
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||||
|
void (*xFinal)(sqlite3_context*),
|
||||||
|
void(*xDestroy)(void*));
|
||||||
|
int (*db_config)(sqlite3*,int,...);
|
||||||
|
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||||
|
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||||
|
int (*extended_errcode)(sqlite3*);
|
||||||
|
void (*log)(int,const char*,...);
|
||||||
|
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||||
|
const char *(*sourceid)(void);
|
||||||
|
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||||
|
int (*strnicmp)(const char*,const char*,int);
|
||||||
|
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||||
|
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||||
|
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||||
|
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||||
|
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||||
|
int (*vtab_config)(sqlite3*,int op,...);
|
||||||
|
int (*vtab_on_conflict)(sqlite3*);
|
||||||
|
/* Version 3.7.16 and later */
|
||||||
|
int (*close_v2)(sqlite3*);
|
||||||
|
const char *(*db_filename)(sqlite3*,const char*);
|
||||||
|
int (*db_readonly)(sqlite3*,const char*);
|
||||||
|
int (*db_release_memory)(sqlite3*);
|
||||||
|
const char *(*errstr)(int);
|
||||||
|
int (*stmt_busy)(sqlite3_stmt*);
|
||||||
|
int (*stmt_readonly)(sqlite3_stmt*);
|
||||||
|
int (*stricmp)(const char*,const char*);
|
||||||
|
int (*uri_boolean)(const char*,const char*,int);
|
||||||
|
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||||
|
const char *(*uri_parameter)(const char*,const char*);
|
||||||
|
char *(*vsnprintf)(int,char*,const char*,va_list);
|
||||||
|
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||||
|
/* Version 3.8.7 and later */
|
||||||
|
int (*auto_extension)(void(*)(void));
|
||||||
|
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
||||||
|
void(*)(void*));
|
||||||
|
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
||||||
|
void(*)(void*),unsigned char);
|
||||||
|
int (*cancel_auto_extension)(void(*)(void));
|
||||||
|
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
||||||
|
void *(*malloc64)(sqlite3_uint64);
|
||||||
|
sqlite3_uint64 (*msize)(void*);
|
||||||
|
void *(*realloc64)(void*,sqlite3_uint64);
|
||||||
|
void (*reset_auto_extension)(void);
|
||||||
|
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
||||||
|
void(*)(void*));
|
||||||
|
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
||||||
|
void(*)(void*), unsigned char);
|
||||||
|
int (*strglob)(const char*,const char*);
|
||||||
|
/* Version 3.8.11 and later */
|
||||||
|
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
||||||
|
void (*value_free)(sqlite3_value*);
|
||||||
|
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
||||||
|
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
||||||
|
/* Version 3.9.0 and later */
|
||||||
|
unsigned int (*value_subtype)(sqlite3_value*);
|
||||||
|
void (*result_subtype)(sqlite3_context*,unsigned int);
|
||||||
|
/* Version 3.10.0 and later */
|
||||||
|
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
||||||
|
int (*strlike)(const char*,const char*,unsigned int);
|
||||||
|
int (*db_cacheflush)(sqlite3*);
|
||||||
|
/* Version 3.12.0 and later */
|
||||||
|
int (*system_errno)(sqlite3*);
|
||||||
|
/* Version 3.14.0 and later */
|
||||||
|
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||||
|
char *(*expanded_sql)(sqlite3_stmt*);
|
||||||
|
/* Version 3.18.0 and later */
|
||||||
|
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This is the function signature used for all extension entry points. It
|
||||||
|
** is also defined in the file "loadext.c".
|
||||||
|
*/
|
||||||
|
typedef int (*sqlite3_loadext_entry)(
|
||||||
|
sqlite3 *db, /* Handle to the database. */
|
||||||
|
char **pzErrMsg, /* Used to set error string on failure. */
|
||||||
|
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following macros redefine the API routines so that they are
|
||||||
|
** redirected through the global sqlite3_api structure.
|
||||||
|
**
|
||||||
|
** This header file is also used by the loadext.c source file
|
||||||
|
** (part of the main SQLite library - not an extension) so that
|
||||||
|
** it can get access to the sqlite3_api_routines structure
|
||||||
|
** definition. But the main library does not want to redefine
|
||||||
|
** the API. So the redefinition macros are only valid if the
|
||||||
|
** SQLITE_CORE macros is undefined.
|
||||||
|
*/
|
||||||
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||||
|
#ifndef SQLITE_OMIT_DEPRECATED
|
||||||
|
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||||
|
#endif
|
||||||
|
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||||
|
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||||
|
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||||
|
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||||
|
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||||
|
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||||
|
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||||
|
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||||
|
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||||
|
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||||
|
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||||
|
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||||
|
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||||
|
#define sqlite3_changes sqlite3_api->changes
|
||||||
|
#define sqlite3_close sqlite3_api->close
|
||||||
|
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||||
|
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||||
|
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||||
|
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||||
|
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||||
|
#define sqlite3_column_count sqlite3_api->column_count
|
||||||
|
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||||
|
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||||
|
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||||
|
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||||
|
#define sqlite3_column_double sqlite3_api->column_double
|
||||||
|
#define sqlite3_column_int sqlite3_api->column_int
|
||||||
|
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||||
|
#define sqlite3_column_name sqlite3_api->column_name
|
||||||
|
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||||
|
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||||
|
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||||
|
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||||
|
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||||
|
#define sqlite3_column_text sqlite3_api->column_text
|
||||||
|
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||||
|
#define sqlite3_column_type sqlite3_api->column_type
|
||||||
|
#define sqlite3_column_value sqlite3_api->column_value
|
||||||
|
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||||
|
#define sqlite3_complete sqlite3_api->complete
|
||||||
|
#define sqlite3_complete16 sqlite3_api->complete16
|
||||||
|
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||||
|
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||||
|
#define sqlite3_create_function sqlite3_api->create_function
|
||||||
|
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||||
|
#define sqlite3_create_module sqlite3_api->create_module
|
||||||
|
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||||
|
#define sqlite3_data_count sqlite3_api->data_count
|
||||||
|
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||||
|
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||||
|
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||||
|
#define sqlite3_errcode sqlite3_api->errcode
|
||||||
|
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||||
|
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||||
|
#define sqlite3_exec sqlite3_api->exec
|
||||||
|
#ifndef SQLITE_OMIT_DEPRECATED
|
||||||
|
#define sqlite3_expired sqlite3_api->expired
|
||||||
|
#endif
|
||||||
|
#define sqlite3_finalize sqlite3_api->finalize
|
||||||
|
#define sqlite3_free sqlite3_api->free
|
||||||
|
#define sqlite3_free_table sqlite3_api->free_table
|
||||||
|
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||||
|
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||||
|
#define sqlite3_get_table sqlite3_api->get_table
|
||||||
|
#ifndef SQLITE_OMIT_DEPRECATED
|
||||||
|
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||||
|
#endif
|
||||||
|
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||||
|
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||||
|
#define sqlite3_libversion sqlite3_api->libversion
|
||||||
|
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||||
|
#define sqlite3_malloc sqlite3_api->malloc
|
||||||
|
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||||
|
#define sqlite3_open sqlite3_api->open
|
||||||
|
#define sqlite3_open16 sqlite3_api->open16
|
||||||
|
#define sqlite3_prepare sqlite3_api->prepare
|
||||||
|
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||||
|
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||||
|
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||||
|
#define sqlite3_profile sqlite3_api->profile
|
||||||
|
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||||
|
#define sqlite3_realloc sqlite3_api->realloc
|
||||||
|
#define sqlite3_reset sqlite3_api->reset
|
||||||
|
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||||
|
#define sqlite3_result_double sqlite3_api->result_double
|
||||||
|
#define sqlite3_result_error sqlite3_api->result_error
|
||||||
|
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||||
|
#define sqlite3_result_int sqlite3_api->result_int
|
||||||
|
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||||
|
#define sqlite3_result_null sqlite3_api->result_null
|
||||||
|
#define sqlite3_result_text sqlite3_api->result_text
|
||||||
|
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||||
|
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||||
|
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||||
|
#define sqlite3_result_value sqlite3_api->result_value
|
||||||
|
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||||
|
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||||
|
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||||
|
#define sqlite3_snprintf sqlite3_api->snprintf
|
||||||
|
#define sqlite3_step sqlite3_api->step
|
||||||
|
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||||
|
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||||
|
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||||
|
#define sqlite3_trace sqlite3_api->trace
|
||||||
|
#ifndef SQLITE_OMIT_DEPRECATED
|
||||||
|
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||||
|
#endif
|
||||||
|
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||||
|
#define sqlite3_user_data sqlite3_api->user_data
|
||||||
|
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||||
|
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||||
|
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||||
|
#define sqlite3_value_double sqlite3_api->value_double
|
||||||
|
#define sqlite3_value_int sqlite3_api->value_int
|
||||||
|
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||||
|
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||||
|
#define sqlite3_value_text sqlite3_api->value_text
|
||||||
|
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||||
|
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||||
|
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||||
|
#define sqlite3_value_type sqlite3_api->value_type
|
||||||
|
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||||
|
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
|
||||||
|
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||||
|
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||||
|
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||||
|
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||||
|
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||||
|
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||||
|
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||||
|
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||||
|
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||||
|
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||||
|
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||||
|
#define sqlite3_file_control sqlite3_api->file_control
|
||||||
|
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||||
|
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||||
|
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||||
|
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||||
|
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||||
|
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||||
|
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||||
|
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||||
|
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||||
|
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||||
|
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||||
|
#define sqlite3_sleep sqlite3_api->sleep
|
||||||
|
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||||
|
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||||
|
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||||
|
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||||
|
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||||
|
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||||
|
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||||
|
#define sqlite3_test_control sqlite3_api->test_control
|
||||||
|
#define sqlite3_randomness sqlite3_api->randomness
|
||||||
|
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||||
|
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||||
|
#define sqlite3_limit sqlite3_api->limit
|
||||||
|
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||||
|
#define sqlite3_sql sqlite3_api->sql
|
||||||
|
#define sqlite3_status sqlite3_api->status
|
||||||
|
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||||
|
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||||
|
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||||
|
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||||
|
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||||
|
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||||
|
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||||
|
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||||
|
#define sqlite3_db_config sqlite3_api->db_config
|
||||||
|
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||||
|
#define sqlite3_db_status sqlite3_api->db_status
|
||||||
|
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||||
|
#define sqlite3_log sqlite3_api->log
|
||||||
|
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||||
|
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||||
|
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||||
|
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||||
|
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||||
|
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||||
|
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||||
|
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||||
|
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||||
|
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||||
|
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||||
|
/* Version 3.7.16 and later */
|
||||||
|
#define sqlite3_close_v2 sqlite3_api->close_v2
|
||||||
|
#define sqlite3_db_filename sqlite3_api->db_filename
|
||||||
|
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
||||||
|
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
||||||
|
#define sqlite3_errstr sqlite3_api->errstr
|
||||||
|
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
||||||
|
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
||||||
|
#define sqlite3_stricmp sqlite3_api->stricmp
|
||||||
|
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||||
|
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||||
|
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||||
|
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
||||||
|
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||||
|
/* Version 3.8.7 and later */
|
||||||
|
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||||
|
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
||||||
|
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
||||||
|
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
||||||
|
#define sqlite3_load_extension sqlite3_api->load_extension
|
||||||
|
#define sqlite3_malloc64 sqlite3_api->malloc64
|
||||||
|
#define sqlite3_msize sqlite3_api->msize
|
||||||
|
#define sqlite3_realloc64 sqlite3_api->realloc64
|
||||||
|
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
||||||
|
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
||||||
|
#define sqlite3_result_text64 sqlite3_api->result_text64
|
||||||
|
#define sqlite3_strglob sqlite3_api->strglob
|
||||||
|
/* Version 3.8.11 and later */
|
||||||
|
#define sqlite3_value_dup sqlite3_api->value_dup
|
||||||
|
#define sqlite3_value_free sqlite3_api->value_free
|
||||||
|
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
||||||
|
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
||||||
|
/* Version 3.9.0 and later */
|
||||||
|
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
||||||
|
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
||||||
|
/* Version 3.10.0 and later */
|
||||||
|
#define sqlite3_status64 sqlite3_api->status64
|
||||||
|
#define sqlite3_strlike sqlite3_api->strlike
|
||||||
|
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||||
|
/* Version 3.12.0 and later */
|
||||||
|
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||||
|
/* Version 3.14.0 and later */
|
||||||
|
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||||
|
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||||
|
/* Version 3.18.0 and later */
|
||||||
|
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
|
||||||
|
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||||
|
|
||||||
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
/* This case when the file really is being compiled as a loadable
|
||||||
|
** extension */
|
||||||
|
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
||||||
|
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
||||||
|
# define SQLITE_EXTENSION_INIT3 \
|
||||||
|
extern const sqlite3_api_routines *sqlite3_api;
|
||||||
|
#else
|
||||||
|
/* This case when the file is being statically linked into the
|
||||||
|
** application */
|
||||||
|
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
||||||
|
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
||||||
|
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SQLITE3EXT_H */
|
@ -15,6 +15,8 @@ SUBDIRS += librtlsdr
|
|||||||
SUBDIRS += libhackrf
|
SUBDIRS += libhackrf
|
||||||
SUBDIRS += libairspy
|
SUBDIRS += libairspy
|
||||||
SUBDIRS += libbladerf
|
SUBDIRS += libbladerf
|
||||||
|
SUBDIRS += libsqlite3
|
||||||
|
SUBDIRS += liblimesuite
|
||||||
SUBDIRS += mbelib
|
SUBDIRS += mbelib
|
||||||
SUBDIRS += dsdcc
|
SUBDIRS += dsdcc
|
||||||
SUBDIRS += serialdv
|
SUBDIRS += serialdv
|
||||||
|
Loading…
x
Reference in New Issue
Block a user