/** @file Connection_uLimeSDR.cpp @author Lime Microsystems @brief Implementation of uLimeSDR board connection. */ #include "Connection_uLimeSDR.h" #include "ErrorReporting.h" #include #include #include #include #include #include #include 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 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 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(trans->user_data); std::unique_lock 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= 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 lck(contexts[contextHandle].transferLock); while(contexts[contextHandle].done.load() == false && std::chrono::duration_cast(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 lck(contextsToSend[contextHandle].transferLock); while(contextsToSend[contextHandle].done.load() == false && std::chrono::duration_cast(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