diff --git a/include/icsneo/communication/communication.h b/include/icsneo/communication/communication.h index 38c9006..60859b9 100644 --- a/include/icsneo/communication/communication.h +++ b/include/icsneo/communication/communication.h @@ -40,6 +40,8 @@ public: bool isDisconnected(); virtual void spawnThreads(); virtual void joinThreads(); + void modeChangeIncoming() { driver->modeChangeIncoming(); } + void awaitModeChangeComplete() { driver->awaitModeChangeComplete(); } bool rawWrite(const std::vector& bytes) { return driver->write(bytes); } virtual bool sendPacket(std::vector& bytes); bool redirectRead(std::function&&)> redirectTo); diff --git a/include/icsneo/communication/driver.h b/include/icsneo/communication/driver.h index c43b2a2..a39ec36 100644 --- a/include/icsneo/communication/driver.h +++ b/include/icsneo/communication/driver.h @@ -20,6 +20,8 @@ public: virtual ~Driver() {} virtual bool open() = 0; virtual bool isOpen() = 0; + virtual void modeChangeIncoming() {} + virtual void awaitModeChangeComplete() {} virtual bool isDisconnected() { return disconnected; }; virtual bool close() = 0; virtual bool read(std::vector& bytes, size_t limit = 0); diff --git a/include/icsneo/platform/posix/stm32.h b/include/icsneo/platform/posix/stm32.h index e70589c..45f7517 100644 --- a/include/icsneo/platform/posix/stm32.h +++ b/include/icsneo/platform/posix/stm32.h @@ -24,21 +24,28 @@ public: * Other POSIX systems (BSDs, QNX, etc) will need bespoke code written in the future */ STM32(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {} - ~STM32() { if(isOpen()) close(); } + ~STM32(); static std::vector FindByProduct(int product); - bool open(); - bool isOpen(); - bool close(); + bool open() override; + bool isOpen() override; + bool close() override; + + void modeChangeIncoming() override; + void awaitModeChangeComplete() override; private: neodevice_t& device; int fd = -1; + std::atomic modeChanging{false}; + std::thread modeChangeThread; + std::mutex modeChangeMutex; + std::condition_variable modeChangeCV; static std::string HandleToTTY(neodevice_handle_t handle); - void readTask(); - void writeTask(); + void readTask() override; + void writeTask() override; bool fdIsValid(); }; diff --git a/platform/posix/stm32.cpp b/platform/posix/stm32.cpp index 7989861..f56945a 100644 --- a/platform/posix/stm32.cpp +++ b/platform/posix/stm32.cpp @@ -14,6 +14,12 @@ using namespace icsneo; +STM32::~STM32() { + awaitModeChangeComplete(); + if(isOpen()) + close(); +} + bool STM32::open() { if(isOpen()) { report(APIEvent::Type::DeviceCurrentlyOpen, APIEvent::Severity::Error); @@ -111,6 +117,12 @@ bool STM32::close() { WriteOperation flushop; while (readQueue.try_dequeue(flush)) {} while (writeQueue.try_dequeue(flushop)) {} + + if(modeChanging) { + modeChanging = false; + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + return open(); // Reopen the reenumerated device + } if(ret == 0) { return true; @@ -120,6 +132,18 @@ bool STM32::close() { } } +void STM32::modeChangeIncoming() { + modeChanging = true; +} + +void STM32::awaitModeChangeComplete() { + std::unique_lock lk(modeChangeMutex); + if(modeChanging && !modeChangeThread.joinable()) // Waiting for the thread to start + modeChangeCV.wait_for(lk, std::chrono::seconds(1), [this] { return modeChangeThread.joinable(); }); + if(modeChangeThread.joinable()) + modeChangeThread.join(); +} + void STM32::readTask() { constexpr size_t READ_BUFFER_SIZE = 2048; uint8_t readbuf[READ_BUFFER_SIZE]; @@ -131,10 +155,27 @@ void STM32::readTask() { tv.tv_usec = 50000; // 50ms ::select(fd + 1, &rfds, NULL, NULL, &tv); auto bytesRead = ::read(fd, readbuf, READ_BUFFER_SIZE); - if(bytesRead > 0) + if(bytesRead > 0) { +#if 0 // Perhaps helpful for debugging :) + std::cout << "Read data: (" << bytesRead << ')' << std::hex << std::endl; + for(int i = 0; i < bytesRead; i += 16) { + for(int j = 0; j < std::min(bytesRead - i, 16); j++) + std::cout << std::setw(2) << std::setfill('0') << uint32_t(readbuf[i+j]) << ' '; + std::cout << std::endl; + } + std::cout << std::dec << std::endl; +#endif + readQueue.enqueue_bulk(readbuf, bytesRead); - else { - if(!fdIsValid() && !isDisconnected()) { + } else { + if(modeChanging) { + // We were expecting a disconnect for reenumeration + modeChangeThread = std::thread([this] { + modeChangeCV.notify_all(); + close(); // Which will trigger an open() due to modeChanging + }); + break; + } else if(!closing && !fdIsValid() && !isDisconnected()) { disconnected = true; report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error); }