mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 00:28:42 +00:00
I hate to change so many lines of code just for a little rename, but I want to distinguish between "devices", like the PowerTap and SRM, and "communications ports", like the serial port and the native D2XX drivers. This work is in preparation for adding direct download support for the SRM.
232 lines
7.5 KiB
C++
232 lines
7.5 KiB
C++
/*
|
|
* Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "D2XX.h"
|
|
#include <dlfcn.h>
|
|
|
|
// D2XXWrapper is a wrapper around libftd2xx to make it amenable to loading
|
|
// with dlopen().
|
|
|
|
#define LOAD_SYM(type,var,name) \
|
|
var = (type*) dlsym(handle, name); \
|
|
if (!var) { \
|
|
error = QString("could not load symbol ") + name; \
|
|
return false; \
|
|
}
|
|
#ifdef WIN32
|
|
#define WIN32_STDCALL __stdcall
|
|
#else
|
|
#define WIN32_STDCALL
|
|
#endif
|
|
|
|
typedef FT_STATUS WIN32_STDCALL FP_OpenEx(PVOID pArg1, DWORD Flags, FT_HANDLE *pHandle);
|
|
typedef FT_STATUS WIN32_STDCALL FP_Close(FT_HANDLE ftHandle);
|
|
typedef FT_STATUS WIN32_STDCALL FP_SetBaudRate(FT_HANDLE ftHandle, ULONG BaudRate);
|
|
typedef FT_STATUS WIN32_STDCALL FP_SetDataCharacteristics(FT_HANDLE ftHandle, UCHAR WordLength, UCHAR StopBits, UCHAR Parity);
|
|
typedef FT_STATUS WIN32_STDCALL FP_SetFlowControl(FT_HANDLE ftHandle, USHORT FlowControl, UCHAR XonChar, UCHAR XoffChar);
|
|
typedef FT_STATUS WIN32_STDCALL FP_GetQueueStatus(FT_HANDLE ftHandle, DWORD *dwRxBytes);
|
|
typedef FT_STATUS WIN32_STDCALL FP_SetTimeouts(FT_HANDLE ftHandle, ULONG ReadTimeout, ULONG WriteTimeout);
|
|
typedef FT_STATUS WIN32_STDCALL FP_Read(FT_HANDLE ftHandle, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesReturned);
|
|
typedef FT_STATUS WIN32_STDCALL FP_Write(FT_HANDLE ftHandle, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesWritten);
|
|
typedef FT_STATUS WIN32_STDCALL FP_CreateDeviceInfoList(LPDWORD lpdwNumDevs);
|
|
typedef FT_STATUS WIN32_STDCALL FP_GetDeviceInfoList(FT_DEVICE_LIST_INFO_NODE *pDest, LPDWORD lpdwNumDevs);
|
|
|
|
struct D2XXWrapper {
|
|
void *handle;
|
|
FP_OpenEx *open_ex;
|
|
FP_Close *close;
|
|
FP_SetBaudRate *set_baud_rate;
|
|
FP_SetDataCharacteristics *set_data_characteristics;
|
|
FP_SetFlowControl *set_flow_control;
|
|
FP_GetQueueStatus *get_queue_status;
|
|
FP_SetTimeouts *set_timeouts;
|
|
FP_Read *read;
|
|
FP_Write *write;
|
|
FP_CreateDeviceInfoList *create_device_info_list;
|
|
FP_GetDeviceInfoList *get_device_info_list;
|
|
D2XXWrapper() : handle(NULL) {}
|
|
~D2XXWrapper() { if (handle) dlclose(handle); }
|
|
bool init(QString &error)
|
|
{
|
|
#if defined(Q_OS_LINUX)
|
|
const char *libname = "libftd2xx.so";
|
|
#elif defined(Q_OS_WIN32)
|
|
const char *libname = "ftd2xx.dll";
|
|
#elif defined(Q_OS_DARWIN)
|
|
const char *libname = "libftd2xx.dylib";
|
|
#endif
|
|
handle = dlopen(libname, RTLD_NOW);
|
|
if (!handle) {
|
|
error = QString("Couldn't load library ") + libname + ".";
|
|
return false;
|
|
}
|
|
LOAD_SYM(FP_OpenEx, open_ex, "FT_OpenEx");
|
|
LOAD_SYM(FP_Close, close, "FT_Close");
|
|
LOAD_SYM(FP_SetBaudRate, set_baud_rate, "FT_SetBaudRate");
|
|
LOAD_SYM(FP_SetDataCharacteristics, set_data_characteristics, "FT_SetDataCharacteristics");
|
|
LOAD_SYM(FP_SetFlowControl, set_flow_control, "FT_SetFlowControl");
|
|
LOAD_SYM(FP_GetQueueStatus, get_queue_status, "FT_GetQueueStatus");
|
|
LOAD_SYM(FP_SetTimeouts, set_timeouts, "FT_SetTimeouts");
|
|
LOAD_SYM(FP_Read, read, "FT_Read");
|
|
LOAD_SYM(FP_Write, write, "FT_Write");
|
|
LOAD_SYM(FP_CreateDeviceInfoList, create_device_info_list, "FT_CreateDeviceInfoList");
|
|
LOAD_SYM(FP_GetDeviceInfoList, get_device_info_list, "FT_GetDeviceInfoList");
|
|
return true;
|
|
}
|
|
};
|
|
|
|
static D2XXWrapper *lib; // singleton lib instance
|
|
|
|
bool D2XXRegistered = CommPort::addListFunction(&D2XX::myListCommPorts);
|
|
|
|
D2XX::D2XX(const FT_DEVICE_LIST_INFO_NODE &info) :
|
|
info(info), isOpen(false)
|
|
{
|
|
}
|
|
|
|
D2XX::~D2XX()
|
|
{
|
|
if (isOpen)
|
|
close();
|
|
}
|
|
|
|
bool
|
|
D2XX::open(QString &err)
|
|
{
|
|
assert(!isOpen);
|
|
FT_STATUS ftStatus =
|
|
lib->open_ex(info.Description, FT_OPEN_BY_DESCRIPTION, &ftHandle);
|
|
if (ftStatus != FT_OK) {
|
|
err = QString("FT_Open: %1").arg(ftStatus);
|
|
return false;
|
|
}
|
|
isOpen = true;
|
|
ftStatus = lib->set_baud_rate(ftHandle, 9600);
|
|
if (ftStatus != FT_OK) {
|
|
err = QString("FT_SetBaudRate: %1").arg(ftStatus);
|
|
close();
|
|
}
|
|
|
|
ftStatus = lib->set_data_characteristics(ftHandle,FT_BITS_8,FT_STOP_BITS_1,
|
|
FT_PARITY_NONE);
|
|
if (ftStatus != FT_OK) {
|
|
err = QString("FT_SetDataCharacteristics: %1").arg(ftStatus);
|
|
close();
|
|
}
|
|
|
|
ftStatus = lib->set_flow_control(ftHandle,FT_FLOW_NONE,
|
|
'0','0'); //the 0's are ignored
|
|
if (ftStatus != FT_OK) {
|
|
err = QString("FT_SetFlowControl: %1").arg(ftStatus);
|
|
close();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
D2XX::close()
|
|
{
|
|
assert(isOpen);
|
|
lib->close(ftHandle);
|
|
isOpen = false;
|
|
}
|
|
|
|
int
|
|
D2XX::read(void *buf, size_t nbyte, QString &err)
|
|
{
|
|
assert(isOpen);
|
|
DWORD rxbytes;
|
|
FT_STATUS ftStatus = lib->get_queue_status(ftHandle, &rxbytes);
|
|
if (ftStatus != FT_OK) {
|
|
err = QString("FT_GetQueueStatus: %1").arg(ftStatus);
|
|
return -1;
|
|
}
|
|
// printf("rxbytes=%d\n", (int) rxbytes);
|
|
// Return immediately whenever there's something to read.
|
|
if (rxbytes > 0 && rxbytes < nbyte)
|
|
nbyte = rxbytes;
|
|
if (nbyte > rxbytes)
|
|
lib->set_timeouts(ftHandle, 5000, 5000);
|
|
DWORD n;
|
|
ftStatus = lib->read(ftHandle, buf, nbyte, &n);
|
|
if (ftStatus == FT_OK)
|
|
return n;
|
|
err = QString("FT_Read: %1").arg(ftStatus);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
D2XX::write(void *buf, size_t nbyte, QString &err)
|
|
{
|
|
assert(isOpen);
|
|
DWORD n;
|
|
FT_STATUS ftStatus = lib->write(ftHandle, buf, nbyte, &n);
|
|
if (ftStatus == FT_OK)
|
|
return n;
|
|
err = QString("FT_Write: %1").arg(ftStatus);
|
|
return -1;
|
|
}
|
|
|
|
QString
|
|
D2XX::name() const
|
|
{
|
|
return QString("D2XX: ") + info.Description;
|
|
}
|
|
|
|
QVector<CommPortPtr>
|
|
D2XX::myListCommPorts(QString &err)
|
|
{
|
|
QVector<CommPortPtr> result;
|
|
if (!lib) {
|
|
lib = new D2XXWrapper;
|
|
if (!lib->init(err)) {
|
|
delete lib;
|
|
lib = NULL;
|
|
return result;
|
|
}
|
|
}
|
|
DWORD numDevs;
|
|
FT_STATUS ftStatus = lib->create_device_info_list(&numDevs);
|
|
if(ftStatus != FT_OK) {
|
|
err = QString("FT_CreateDeviceInfoList: %1").arg(ftStatus);
|
|
return result;
|
|
}
|
|
FT_DEVICE_LIST_INFO_NODE *devInfo = new FT_DEVICE_LIST_INFO_NODE[numDevs];
|
|
ftStatus = lib->get_device_info_list(devInfo, &numDevs);
|
|
if (ftStatus != FT_OK)
|
|
err = QString("FT_GetDeviceInfoList: %1").arg(ftStatus);
|
|
else {
|
|
for (DWORD i = 0; i < numDevs; i++)
|
|
result.append(CommPortPtr(new D2XX(devInfo[i])));
|
|
}
|
|
delete [] devInfo;
|
|
// If we can't open a D2XX device, it's usually because the VCP drivers
|
|
// are installed, so it should also show up in the list of serial devices.
|
|
for (int i = 0; i < result.size(); ++i) {
|
|
CommPortPtr dev = result[i];
|
|
QString tmp;
|
|
if (dev->open(tmp))
|
|
dev->close();
|
|
else
|
|
result.remove(i--);
|
|
}
|
|
return result;
|
|
}
|
|
|