Remove the dependency on libftdipp and libboost for Linux and macOS
parent
1ad201ddf6
commit
993974234b
|
|
@ -132,12 +132,13 @@ set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
# libftdi
|
# libftdi
|
||||||
if(NOT WIN32)
|
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_DOCUMENTATION OFF)
|
||||||
set(LIBFTDI_BUILD_TESTS OFF)
|
set(LIBFTDI_BUILD_TESTS OFF)
|
||||||
set(LIBFTDI_INSTALL OFF)
|
set(LIBFTDI_INSTALL OFF)
|
||||||
set(LIBFTDI_PYTHON_BINDINGS OFF)
|
set(LIBFTDI_PYTHON_BINDINGS OFF)
|
||||||
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF)
|
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF)
|
||||||
|
set(FTDIPP OFF)
|
||||||
set(FTDI_EEPROM OFF)
|
set(FTDI_EEPROM OFF)
|
||||||
add_subdirectory(third-party/libftdi)
|
add_subdirectory(third-party/libftdi)
|
||||||
endif(NOT WIN32)
|
endif(NOT WIN32)
|
||||||
|
|
@ -180,8 +181,7 @@ target_compile_features(icsneolegacy PRIVATE cxx_auto_type cxx_constexpr cxx_lam
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
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 ftdi1-static)
|
||||||
target_link_libraries(icsneocpp PUBLIC ftdipp1-static)
|
|
||||||
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,5 +121,4 @@ The dependencies are as follows
|
||||||
- CMake 3.2 or above
|
- CMake 3.2 or above
|
||||||
- GCC 4.7 or above, 4.8+ recommended
|
- GCC 4.7 or above, 4.8+ recommended
|
||||||
- `libusb-1.0-0-dev`
|
- `libusb-1.0-0-dev`
|
||||||
- `libboost-dev`
|
- `build-essential` is recommended
|
||||||
- `build-essential` on Ubuntu is recommended
|
|
||||||
|
|
@ -4,8 +4,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <atomic>
|
#include <ftdi.h>
|
||||||
#include <ftdi.hpp>
|
|
||||||
#include "icsneo/device/neodevice.h"
|
#include "icsneo/device/neodevice.h"
|
||||||
#include "icsneo/communication/icommunication.h"
|
#include "icsneo/communication/icommunication.h"
|
||||||
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
|
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
|
||||||
|
|
@ -15,37 +14,49 @@ namespace icsneo {
|
||||||
|
|
||||||
class FTDI : public ICommunication {
|
class FTDI : public ICommunication {
|
||||||
public:
|
public:
|
||||||
static constexpr neodevice_handle_t INVALID_HANDLE = 0x7fffffff; // int32_t max value
|
|
||||||
static std::vector<neodevice_t> FindByProduct(int product);
|
static std::vector<neodevice_t> FindByProduct(int product);
|
||||||
static bool IsHandleValid(neodevice_handle_t handle);
|
|
||||||
|
|
||||||
FTDI(device_errorhandler_t err, neodevice_t& forDevice);
|
FTDI(device_errorhandler_t err, neodevice_t& forDevice);
|
||||||
~FTDI() { close(); }
|
~FTDI() { close(); }
|
||||||
bool open();
|
bool open();
|
||||||
bool close();
|
bool close();
|
||||||
bool isOpen() { return ftdiDevice.is_open(); }
|
bool isOpen() { return ftdi.isOpen(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Ftdi::Context context;
|
class FTDIContext {
|
||||||
static neodevice_handle_t handleCounter;
|
|
||||||
class FTDIDevice : public Ftdi::Context {
|
|
||||||
public:
|
public:
|
||||||
FTDIDevice() {}
|
FTDIContext() : context(ftdi_new()) {}
|
||||||
FTDIDevice(const Ftdi::Context &x) : Ftdi::Context(x) {
|
~FTDIContext() {
|
||||||
handle = handleCounter++;
|
if(context)
|
||||||
|
ftdi_free(context); // calls ftdi_deinit and ftdi_close if required
|
||||||
|
context = nullptr;
|
||||||
}
|
}
|
||||||
neodevice_handle_t handle = INVALID_HANDLE;
|
|
||||||
|
std::pair<int, std::vector<std::string>> 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<FTDIDevice> searchResultDevices;
|
FTDIContext ftdi;
|
||||||
static bool GetDeviceForHandle(neodevice_handle_t handle, FTDIDevice& device);
|
|
||||||
|
static std::vector<std::tuple<int, std::string>> handles;
|
||||||
|
|
||||||
void readTask();
|
void readTask();
|
||||||
void writeTask();
|
void writeTask();
|
||||||
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
|
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
|
||||||
|
|
||||||
neodevice_t& device;
|
neodevice_t& device;
|
||||||
device_errorhandler_t err;
|
device_errorhandler_t err;
|
||||||
FTDIDevice ftdiDevice;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,79 +3,67 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
// Instantiate static variables
|
std::vector<std::tuple<int, std::string>> FTDI::handles;
|
||||||
neodevice_handle_t FTDI::handleCounter = 1;
|
|
||||||
Ftdi::Context FTDI::context;
|
|
||||||
std::vector<FTDI::FTDIDevice> FTDI::searchResultDevices;
|
|
||||||
|
|
||||||
/* 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<neodevice_t> FTDI::FindByProduct(int product) {
|
std::vector<neodevice_t> FTDI::FindByProduct(int product) {
|
||||||
constexpr size_t deviceSerialBufferLength = sizeof(device.serial);
|
constexpr size_t deviceSerialBufferLength = sizeof(device.serial);
|
||||||
std::vector<neodevice_t> found;
|
std::vector<neodevice_t> found;
|
||||||
|
FTDIContext context;
|
||||||
|
|
||||||
auto devlist = std::unique_ptr<Ftdi::List>(Ftdi::List::find_all(context, INTREPID_USB_VENDOR_ID, product));
|
std::pair<int, std::vector<std::string>> result = context.findDevices(product);
|
||||||
searchResultDevices.clear();
|
if(result.first < 0)
|
||||||
for(auto it = devlist->begin(); it != devlist->end(); it++)
|
return found; // TODO Flag an error for the client application, there was an issue with FTDI
|
||||||
searchResultDevices.push_back(*it); // The upconversion to FTDIDevice will assign a handle
|
|
||||||
|
|
||||||
for(auto& dev : searchResultDevices) {
|
for(auto& serial : result.second) {
|
||||||
neodevice_t d;
|
neodevice_t d;
|
||||||
auto& serial = dev.serial();
|
|
||||||
strncpy(d.serial, serial.c_str(), deviceSerialBufferLength - 1);
|
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.serial[deviceSerialBufferLength - 1] = '\0'; // strncpy does not write a null terminator if serial is too long
|
||||||
d.handle = dev.handle;
|
std::tuple<int, std::string> 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);
|
found.push_back(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
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) {
|
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() {
|
bool FTDI::open() {
|
||||||
if(isOpen() || !openable)
|
if(isOpen())
|
||||||
return false;
|
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<int, std::string>& handle = handles[device.handle];
|
||||||
|
if(ftdi.openDevice(std::get<0>(handle), std::get<1>(handle).c_str()) != 0) {
|
||||||
err(APIError::DriverFailedToOpen);
|
err(APIError::DriverFailedToOpen);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ftdiDevice.set_usb_read_timeout(100);
|
ftdi.setReadTimeout(100);
|
||||||
ftdiDevice.set_usb_write_timeout(1000);
|
ftdi.setWriteTimeout(1000);
|
||||||
ftdiDevice.reset();
|
ftdi.reset();
|
||||||
ftdiDevice.set_baud_rate(500000);
|
ftdi.setBaudrate(500000);
|
||||||
ftdiDevice.flush();
|
ftdi.flush();
|
||||||
|
|
||||||
// Create threads
|
// Create threads
|
||||||
closing = false;
|
closing = false;
|
||||||
|
|
@ -97,25 +85,92 @@ bool FTDI::close() {
|
||||||
if(writeThread.joinable())
|
if(writeThread.joinable())
|
||||||
writeThread.join();
|
writeThread.join();
|
||||||
|
|
||||||
closing = false;
|
bool ret = ftdi.closeDevice();
|
||||||
|
|
||||||
bool ret = true;
|
|
||||||
if(ftdiDevice.close())
|
|
||||||
ret = false;
|
|
||||||
|
|
||||||
uint8_t flush;
|
uint8_t flush;
|
||||||
WriteOperation flushop;
|
WriteOperation flushop;
|
||||||
while(readQueue.try_dequeue(flush)) {}
|
while(readQueue.try_dequeue(flush)) {}
|
||||||
while(writeQueue.try_dequeue(flushop)) {}
|
while(writeQueue.try_dequeue(flushop)) {}
|
||||||
|
|
||||||
|
closing = false;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<int, std::vector<std::string>> FTDI::FTDIContext::findDevices(int pid) {
|
||||||
|
std::pair<int, std::vector<std::string>> 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() {
|
void FTDI::readTask() {
|
||||||
constexpr size_t READ_BUFFER_SIZE = 8;
|
constexpr size_t READ_BUFFER_SIZE = 8;
|
||||||
uint8_t readbuf[READ_BUFFER_SIZE];
|
uint8_t readbuf[READ_BUFFER_SIZE];
|
||||||
while(!closing) {
|
while(!closing) {
|
||||||
auto readBytes = ftdiDevice.read(readbuf, READ_BUFFER_SIZE);
|
auto readBytes = ftdi.read(readbuf, READ_BUFFER_SIZE);
|
||||||
if(readBytes > 0)
|
if(readBytes > 0)
|
||||||
readQueue.enqueue_bulk(readbuf, readBytes);
|
readQueue.enqueue_bulk(readbuf, readBytes);
|
||||||
}
|
}
|
||||||
|
|
@ -127,6 +182,6 @@ void FTDI::writeTask() {
|
||||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ftdiDevice.write(writeOp.bytes.data(), (int)writeOp.bytes.size());
|
ftdi.write(writeOp.bytes.data(), (int)writeOp.bytes.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue