diff --git a/GUI/fftviewer_wxgui/fftviewer_frFFTviewer.cpp b/GUI/fftviewer_wxgui/fftviewer_frFFTviewer.cpp index 826cf74bf..9068938f9 100644 --- a/GUI/fftviewer_wxgui/fftviewer_frFFTviewer.cpp +++ b/GUI/fftviewer_wxgui/fftviewer_frFFTviewer.cpp @@ -15,6 +15,7 @@ #include "limesuiteng/StreamConfig.h" #include "limesuiteng/complex.h" #include +#include using namespace std; using namespace lime; @@ -463,9 +464,16 @@ void fftviewer_frFFTviewer::StreamingLoop( const lime::complex32f_t* src[2] = { txPattern[0].data(), txPattern[1].data() };*/ + std::optional callbackId{ std::nullopt }; try { pthis->device->StreamSetup(config, chipIndex); + callbackId = pthis->device->AddHotplugDisconnectCallback( + [](void* data) { + auto* const viewer = reinterpret_cast(data); + viewer->StopStreaming(); + }, + pthis); pthis->device->StreamStart(chipIndex); } catch (std::logic_error& e) { @@ -646,6 +654,10 @@ void fftviewer_frFFTviewer::StreamingLoop( }*/ kiss_fft_free(m_fftCalcPlan); + if (callbackId) + { + pthis->device->RemoveHotplugDisconnectCallback(callbackId.value()); + } pthis->stopProcessing.store(true); pthis->device->StreamStop(chipIndex); diff --git a/GUI/limeGUIFrame.cpp b/GUI/limeGUIFrame.cpp index 38d34a86c..2935eee0a 100644 --- a/GUI/limeGUIFrame.cpp +++ b/GUI/limeGUIFrame.cpp @@ -5,9 +5,9 @@ #endif //__BORLANDC__ #include +#include #include "chips/LMS7002M/lms7002_mainPanel.h" - #include "limeGUIFrame.h" #include "dlgAbout.h" #include "lms7suiteEvents.h" @@ -19,9 +19,7 @@ #include "utility/pnlMiniLog.h" #include "FPGAcontrols_wxgui.h" #include "utility/SPI_wxgui.h" -#include #include "utility/dlgDeviceInfo.h" -#include #include "boards/pnlBoardControls.h" #include "protocols/LMSBoards.h" #include "utility/SPI_wxgui.h" @@ -34,6 +32,9 @@ #include "limesuiteng/SDRDescriptor.h" #include "DeviceTreeNode.h" #include "limesuiteng/Logger.h" +#include "DeviceConnectionPanel.h" + +#include using namespace std; using namespace lime; @@ -43,8 +44,6 @@ static constexpr int controlColumn = 1; limeGUIFrame* limeGUIFrame::obj_ptr = nullptr; -int limeGUIFrame::m_lmsSelection = 0; - void limeGUIFrame::OnGlobalLogEvent(const lime::LogLevel level, const std::string& message) { if (obj_ptr == nullptr || obj_ptr->mMiniLog == nullptr) @@ -399,6 +398,15 @@ void limeGUIFrame::OnDeviceHandleChange(wxCommandEvent& event) wxPostEvent(this, evt); UpdateConnections(lmsControl); + lmsControl->AddHotplugDisconnectCallback( + [&](void* data) { + auto* evt = new wxCommandEvent(); + evt->SetEventType(limeEVT_SDR_HANDLE_SELECTED); + evt->SetString(wxEmptyString); + wxQueueEvent(this, evt); + }, + this); + Fit(); } catch (std::runtime_error& e) { diff --git a/GUI/limeGUIFrame.h b/GUI/limeGUIFrame.h index 9e357a9c6..00a53a963 100644 --- a/GUI/limeGUIFrame.h +++ b/GUI/limeGUIFrame.h @@ -29,10 +29,6 @@ #include #include -#include "IModuleFrame.h" -#include "ISOCPanel.h" -#include "DeviceConnectionPanel.h" -#include "dlgAbout.h" #include "limeGUI.h" class pnlMiniLog; @@ -40,10 +36,21 @@ class lms7002_mainPanel; class fftviewer_frFFTviewer; class LMS_Programming_wxgui; class pnlBoardControls; +class IModuleFrame; +class ISOCPanel; + +namespace lime { +class SDRDevice; +class DeviceConnectionPanel; +} // namespace lime class limeGUIFrame : public wxFrame { - protected: + public: + limeGUIFrame(wxWindow* parent, const AppArgs& appArgs); + ~limeGUIFrame(); + + private: void AddModule(IModuleFrame* module, const std::string& title); void RemoveModule(IModuleFrame* module); // Handlers for AppFrame events. @@ -57,13 +64,6 @@ class limeGUIFrame : public wxFrame void DeviceTreeSelectionChanged(wxTreeEvent& event); - public: - limeGUIFrame(wxWindow* parent, const AppArgs& appArgs); - - virtual ~limeGUIFrame(); - static int m_lmsSelection; - - protected: static void OnGlobalLogEvent(const lime::LogLevel level, const std::string& message); void OnLogMessage(wxCommandEvent& event); void UpdateConnections(lime::SDRDevice* port); diff --git a/embedded/lms7002m/privates.c b/embedded/lms7002m/privates.c index a4227998b..f19b3016c 100644 --- a/embedded/lms7002m/privates.c +++ b/embedded/lms7002m/privates.c @@ -59,7 +59,7 @@ void lms7002m_sleep(long timeInMicroseconds) void lms7002m_spi_write(lms7002m_context* self, uint16_t address, uint16_t value) { uint32_t mosi = address << 16 | value; - mosi |= 1 << 31; + mosi |= 0x80000000; self->hooks.spi16_transact(&mosi, NULL, 1, self->hooks.spi16_userData); } diff --git a/src/CommonFunctions.h b/src/CommonFunctions.h index 3210fc55e..a11c324bf 100644 --- a/src/CommonFunctions.h +++ b/src/CommonFunctions.h @@ -10,9 +10,15 @@ namespace lime { const std::string strFormat(const char* format, ...); -template std::string intToHex(T i) +template std::string intToHex(T i, bool uppercase = false) { std::stringstream stream; + + if (uppercase) + { + stream << std::uppercase; + } + stream << std::setfill('0') << std::setw(sizeof(T) * 2) << std::hex << i; return stream.str(); } diff --git a/src/boards/LMS7002M_SDRDevice.cpp b/src/boards/LMS7002M_SDRDevice.cpp index e30790f4f..554d0d54b 100644 --- a/src/boards/LMS7002M_SDRDevice.cpp +++ b/src/boards/LMS7002M_SDRDevice.cpp @@ -1274,4 +1274,14 @@ OpStatus LMS7002M_SDRDevice::LMS7002TestSignalConfigure(LMS7002M* chip, const Ch return OpStatus::Success; } +std::size_t LMS7002M_SDRDevice::AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) +{ + return 0; +} + +void LMS7002M_SDRDevice::RemoveHotplugDisconnectCallback(std::size_t id) +{ + return; +} + } // namespace lime diff --git a/src/boards/LMS7002M_SDRDevice.h b/src/boards/LMS7002M_SDRDevice.h index 477b0bd83..d756975c9 100644 --- a/src/boards/LMS7002M_SDRDevice.h +++ b/src/boards/LMS7002M_SDRDevice.h @@ -125,6 +125,9 @@ class LIME_API LMS7002M_SDRDevice : public SDRDevice /// @copydoc FPGA::WriteRegister() virtual OpStatus WriteFPGARegister(uint32_t address, uint32_t value); + std::size_t AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) override; + void RemoveHotplugDisconnectCallback(std::size_t id) override; + protected: static OpStatus UpdateFPGAInterfaceFrequency(LMS7002M& soc, FPGA& fpga, uint8_t chipIndex); void SetGainInformationInDescriptor(RFSOCDescriptor& descriptor); diff --git a/src/boards/LimeSDR/LimeSDR.cpp b/src/boards/LimeSDR/LimeSDR.cpp index 149b692fe..89e025304 100644 --- a/src/boards/LimeSDR/LimeSDR.cpp +++ b/src/boards/LimeSDR/LimeSDR.cpp @@ -24,11 +24,12 @@ #include #include -using namespace lime; using namespace lime::LMS64CProtocol; using namespace lime::LMS7002MCSR_Data; using namespace std::literals::string_literals; +namespace lime { + static const uint8_t SPI_LMS7002M = 0; static const uint8_t SPI_FPGA = 1; static const uint8_t SPI_ADF4002 = 2; @@ -154,6 +155,18 @@ LimeSDR::LimeSDR(std::shared_ptr spiLMS, mDeviceDescriptor = descriptor; + mStreamPort->AddOnHotplugDisconnectCallback( + [](void* userData) { + auto* const sdr = reinterpret_cast(userData); + + // Call in reverse order so that the "smaller" scoped callbacks run first. + for (auto iter = sdr->disconnectCallbacks.rbegin(); iter != sdr->disconnectCallbacks.rend(); ++iter) + { + (*iter)(); + } + }, + this); + //must configure synthesizer before using LimeSDR /*if (info.device == LMS_DEV_LIMESDR && info.hardware < 4) { @@ -601,3 +614,28 @@ OpStatus LimeSDR::MemoryRead(std::shared_ptr storage, Region region return OpStatus::Error; return mfpgaPort->MemoryRead(region.address, data, region.size); } + +std::size_t LimeSDR::AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) +{ + std::size_t id = 0; + + if (!disconnectCallbacks.empty()) + { + // As long as elements are not out of order this guarantees a unique ID in the array. + id = disconnectCallbacks.back().id + 1; + } + + disconnectCallbacks.push_back({ function, userData, id }); + + return id; +} + +void LimeSDR::RemoveHotplugDisconnectCallback(std::size_t id) +{ + disconnectCallbacks.erase(std::remove_if(disconnectCallbacks.begin(), + disconnectCallbacks.end(), + [&id](const CallbackInfo& info) { return id == info.id; }), + disconnectCallbacks.end()); +} + +} // namespace lime diff --git a/src/boards/LimeSDR/LimeSDR.h b/src/boards/LimeSDR/LimeSDR.h index 14520e57f..5e065185f 100644 --- a/src/boards/LimeSDR/LimeSDR.h +++ b/src/boards/LimeSDR/LimeSDR.h @@ -56,6 +56,9 @@ class LimeSDR : public LMS7002M_SDRDevice OpStatus MemoryWrite(std::shared_ptr storage, Region region, const void* data) override; OpStatus MemoryRead(std::shared_ptr storage, Region region, void* data) override; + std::size_t AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) override; + void RemoveHotplugDisconnectCallback(std::size_t id) override; + protected: SDRDescriptor GetDeviceInfo(); void ResetUSBFIFO(); @@ -77,6 +80,9 @@ class LimeSDR : public LMS7002M_SDRDevice std::shared_ptr mlms7002mPort; std::shared_ptr mfpgaPort; bool mConfigInProgress; + + std::vector> disconnectCallbacks; + std::size_t mStreamStopCallbackId; }; } // namespace lime diff --git a/src/boards/LimeSDR_Mini/LimeSDR_Mini.cpp b/src/boards/LimeSDR_Mini/LimeSDR_Mini.cpp index fe54303bd..7e6364a9f 100644 --- a/src/boards/LimeSDR_Mini/LimeSDR_Mini.cpp +++ b/src/boards/LimeSDR_Mini/LimeSDR_Mini.cpp @@ -23,12 +23,13 @@ #include #include -using namespace lime; using namespace lime::LMS64CProtocol; using namespace lime::EqualizerCSR; using namespace lime::LMS7002MCSR_Data; using namespace std::literals::string_literals; +namespace lime { + static const int STREAM_BULK_WRITE_ADDRESS = 0x03; static const int STREAM_BULK_READ_ADDRESS = 0x83; @@ -205,6 +206,18 @@ LimeSDR_Mini::LimeSDR_Mini(std::shared_ptr spiLMS, descriptor.socTree->children.push_back(fpgaNode); mDeviceDescriptor = descriptor; + + mStreamPort->AddOnHotplugDisconnectCallback( + [](void* userData) { + auto* const mini = reinterpret_cast(userData); + + // Call in reverse order so that the "smaller" scoped callbacks run first. + for (auto iter = mini->disconnectCallbacks.rbegin(); iter != mini->disconnectCallbacks.rend(); ++iter) + { + (*iter)(); + } + }, + this); } LimeSDR_Mini::~LimeSDR_Mini() @@ -660,3 +673,28 @@ void LimeSDR_Mini::SetSerialNumber(const std::string& number) sscanf(number.c_str(), "%16lX", &sn); mDeviceDescriptor.serialNumber = sn; } + +std::size_t LimeSDR_Mini::AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) +{ + std::size_t id = 0; + + if (!disconnectCallbacks.empty()) + { + // As long as elements are not out of order this guarantees a unique ID in the array. + id = disconnectCallbacks.back().id + 1; + } + + disconnectCallbacks.push_back({ function, userData, id }); + + return id; +} + +void LimeSDR_Mini::RemoveHotplugDisconnectCallback(std::size_t id) +{ + disconnectCallbacks.erase(std::remove_if(disconnectCallbacks.begin(), + disconnectCallbacks.end(), + [&id](const CallbackInfo& info) { return id == info.id; }), + disconnectCallbacks.end()); +} + +} // namespace lime diff --git a/src/boards/LimeSDR_Mini/LimeSDR_Mini.h b/src/boards/LimeSDR_Mini/LimeSDR_Mini.h index 32bb480bf..b69dd85b8 100644 --- a/src/boards/LimeSDR_Mini/LimeSDR_Mini.h +++ b/src/boards/LimeSDR_Mini/LimeSDR_Mini.h @@ -9,7 +9,6 @@ namespace lime { -class USBGeneric; class IComms; class IUSB; @@ -54,6 +53,9 @@ class LimeSDR_Mini : public LMS7002M_SDRDevice void SetSerialNumber(const std::string& number); + std::size_t AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) override; + void RemoveHotplugDisconnectCallback(std::size_t id) override; + protected: SDRDescriptor GetDeviceInfo(); static OpStatus UpdateFPGAInterface(void* userData); @@ -64,6 +66,8 @@ class LimeSDR_Mini : public LMS7002M_SDRDevice std::shared_ptr mlms7002mPort; std::shared_ptr mfpgaPort; bool mConfigInProgress{}; + + std::vector> disconnectCallbacks; }; } // namespace lime diff --git a/src/boards/MMX8/MM_X8.cpp b/src/boards/MMX8/MM_X8.cpp index 3a00182ac..d073571f2 100644 --- a/src/boards/MMX8/MM_X8.cpp +++ b/src/boards/MMX8/MM_X8.cpp @@ -830,4 +830,14 @@ OpStatus LimeSDR_MMX8::UploadTxWaveform(const StreamConfig& config, uint8_t modu return mSubDevices[moduleIndex]->UploadTxWaveform(config, 0, samples, count); } +std::size_t LimeSDR_MMX8::AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) +{ + return 0; +} + +void LimeSDR_MMX8::RemoveHotplugDisconnectCallback(std::size_t id) +{ + return; +} + } //namespace lime diff --git a/src/boards/MMX8/MM_X8.h b/src/boards/MMX8/MM_X8.h index 032f870eb..a7f990ca7 100644 --- a/src/boards/MMX8/MM_X8.h +++ b/src/boards/MMX8/MM_X8.h @@ -149,6 +149,9 @@ class LimeSDR_MMX8 : public SDRDevice OpStatus MemoryRead(std::shared_ptr storage, Region region, void* data) override; OpStatus UploadTxWaveform(const StreamConfig& config, uint8_t moduleIndex, const void** samples, uint32_t count) override; + std::size_t AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) override; + void RemoveHotplugDisconnectCallback(std::size_t id) override; + private: std::shared_ptr mMainFPGAcomms; SDRDescriptor mDeviceDescriptor; diff --git a/src/cli/limeTRX.cpp b/src/cli/limeTRX.cpp index 980b71dfa..d8e743c38 100644 --- a/src/cli/limeTRX.cpp +++ b/src/cli/limeTRX.cpp @@ -467,6 +467,8 @@ int main(int argc, char** argv) else device->StreamStart(chipIndex); + device->AddHotplugDisconnectCallback([](void* data) { *reinterpret_cast(data) = true; }, &stopProgram); + auto startTime = std::chrono::high_resolution_clock::now(); auto t1 = startTime - std::chrono::seconds(2); // rewind t1 to do update on first loop auto t2 = t1; diff --git a/src/comms/USB/CMakeLists.txt b/src/comms/USB/CMakeLists.txt index dfac03cb2..71f16e05f 100644 --- a/src/comms/USB/CMakeLists.txt +++ b/src/comms/USB/CMakeLists.txt @@ -19,9 +19,11 @@ set(COMMS_USB_SOURCES ######################################################################## ## Add to library ######################################################################## -target_include_directories(${MAIN_LIBRARY_NAME} PRIVATE ${THIS_SOURCE_DIR}) target_sources(${MAIN_LIBRARY_NAME} PRIVATE ${COMMS_USB_SOURCES}) if(UNIX) target_sources(${MAIN_LIBRARY_NAME} PRIVATE ${THIS_SOURCE_DIR}/USBGeneric.cpp) +else() + target_sources(${MAIN_LIBRARY_NAME} PRIVATE ${THIS_SOURCE_DIR}/WindowsHotplug.cpp) + target_link_options(${MAIN_LIBRARY_NAME} PUBLIC "cfgmgr32.lib") endif() diff --git a/src/comms/USB/FT601/FT601.cpp b/src/comms/USB/FT601/FT601.cpp index 8f68df784..cb08c0267 100644 --- a/src/comms/USB/FT601/FT601.cpp +++ b/src/comms/USB/FT601/FT601.cpp @@ -71,7 +71,7 @@ FT601::~FT601() Disconnect(); } -bool FT601::Connect(uint16_t vid, uint16_t pid, const char* serial) +bool FT601::Connect(uint16_t vid, uint16_t pid, const std::string& serial) { Disconnect(); #ifdef __unix__ @@ -95,7 +95,7 @@ bool FT601::Connect(uint16_t vid, uint16_t pid, const char* serial) FT_STATUS ftStatus = FT_OK; DWORD dwNumDevices = 0; // Open a device - ftStatus = FT_Create(reinterpret_cast(const_cast(serial)), FT_OPEN_BY_SERIAL_NUMBER, &mFTHandle); + ftStatus = FT_Create(reinterpret_cast(const_cast(serial.c_str())), FT_OPEN_BY_SERIAL_NUMBER, &mFTHandle); if (FT_FAILED(ftStatus)) return false; @@ -110,6 +110,14 @@ bool FT601::Connect(uint16_t vid, uint16_t pid, const char* serial) FT_SetPipeTimeout(mFTHandle, CONTROL_BULK_READ_ADDRESS, 500); FT_SetPipeTimeout(mFTHandle, STREAM_BULK_READ_ADDRESS, 100); FT_SetPipeTimeout(mFTHandle, STREAM_BULK_WRITE_ADDRESS, 100); + + hotplug.AddDeviceToReceiveHotplugDisconnectEvents(vid, pid, serial); + hotplug.AddOnHotplugDisconnectCallback( + [](void* data) { + auto* ft601 = reinterpret_cast(data); + ft601->Disconnect(); + }, + this); return true; #endif } @@ -373,4 +381,13 @@ int FT601::FT_SetStreamPipe(unsigned char ep, size_t size) } #endif +void FT601::AddOnHotplugDisconnectCallback(const IUSB::HotplugDisconnectCallbackType& function, void* userData) +{ +#ifdef __unix__ + libusb_impl.AddOnHotplugDisconnectCallback(function, userData); +#else + hotplug.AddOnHotplugDisconnectCallback(function, userData); +#endif +} + } // namespace lime diff --git a/src/comms/USB/FT601/FT601.h b/src/comms/USB/FT601/FT601.h index 50ab86577..f1e8ac9d6 100644 --- a/src/comms/USB/FT601/FT601.h +++ b/src/comms/USB/FT601/FT601.h @@ -6,6 +6,7 @@ #include "comms/USB/USBGeneric.h" #else #include "FTD3XXLibrary/FTD3XX.h" + #include "comms/USB/WindowsHotplug.h" #endif namespace lime { @@ -19,7 +20,7 @@ class FT601 : public IUSB FT601(); virtual ~FT601(); - bool Connect(uint16_t vid, uint16_t pid, const char* serial) override; + bool Connect(uint16_t vid, uint16_t pid, const std::string& serial) override; void Disconnect() override; bool IsConnected() override; @@ -41,6 +42,8 @@ class FT601 : public IUSB */ OpStatus ResetStreamBuffers(); + void AddOnHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) override; + protected: #ifdef __unix__ USBGeneric libusb_impl; @@ -50,6 +53,8 @@ class FT601 : public IUSB #else FT_HANDLE mFTHandle; int ReinitPipe(unsigned char ep); + + WindowsHotplug hotplug{}; #endif }; diff --git a/src/comms/USB/FX3/FX3.cpp b/src/comms/USB/FX3/FX3.cpp index 10b08f817..b0562cff8 100644 --- a/src/comms/USB/FX3/FX3.cpp +++ b/src/comms/USB/FX3/FX3.cpp @@ -2,7 +2,6 @@ #include -using namespace lime; using namespace std::literals::string_literals; #ifdef __unix__ @@ -14,13 +13,19 @@ using namespace std::literals::string_literals; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif -const int FX3::CTR_WRITE_REQUEST_VALUE = LIBUSB_REQUEST_TYPE_VENDOR; -const int FX3::CTR_READ_REQUEST_VALUE = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN; #else - #include "windows.h" + #include "Windows.h" #include "CyAPI.h" #include + #include "comms/USB/WindowsHotplug.h" +#endif + +namespace lime { +#ifdef __unix__ +const int FX3::CTR_WRITE_REQUEST_VALUE = LIBUSB_REQUEST_TYPE_VENDOR; +const int FX3::CTR_READ_REQUEST_VALUE = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN; +#else class FX3AsyncContext { public: @@ -98,7 +103,7 @@ FX3::~FX3() Disconnect(); } -bool FX3::Connect(uint16_t vid, uint16_t pid, const char* serial) +bool FX3::Connect(uint16_t vid, uint16_t pid, const std::string& serial) { Disconnect(); #ifdef __unix__ @@ -107,8 +112,25 @@ bool FX3::Connect(uint16_t vid, uint16_t pid, const char* serial) if (fx3device->DeviceCount() == 0) return false; - if (fx3device->Open(0) == false) + for (UCHAR i = 0; i < fx3device->DeviceCount(); ++i) + { + if (fx3device->Open(i) == false) + continue; + + if (fx3device->VendorID == vid && fx3device->ProductID == pid && + (serial.empty() || + std::string{ std::wstring_convert>().to_bytes(fx3device->SerialNumber) } == serial)) + { + break; + } + + fx3device->Close(); + } + + if (!fx3device->IsOpen()) + { return false; + } for (int i = 0; i < fx3device->EndPointCount(); ++i) { @@ -117,6 +139,14 @@ bool FX3::Connect(uint16_t vid, uint16_t pid, const char* serial) ep->SetXferSize(len); endpoints[ep->Address] = ep; } + + hotplug.AddDeviceToReceiveHotplugDisconnectEvents(vid, pid, serial); + hotplug.AddOnHotplugDisconnectCallback( + [](void* data) { + auto* fx3 = reinterpret_cast(data); + fx3->Disconnect(); + }, + this); #endif return true; } @@ -250,3 +280,14 @@ void FX3::FreeAsyncContext(void* context) delete reinterpret_cast(context); #endif } + +void FX3::AddOnHotplugDisconnectCallback(const IUSB::HotplugDisconnectCallbackType& function, void* userData) +{ +#ifdef __unix__ + libusb_impl.AddOnHotplugDisconnectCallback(function, userData); +#else + hotplug.AddOnHotplugDisconnectCallback(function, userData); +#endif +} + +} // namespace lime diff --git a/src/comms/USB/FX3/FX3.h b/src/comms/USB/FX3/FX3.h index bc4197b73..812063aa2 100644 --- a/src/comms/USB/FX3/FX3.h +++ b/src/comms/USB/FX3/FX3.h @@ -7,11 +7,14 @@ #ifdef __unix__ #include "comms/USB/USBGeneric.h" -#endif // !__unix__ +#else + #include "comms/USB/WindowsHotplug.h" class CCyFX3Device; class CCyUSBEndPoint; +#endif // !__unix__ + namespace lime { /// @brief A class for communicating with devices using the Cypress USB 3.0 CYUSB3014-BZXC USB controller. @@ -23,7 +26,7 @@ class FX3 : public IUSB FX3(); virtual ~FX3(); - bool Connect(uint16_t vid, uint16_t pid, const char* serial) override; + bool Connect(uint16_t vid, uint16_t pid, const std::string& serial) override; void Disconnect() override; bool IsConnected() override; @@ -38,13 +41,16 @@ class FX3 : public IUSB int32_t ControlTransfer( int requestType, int request, int value, int index, uint8_t* data, size_t length, int32_t timeout_ms) override; + void AddOnHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) override; + static const int CTR_WRITE_REQUEST_VALUE; static const int CTR_READ_REQUEST_VALUE; - static constexpr uint8_t CONTROL_BULK_OUT_ADDRESS = - 0x0F; ///< The memory address for writing information via the bulk transfer protocol. - static constexpr uint8_t CONTROL_BULK_IN_ADDRESS = - 0x8F; ///< THe memory address for reading information via the bulk transfer protocol. + /// The memory address for writing information via the bulk transfer protocol. + static constexpr uint8_t CONTROL_BULK_OUT_ADDRESS = 0x0F; + /// The memory address for reading information via the bulk transfer protocol. + static constexpr uint8_t CONTROL_BULK_IN_ADDRESS = 0x8F; + protected: #ifdef __unix__ USBGeneric libusb_impl; @@ -54,6 +60,8 @@ class FX3 : public IUSB //end points for samples reading and writing std::map endpoints{}; + + WindowsHotplug hotplug{}; #endif }; diff --git a/src/comms/USB/IUSB.h b/src/comms/USB/IUSB.h index b7dbf15f4..972e35051 100644 --- a/src/comms/USB/IUSB.h +++ b/src/comms/USB/IUSB.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "limesuiteng/OpStatus.h" namespace lime { @@ -22,8 +23,16 @@ class IUSB struct VendorProductId { uint16_t vendorId; uint16_t productId; - bool operator<(const VendorProductId& rhs) const { return vendorId < rhs.vendorId || productId < rhs.productId; } + bool operator<(const VendorProductId& rhs) const + { + if (vendorId == rhs.vendorId) + { + return productId < rhs.productId; + } + return vendorId < rhs.vendorId; + } }; + /** * @brief Returns list of detected devices descriptors used for connecting to device. * @param ids Set of vendor and product IDs to search for @@ -39,7 +48,7 @@ class IUSB @param serial The serial number of the device. @return The status of the operation (true on success). */ - virtual bool Connect(uint16_t vid, uint16_t pid, const char* serial = nullptr) = 0; + virtual bool Connect(uint16_t vid, uint16_t pid, const std::string& serial = "") = 0; /** @brief Returns whether this instance is connected to a device. @@ -116,6 +125,15 @@ class IUSB @param context Pointer to transfer context (retuned by AllocateAsyncContext). */ virtual void FreeAsyncContext(void* context) = 0; + + typedef std::function HotplugDisconnectCallbackType; + template struct CallbackInfo { + T function; + void* userData; + + void operator()() { return function(userData); } + }; + virtual void AddOnHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) = 0; }; } // namespace lime diff --git a/src/comms/USB/USBGeneric.cpp b/src/comms/USB/USBGeneric.cpp index bf44510a6..4e2845f65 100644 --- a/src/comms/USB/USBGeneric.cpp +++ b/src/comms/USB/USBGeneric.cpp @@ -110,6 +110,20 @@ static void process_libusbtransfer(libusb_transfer* trans) context->cv.notify_one(); } +// Runs within the gUSBProcessingThread thread +int USBGeneric::HotplugCallback(libusb_context* ctx, libusb_device* device, libusb_hotplug_event event, void* user_data) +{ + auto* usb = reinterpret_cast(user_data); + + for (auto iter = usb->hotplugDisconnectCallbacks.rbegin(); iter != usb->hotplugDisconnectCallbacks.rend(); ++iter) + { + (*iter)(); + } + + usb->Disconnect(); + return 1; +} + USBGeneric::AsyncContext::AsyncContext() : transfer(libusb_alloc_transfer(0)) , bytesXfered(0) @@ -205,7 +219,7 @@ USBGeneric::~USBGeneric() SessionRefCountDecrement(); } -bool USBGeneric::Connect(uint16_t vid, uint16_t pid, const char* serial) +bool USBGeneric::Connect(uint16_t vid, uint16_t pid, const std::string& serial) { libusb_device** devs; // Pointer to pointer of device, used to retrieve a list of devices int usbDeviceCount = libusb_get_device_list(gContextLibUsb, &devs); @@ -257,8 +271,20 @@ bool USBGeneric::Connect(uint16_t vid, uint16_t pid, const char* serial) foundSerial = std::string(data, static_cast(stringLength)); } - if (std::string{ serial }.empty() || std::string{ serial } == foundSerial) + if (serial.empty() || serial == foundSerial) + { + libusb_hotplug_register_callback(gContextLibUsb, + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, + 0, + desc.idVendor, + desc.idProduct, + LIBUSB_HOTPLUG_MATCH_ANY, + HotplugCallback, + this, + nullptr); + break; //found it + } libusb_close(dev_handle); dev_handle = nullptr; @@ -340,6 +366,11 @@ OpStatus USBGeneric::BeginDataXfer(void* context, uint8_t* buffer, size_t length libusb_fill_bulk_transfer(tr, dev_handle, endPointAddr, buffer, length, process_libusbtransfer, xfer, 0); xfer->done.store(false); xfer->bytesXfered = 0; + if (tr->dev_handle == nullptr) + { + return OpStatus::Error; + } + int status = libusb_submit_transfer(tr); if (status != 0) { @@ -409,4 +440,9 @@ OpStatus USBGeneric::ClaimInterface(int32_t interface_number) return OpStatus::Success; } +void USBGeneric::AddOnHotplugDisconnectCallback(const IUSB::HotplugDisconnectCallbackType& function, void* userData) +{ + hotplugDisconnectCallbacks.push_back({ function, userData }); +} + } // namespace lime diff --git a/src/comms/USB/USBGeneric.h b/src/comms/USB/USBGeneric.h index 90a1f988f..ace1101dc 100644 --- a/src/comms/USB/USBGeneric.h +++ b/src/comms/USB/USBGeneric.h @@ -5,8 +5,16 @@ #include #include -struct libusb_device_handle; -struct libusb_transfer; +#ifdef __unix__ + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" + #endif + #include + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif +#endif namespace lime { @@ -35,7 +43,7 @@ class USBGeneric : IUSB virtual ~USBGeneric(); static constexpr int32_t defaultTimeout = 1000; ///< The default timeout to use if none is specified. - bool Connect(uint16_t vid, uint16_t pid, const char* serial) override; + bool Connect(uint16_t vid, uint16_t pid, const std::string& serial) override; bool IsConnected() override; void Disconnect() override; @@ -52,10 +60,16 @@ class USBGeneric : IUSB OpStatus AbortXfer(void* context) override; void FreeAsyncContext(void* context) override; - virtual OpStatus ClaimInterface(int32_t interface_number); + OpStatus ClaimInterface(int32_t interface_number); - protected: + void AddOnHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) override; + + private: libusb_device_handle* dev_handle; //a device handle + + std::vector> hotplugDisconnectCallbacks{}; + + static int HotplugCallback(libusb_context* ctx, libusb_device* device, libusb_hotplug_event event, void* user_data); }; } // namespace lime diff --git a/src/comms/USB/WindowsHotplug.cpp b/src/comms/USB/WindowsHotplug.cpp new file mode 100644 index 000000000..da16a16ae --- /dev/null +++ b/src/comms/USB/WindowsHotplug.cpp @@ -0,0 +1,140 @@ +#include "WindowsHotplug.h" + +#include +#include + +#include "CommonFunctions.h" +#include "IUSB.h" + +#include +#include +#include +#include +#include +#include + +using namespace std::literals::string_literals; +using namespace std::literals::string_view_literals; + +namespace lime { + +namespace { + +template std::vector SplitString(strView string, strView delimiter) +{ + std::vector ret; + + auto position{ string.find(delimiter) }; + + while (position != strView::npos) + { + if (position != 0) + { + ret.push_back(string.substr(0, position)); + } + + string = string.substr(position + delimiter.size()); + position = string.find(delimiter); + } + + if (string.size() > 0) + { + ret.push_back(string); + } + return ret; +} + +std::string_view GetSpecificDeviceIDFromDeviceIDListBySerial(const std::vector& list, const std::string& serial) +{ + for (const auto& deviceID : list) + { + if (deviceID.find(serial) != std::string_view::npos) + { + return deviceID; + } + } + + throw std::runtime_error("Serial not found"s); +} + +std::string GetDeviceID(uint16_t vid, uint16_t pid, const std::string& serial) +{ + ULONG bufferSize = 0; + const std::string filter = "USB\\VID_"s + intToHex(vid, true) + "&PID_"s + intToHex(pid, true); + const ULONG flags = CM_GETIDLIST_FILTER_ENUMERATOR; + + CONFIGRET status = CM_Get_Device_ID_List_Size(&bufferSize, filter.c_str(), flags); + if (status == CR_NO_SUCH_VALUE) // no device like that exists, return + { + return ""s; + } + else if (status != CR_SUCCESS) + { + throw std::runtime_error("CM_Get_Device_ID_List_Size failed"s); + } + + const PZZSTR buffer = new CHAR[bufferSize]; + + status = CM_Get_Device_ID_List(filter.c_str(), buffer, bufferSize, flags); + if (status != CR_SUCCESS) + { + throw std::runtime_error("CM_Get_Device_ID_List failed"s); + } + + const std::string_view allDeviceIDs{ buffer, bufferSize }; + const auto& allDeviceIDsSplits = SplitString(allDeviceIDs, "\0"sv); + + const auto deviceID = std::string{ GetSpecificDeviceIDFromDeviceIDListBySerial(allDeviceIDsSplits, serial) }; + + delete[] buffer; + return deviceID; +} + +} // namespace + +WindowsHotplug ::~WindowsHotplug() +{ + CM_Unregister_Notification(deviceDisconnectCallbackHandle); +} + +void WindowsHotplug::AddOnHotplugDisconnectCallback(const IUSB::HotplugDisconnectCallbackType& function, void* userData) +{ + hotplugDisconnectCallbacks.push_back({ function, userData }); +} + +void WindowsHotplug::AddDeviceToReceiveHotplugDisconnectEvents(uint16_t vid, uint16_t pid, const std::string& serial) +{ + const auto deviceID = GetDeviceID(vid, pid, serial); + const auto wideDeviceID = std::wstring_convert>().from_bytes(deviceID); + + CM_NOTIFY_FILTER filter{}; + filter.cbSize = sizeof(filter); + filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE; + std::wcsncpy(filter.u.DeviceInstance.InstanceId, + wideDeviceID.c_str(), + std::min(sizeof(filter.u.DeviceInstance.InstanceId), wideDeviceID.size())); + + auto returnValue = CM_Register_Notification(&filter, this, callback, &deviceDisconnectCallbackHandle); + if (returnValue != CR_SUCCESS) + { + throw std::runtime_error("CM_Register_Notification failed with error code "s + intToHex(returnValue)); + } +} + +DWORD CALLBACK WindowsHotplug::callback( + HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize) +{ + if (CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED == Action) + { + auto* hotplug = reinterpret_cast(Context); + + for (auto iter = hotplug->hotplugDisconnectCallbacks.rbegin(); iter != hotplug->hotplugDisconnectCallbacks.rend(); ++iter) + { + (*iter)(); + } + } + + return ERROR_SUCCESS; +} + +} // namespace lime diff --git a/src/comms/USB/WindowsHotplug.h b/src/comms/USB/WindowsHotplug.h new file mode 100644 index 000000000..2a022f0d6 --- /dev/null +++ b/src/comms/USB/WindowsHotplug.h @@ -0,0 +1,34 @@ +#ifndef LIME_WINDOWS_HOTPLUG_H +#define LIME_WINDOWS_HOTPLUG_H + +#include "Windows.h" +#include "cfgmgr32.h" + +#include "comms/USB/IUSB.h" + +#include +#include +#include + +namespace lime { + +class WindowsHotplug +{ + public: + ~WindowsHotplug(); + + void AddOnHotplugDisconnectCallback(const IUSB::HotplugDisconnectCallbackType& function, void* userData); + + void AddDeviceToReceiveHotplugDisconnectEvents(uint16_t vid, uint16_t pid, const std::string& serial); + + private: + std::vector> hotplugDisconnectCallbacks{}; + HCMNOTIFICATION deviceDisconnectCallbackHandle{}; + + static DWORD CALLBACK callback( + HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize); +}; + +} // namespace lime + +#endif // LIME_WINDOWS_HOTPLUG_H diff --git a/src/examples/basicRX.cpp b/src/examples/basicRX.cpp index f55f6decb..e97188228 100644 --- a/src/examples/basicRX.cpp +++ b/src/examples/basicRX.cpp @@ -97,7 +97,7 @@ int main(int argc, char** argv) device->StreamSetup(stream, chipIndex); device->StreamStart(chipIndex); - + device->AddHotplugDisconnectCallback([](void* data) { *reinterpret_cast(data) = true; }, &stopProgram); } catch (std::runtime_error& e) { std::cout << "Failed to configure settings: "sv << e.what() << std::endl; diff --git a/src/examples/basicTX.cpp b/src/examples/basicTX.cpp index 213776bd1..a96a1ecdb 100644 --- a/src/examples/basicTX.cpp +++ b/src/examples/basicTX.cpp @@ -90,7 +90,7 @@ int main(int argc, char** argv) device->StreamSetup(stream, chipIndex); device->StreamStart(chipIndex); - + device->AddHotplugDisconnectCallback([](void* data) { *reinterpret_cast(data) = true; }, &stopProgram); } catch (std::runtime_error& e) { std::cout << "Failed to configure settings: "sv << e.what() << std::endl; diff --git a/src/examples/dualRXTX.cpp b/src/examples/dualRXTX.cpp index 71efe7f7e..b6517e8c7 100644 --- a/src/examples/dualRXTX.cpp +++ b/src/examples/dualRXTX.cpp @@ -110,7 +110,7 @@ int main(int argc, char** argv) device->StreamSetup(stream, chipIndex); device->StreamStart(chipIndex); - + device->AddHotplugDisconnectCallback([](void* data) { *reinterpret_cast(data) = true; }, &stopProgram); } catch (std::runtime_error& e) { std::cout << "Failed to configure settings: "sv << e.what() << std::endl; diff --git a/src/include/limesuiteng/SDRDevice.h b/src/include/limesuiteng/SDRDevice.h index 34dd1e78c..79fe238ef 100644 --- a/src/include/limesuiteng/SDRDevice.h +++ b/src/include/limesuiteng/SDRDevice.h @@ -572,6 +572,29 @@ class LIME_API SDRDevice /// @param serialNumber Device's serial number /// @return The operation success state. virtual OpStatus WriteSerialNumber(uint64_t serialNumber); + + /// @brief The type of the function to call when a disconnect event happens. + typedef std::function HotplugDisconnectCallbackType; + + /// @brief The structure to hold the information for the callback + /// @tparam T The type of function to call + template struct CallbackInfo { + T function; + void* userData; + std::size_t id; + + void operator()() const { return function(userData); } + }; + + /// @brief Adds a callback to run when a hotplug disconnect event happens (currently USB only). + /// @param function The function to run when a disconnect happens. + /// @param userData The data to pass into the function when it runs. + /// @return The ID of the callback for removal later. + virtual std::size_t AddHotplugDisconnectCallback(const HotplugDisconnectCallbackType& function, void* userData) = 0; + + /// @brief Removes the given hotplug disconnect callback by ID. + /// @param id The ID of the callback to remove. + virtual void RemoveHotplugDisconnectCallback(std::size_t id) = 0; }; } // namespace lime diff --git a/src/protocols/TRXLooper.cpp b/src/protocols/TRXLooper.cpp index 457d5acf2..78dffa900 100644 --- a/src/protocols/TRXLooper.cpp +++ b/src/protocols/TRXLooper.cpp @@ -257,28 +257,35 @@ void TRXLooper::Stop() { lime::error("Failed to join TRXLooper threads"s); } - fpga->StopStreaming(); - uint32_t fpgaTxPktIngressCount; - uint32_t fpgaTxPktDropCounter; - fpga->ReadTxPacketCounters(chipId, &fpgaTxPktIngressCount, &fpgaTxPktDropCounter); - if (mCallback_logMessage) + mStreamEnabled = false; + + try { - char msg[512]; - std::snprintf(msg, - sizeof(msg), - "Tx%i stop: host sent packets: %li (0x%08lX), FPGA packet ingresed: %i (0x%08X), diff: %li, Tx packet dropped: %i", - chipId, - mTx.stats.packets, - mTx.stats.packets, - fpgaTxPktIngressCount, - fpgaTxPktIngressCount, - (mTx.stats.packets & 0xFFFFFFFF) - fpgaTxPktIngressCount, - fpgaTxPktDropCounter); - mCallback_logMessage(LogLevel::Debug, msg); - } + fpga->StopStreaming(); - mStreamEnabled = false; + uint32_t fpgaTxPktIngressCount; + uint32_t fpgaTxPktDropCounter; + fpga->ReadTxPacketCounters(chipId, &fpgaTxPktIngressCount, &fpgaTxPktDropCounter); + if (mCallback_logMessage) + { + char msg[512]; + std::snprintf(msg, + sizeof(msg), + "Tx%i stop: host sent packets: %li (0x%08lX), FPGA packet ingresed: %i (0x%08X), diff: %li, Tx packet dropped: %i", + chipId, + mTx.stats.packets, + mTx.stats.packets, + fpgaTxPktIngressCount, + fpgaTxPktIngressCount, + (mTx.stats.packets & 0xFFFFFFFF) - fpgaTxPktIngressCount, + fpgaTxPktDropCounter); + mCallback_logMessage(LogLevel::Debug, msg); + } + } catch (const std::exception& e) + { + lime::error("%s", e.what()); + } } void TRXLooper::Teardown()