From 993974234bb3db9faf04309e9565def45d498c54 Mon Sep 17 00:00:00 2001 From: Paul Hollinsky Date: Thu, 28 Feb 2019 18:32:16 -0500 Subject: [PATCH] Remove the dependency on libftdipp and libboost for Linux and macOS --- CMakeLists.txt | 6 +- README.md | 3 +- include/icsneo/platform/posix/ftdi.h | 43 +++++--- platform/posix/ftdi.cpp | 159 ++++++++++++++++++--------- 4 files changed, 138 insertions(+), 73 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 92a9319..fa24414 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,12 +132,13 @@ set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON) # libftdi if(NOT WIN32) - target_include_directories(icsneocpp PUBLIC third-party/libftdi/ftdipp) + target_include_directories(icsneocpp PUBLIC third-party/libftdi/src) set(LIBFTDI_DOCUMENTATION OFF) set(LIBFTDI_BUILD_TESTS OFF) set(LIBFTDI_INSTALL OFF) set(LIBFTDI_PYTHON_BINDINGS OFF) set(LIBFTDI_LINK_PYTHON_LIBRARY OFF) + set(FTDIPP OFF) set(FTDI_EEPROM OFF) add_subdirectory(third-party/libftdi) endif(NOT WIN32) @@ -180,8 +181,7 @@ target_compile_features(icsneolegacy PRIVATE cxx_auto_type cxx_constexpr cxx_lam if(NOT WIN32) find_package(Threads) set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON) - set_property(TARGET ftdipp1-static PROPERTY POSITION_INDEPENDENT_CODE ON) - target_link_libraries(icsneocpp PUBLIC ftdipp1-static) + target_link_libraries(icsneocpp PUBLIC ftdi1-static) target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT}) endif() diff --git a/README.md b/README.md index c477c86..bd72d96 100644 --- a/README.md +++ b/README.md @@ -121,5 +121,4 @@ The dependencies are as follows - CMake 3.2 or above - GCC 4.7 or above, 4.8+ recommended - `libusb-1.0-0-dev` - - `libboost-dev` - - `build-essential` on Ubuntu is recommended \ No newline at end of file + - `build-essential` is recommended \ No newline at end of file diff --git a/include/icsneo/platform/posix/ftdi.h b/include/icsneo/platform/posix/ftdi.h index ead692f..4957f18 100644 --- a/include/icsneo/platform/posix/ftdi.h +++ b/include/icsneo/platform/posix/ftdi.h @@ -4,8 +4,7 @@ #include #include #include -#include -#include +#include #include "icsneo/device/neodevice.h" #include "icsneo/communication/icommunication.h" #include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h" @@ -15,37 +14,49 @@ namespace icsneo { class FTDI : public ICommunication { public: - static constexpr neodevice_handle_t INVALID_HANDLE = 0x7fffffff; // int32_t max value static std::vector FindByProduct(int product); - static bool IsHandleValid(neodevice_handle_t handle); FTDI(device_errorhandler_t err, neodevice_t& forDevice); ~FTDI() { close(); } bool open(); bool close(); - bool isOpen() { return ftdiDevice.is_open(); } + bool isOpen() { return ftdi.isOpen(); } private: - static Ftdi::Context context; - static neodevice_handle_t handleCounter; - class FTDIDevice : public Ftdi::Context { + class FTDIContext { public: - FTDIDevice() {} - FTDIDevice(const Ftdi::Context &x) : Ftdi::Context(x) { - handle = handleCounter++; + FTDIContext() : context(ftdi_new()) {} + ~FTDIContext() { + if(context) + ftdi_free(context); // calls ftdi_deinit and ftdi_close if required + context = nullptr; } - neodevice_handle_t handle = INVALID_HANDLE; + + std::pair> findDevices(int pid); + int openDevice(int pid, const char* serial); + bool closeDevice(); + bool isOpen() const { return deviceOpen; } + int flush() { return ftdi_usb_purge_buffers(context); } + int reset() { return ftdi_usb_reset(context); } + int read(uint8_t* data, size_t size) { return ftdi_read_data(context, data, (int)size); } + int write(const uint8_t* data, size_t size) { return ftdi_write_data(context, data, (int)size); } + int setBaudrate(int baudrate) { return ftdi_set_baudrate(context, baudrate); } + bool setReadTimeout(int timeout) { if(context == nullptr) return false; context->usb_read_timeout = timeout; return true; } + bool setWriteTimeout(int timeout) { if(context == nullptr) return false; context->usb_write_timeout = timeout; return true; } + private: + struct ftdi_context* context; + bool deviceOpen = false; }; - static std::vector searchResultDevices; - static bool GetDeviceForHandle(neodevice_handle_t handle, FTDIDevice& device); - + FTDIContext ftdi; + + static std::vector> handles; + void readTask(); void writeTask(); bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices neodevice_t& device; device_errorhandler_t err; - FTDIDevice ftdiDevice; }; } diff --git a/platform/posix/ftdi.cpp b/platform/posix/ftdi.cpp index fe83638..fb67da4 100644 --- a/platform/posix/ftdi.cpp +++ b/platform/posix/ftdi.cpp @@ -3,79 +3,67 @@ #include #include #include +#include +#include using namespace icsneo; -// Instantiate static variables -neodevice_handle_t FTDI::handleCounter = 1; -Ftdi::Context FTDI::context; -std::vector FTDI::searchResultDevices; +std::vector> FTDI::handles; -/* Theory: Ftdi::List::find_all gives us back Ftdi::Context objects, but these can't be passed - * back and forth with C nicely. So we wrap the Ftdi::Context objects in FTDIDevice classes which - * will give it a nice neodevice_handle_t handle that we can reference it by. These FTDIDevice objects are - * stored in searchResultDevices, and then moved into the instantiated FTDI class by the constructor. - */ std::vector FTDI::FindByProduct(int product) { constexpr size_t deviceSerialBufferLength = sizeof(device.serial); std::vector found; + FTDIContext context; - auto devlist = std::unique_ptr(Ftdi::List::find_all(context, INTREPID_USB_VENDOR_ID, product)); - searchResultDevices.clear(); - for(auto it = devlist->begin(); it != devlist->end(); it++) - searchResultDevices.push_back(*it); // The upconversion to FTDIDevice will assign a handle + std::pair> result = context.findDevices(product); + if(result.first < 0) + return found; // TODO Flag an error for the client application, there was an issue with FTDI - for(auto& dev : searchResultDevices) { + for(auto& serial : result.second) { neodevice_t d; - auto& serial = dev.serial(); strncpy(d.serial, serial.c_str(), deviceSerialBufferLength - 1); d.serial[deviceSerialBufferLength - 1] = '\0'; // strncpy does not write a null terminator if serial is too long - d.handle = dev.handle; + std::tuple devHandle = std::make_tuple(product, serial); + auto it = std::find(handles.begin(), handles.end(), devHandle); + size_t foundHandle = SIZE_MAX; + if(it != handles.end()) { + foundHandle = it - handles.begin(); + } else { + foundHandle = handles.size(); + handles.push_back(devHandle); + } + d.handle = foundHandle; found.push_back(d); } return found; } -bool FTDI::IsHandleValid(neodevice_handle_t handle) { - for(auto& dev : searchResultDevices) { - if(dev.handle != handle) - continue; - - return true; - } - return false; -} - -bool FTDI::GetDeviceForHandle(neodevice_handle_t handle, FTDIDevice& device) { - for(auto& dev : searchResultDevices) { - if(dev.handle != handle) - continue; - - device = dev; - return true; - } - return false; -} - FTDI::FTDI(device_errorhandler_t err, neodevice_t& forDevice) : device(forDevice), err(err) { - openable = GetDeviceForHandle(forDevice.handle, ftdiDevice); + openable = strlen(forDevice.serial) > 0 && device.handle >= 0 && device.handle < (neodevice_handle_t)handles.size(); } bool FTDI::open() { - if(isOpen() || !openable) + if(isOpen()) return false; - if(ftdiDevice.open()) { + if(!openable) { + err(APIError::InvalidNeoDevice); + return false; + } + + // At this point the handle has been checked to be within the bounds of the handles array + std::tuple& handle = handles[device.handle]; + if(ftdi.openDevice(std::get<0>(handle), std::get<1>(handle).c_str()) != 0) { err(APIError::DriverFailedToOpen); return false; } - ftdiDevice.set_usb_read_timeout(100); - ftdiDevice.set_usb_write_timeout(1000); - ftdiDevice.reset(); - ftdiDevice.set_baud_rate(500000); - ftdiDevice.flush(); + ftdi.setReadTimeout(100); + ftdi.setWriteTimeout(1000); + ftdi.reset(); + ftdi.setBaudrate(500000); + ftdi.flush(); // Create threads closing = false; @@ -97,25 +85,92 @@ bool FTDI::close() { if(writeThread.joinable()) writeThread.join(); - closing = false; - - bool ret = true; - if(ftdiDevice.close()) - ret = false; + bool ret = ftdi.closeDevice(); uint8_t flush; WriteOperation flushop; while(readQueue.try_dequeue(flush)) {} while(writeQueue.try_dequeue(flushop)) {} + closing = false; return ret; } +std::pair> FTDI::FTDIContext::findDevices(int pid) { + std::pair> ret; + + if(context == nullptr) { + ret.first = -1; + return ret; + } + if(pid == 0) { + ret.first = -2; + return ret; + } + + struct ftdi_device_list* devlist = nullptr; + ret.first = ftdi_usb_find_all(context, &devlist, INTREPID_USB_VENDOR_ID, pid); + if(ret.first < 1) { + // Didn't find anything, maybe got an error + if(devlist != nullptr) + ftdi_list_free(&devlist); + return ret; + } + + if(devlist == nullptr) { + ret.first = -4; + return ret; + } + + for (struct ftdi_device_list* curdev = devlist; curdev != NULL;) { + char serial[32]; + memset(serial, 0, sizeof(serial)); + int result = ftdi_usb_get_strings(context, curdev->dev, nullptr, 0, nullptr, 0, serial, 32); + size_t len = strlen(serial); + if(result >= 0 && len > 0) + ret.second.emplace_back(serial); + else if(ret.first > 0) + ret.first--; // We're discarding this device + curdev = curdev->next; + } + + ftdi_list_free(&devlist); + return ret; +} + +int FTDI::FTDIContext::openDevice(int pid, const char* serial) { + if(context == nullptr) + return 1; + if(pid == 0 || serial == nullptr) + return 2; + if(serial[0] == '\0') + return 3; + if(deviceOpen) + return 4; + int ret = ftdi_usb_open_desc(context, INTREPID_USB_VENDOR_ID, pid, nullptr, serial); + if(ret == 0 /* all ok */) + deviceOpen = true; + return ret; +} + +bool FTDI::FTDIContext::closeDevice() { + if(context == nullptr) + return false; + if(!deviceOpen) + return true; + + int ret = ftdi_usb_close(context); + if(ret != 0) + return false; + deviceOpen = false; + return true; +} + void FTDI::readTask() { constexpr size_t READ_BUFFER_SIZE = 8; uint8_t readbuf[READ_BUFFER_SIZE]; while(!closing) { - auto readBytes = ftdiDevice.read(readbuf, READ_BUFFER_SIZE); + auto readBytes = ftdi.read(readbuf, READ_BUFFER_SIZE); if(readBytes > 0) readQueue.enqueue_bulk(readbuf, readBytes); } @@ -127,6 +182,6 @@ void FTDI::writeTask() { if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100))) continue; - ftdiDevice.write(writeOp.bytes.data(), (int)writeOp.bytes.size()); + ftdi.write(writeOp.bytes.data(), (int)writeOp.bytes.size()); } } \ No newline at end of file