now also downloads from serial/usbserial devices and automatically chooses

between them and D2XX based on what's installed
This commit is contained in:
Sean C. Rhea
2008-04-12 20:56:12 +00:00
parent b47ac76116
commit 2e21b6e328
10 changed files with 496 additions and 132 deletions

View File

@@ -19,42 +19,48 @@
#include "D2XX.h"
#include <ctype.h>
Device::Device(const FT_DEVICE_LIST_INFO_NODE &info) :
bool D2XXRegistered = Device::addListFunction(&D2XX::myListDevices);
D2XX::D2XX(const FT_DEVICE_LIST_INFO_NODE &info) :
info(info), isOpen(false)
{
}
Device::~Device()
D2XX::~D2XX()
{
if (isOpen)
close();
}
bool Device::open(QString &err)
bool
D2XX::open(QString &err)
{
assert(!isOpen);
FT_STATUS ftStatus =
FT_OpenEx(info.Description, FT_OPEN_BY_DESCRIPTION, &ftHandle);
if (ftStatus == FT_OK)
isOpen = true;
else
if (ftStatus != FT_OK) {
err = QString("FT_Open: %1").arg(ftStatus);
return false;
}
isOpen = true;
ftStatus = FT_SetBaudRate(ftHandle, 9600);
if (ftStatus != FT_OK) {
err = QString("FT_SetBaudRate: %1").arg(ftStatus);
close();
}
return isOpen;
return true;
}
void Device::close()
void
D2XX::close()
{
assert(isOpen);
FT_Close(ftHandle);
isOpen = false;
}
int Device::read(void *buf, size_t nbyte, QString &err)
int
D2XX::read(void *buf, size_t nbyte, QString &err)
{
assert(isOpen);
DWORD rxbytes;
@@ -77,7 +83,8 @@ int Device::read(void *buf, size_t nbyte, QString &err)
return -1;
}
int Device::write(void *buf, size_t nbyte, QString &err)
int
D2XX::write(void *buf, size_t nbyte, QString &err)
{
assert(isOpen);
DWORD n;
@@ -88,8 +95,14 @@ int Device::write(void *buf, size_t nbyte, QString &err)
return -1;
}
QString
D2XX::name() const
{
return QString("D2XX: ") + info.Description;
}
QVector<DevicePtr>
Device::listDevices(QString &err)
D2XX::myListDevices(QString &err)
{
QVector<DevicePtr> result;
DWORD numDevs;
@@ -104,10 +117,19 @@ Device::listDevices(QString &err)
err = QString("FT_GetDeviceInfoList: %1").arg(ftStatus);
else {
for (DWORD i = 0; i < numDevs; i++)
result.append(DevicePtr(new Device(devInfo[i])));
result.append(DevicePtr(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) {
DevicePtr dev = result[i];
QString tmp;
if (dev->open(tmp))
dev->close();
else
result.remove(i--);
}
return result;
}

View File

@@ -16,31 +16,33 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <QtCore>
#ifndef _GC_PT_D2XX_h
#define _GC_PT_D2XX_h 1
#include "Device.h"
#include <D2XX/ftd2xx.h>
#include <boost/shared_ptr.hpp>
class Device;
typedef boost::shared_ptr<Device> DevicePtr;
class Device
class D2XX : public Device
{
Device(const Device &);
Device& operator=(const Device &);
D2XX(const D2XX &);
D2XX& operator=(const D2XX &);
FT_DEVICE_LIST_INFO_NODE info;
FT_HANDLE ftHandle;
bool isOpen;
Device(const FT_DEVICE_LIST_INFO_NODE &info);
D2XX(const FT_DEVICE_LIST_INFO_NODE &info);
public:
static QVector<DevicePtr> listDevices(QString &err);
~Device();
bool open(QString &err);
void close();
int read(void *buf, size_t nbyte, QString &err);
int write(void *buf, size_t nbyte, QString &err);
static QVector<DevicePtr> myListDevices(QString &err);
virtual ~D2XX();
virtual bool open(QString &err);
virtual void close();
virtual int read(void *buf, size_t nbyte, QString &err);
virtual int write(void *buf, size_t nbyte, QString &err);
virtual QString name() const;
};
#endif // _GC_PT_D2XX_h

45
src/pt/Device.cpp Normal file
View File

@@ -0,0 +1,45 @@
/*
* 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 "Device.h"
#include "Serial.h"
QVector<Device::ListFunction> Device::listFunctions;
bool
Device::addListFunction(ListFunction f)
{
listFunctions.append(f);
return true;
}
QVector<DevicePtr>
Device::listDevices(QString &err)
{
err = "";
QVector<DevicePtr> result;
for (int i = 0; i < listFunctions.size(); ++i) {
QVector<DevicePtr> tmp = listFunctions[i](err);
if (err != "")
return result;
for (int j = 0; j < tmp.size(); ++j)
result.append(tmp[j]);
}
return result;
}

48
src/pt/Device.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* 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
*/
#ifndef _GC_PT_Device_h
#define _GC_PT_Device_h 1
#include <QtCore>
#include <boost/shared_ptr.hpp>
class Device;
typedef boost::shared_ptr<Device> DevicePtr;
class Device
{
typedef QVector<DevicePtr> (*ListFunction)(QString &err);
static QVector<ListFunction> listFunctions;
public:
static bool addListFunction(ListFunction f);
static QVector<DevicePtr> listDevices(QString &err);
virtual ~Device() {}
virtual bool open(QString &err) = 0;
virtual void close() = 0;
virtual int read(void *buf, size_t nbyte, QString &err) = 0;
virtual int write(void *buf, size_t nbyte, QString &err) = 0;
virtual QString name() const = 0;
};
#endif // _GC_PT_Device_h

View File

@@ -16,11 +16,9 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "D2XX.h"
#include "PowerTap.h"
#include "../lib/pt.h"
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
static bool
hasNewline(const char *buf, int len)
@@ -58,20 +56,30 @@ cEscape(const char *buf, int len)
return result;
}
static void
doWrite(DevicePtr dev, char c)
static bool
doWrite(DevicePtr dev, char c, bool hwecho, QString &err)
{
// printf("writing '%c' to device\n", c);
QString err;
int n = dev->write(&c, 1, err);
if (n != 1) {
if (n < 0)
printf("ERROR: failed to write %c to device: %s\n",
c, err.toAscii().constData());
err = QString("failed to write %1 to device: %2").arg(c).arg(err);
else
printf("ERROR: timeout writing %c to device\n", c);
exit(1);
err = QString("timeout writing %1 to device").arg(c);
return false;
}
if (hwecho) {
char c;
int n = dev->read(&c, 1, err);
if (n != 1) {
if (n < 0)
err = QString("failed to read back hardware echo: %2").arg(err);
else
err = "timeout reading back hardware echo";
return false;
}
}
return true;
}
static int
@@ -95,40 +103,42 @@ readUntilNewline(DevicePtr dev, char *buf, int len)
return sofar;
}
typedef void (*StatusCallback)(QString status);
static bool
download(DevicePtr dev, QString &version,
QVector<unsigned char> &records,
StatusCallback statusCallback, QString &err)
bool
PowerTap::download(DevicePtr dev, QByteArray &version,
QVector<unsigned char> &records,
StatusCallback statusCallback, QString &err)
{
if (!dev->open(err)) {
err = "ERROR: open failed: " + err;
return false;
}
/*
printf("clearing read buffer\n");
while (true) {
QString err;
char buf[256];
if (dev->read(buf, sizeof(buf), err) < (int) sizeof(buf))
break;
}
*/
doWrite(dev, 0x56); // 'V'
if (!doWrite(dev, 0x56, false, err)) // 'V'
return false;
statusCallback("Reading version string...");
char vbuf[256];
int version_len = readUntilNewline(dev, vbuf, sizeof(vbuf));
printf("read version \"%s\"\n",
cEscape(vbuf, version_len).toAscii().constData());
vbuf[version_len] = '\0';
version = vbuf;
version = QByteArray(vbuf, version_len);
// We expect the version string to be something like
// "VER 02.21 PRO...", so if we see two V's, it's probably
// because there's a hardware echo going on.
int veridx = version.indexOf("VER");
if (veridx < 0) {
err = QString("Unrecognized version \"%1\"")
.arg(cEscape(vbuf, version_len));
return false;
}
bool hwecho = version.indexOf('V') < veridx;
printf("hwecho=%s\n", hwecho ? "true" : "false");
statusCallback("Reading header...");
doWrite(dev, 0x44); // 'D'
if (!doWrite(dev, 0x44, hwecho, err)) // 'D'
return false;
unsigned char header[6];
int header_len = dev->read(header, sizeof(header), err);
if (header_len != 6) {
@@ -203,80 +213,10 @@ download(DevicePtr dev, QString &version,
int rem = (int) round(secs);
int min = rem / 60;
statusCallback(QString("%1 minutes downloaded...").arg(min));
doWrite(dev, 0x71); // 'q'
if (!doWrite(dev, 0x71, hwecho, err)) // 'q'
return false;
}
return true;
}
static void
statusCallback(QString status)
{
printf("STATUS: %s\n", status.toAscii().constData());
}
int
main()
{
QString err;
QVector<DevicePtr> devList = Device::listDevices(err);
if (devList.empty()) {
printf("ERROR: no devices\n");
exit(1);
}
if (devList.size() > 1) {
printf("ERROR: found %d devices\n", devList.size());
exit(1);
}
printf("Opening device\n");
DevicePtr dev = devList[0];
QString version;
QVector<unsigned char> records;
if (!download(dev, version, records, &statusCallback, err)) {
printf("%s\n", err.toAscii().constData());
exit(1);
}
char tmpname[32];
snprintf(tmpname, sizeof(tmpname), ".ptdl.XXXXXX");
int fd = mkstemp(tmpname);
if (fd == -1)
assert(false);
FILE *file = fdopen(fd, "w");
if (file == NULL)
assert(false);
unsigned char *data = records.data();
struct tm time;
bool time_set = false;
for (int i = 0; i < records.size(); i += 6) {
if (data[i] == 0)
continue;
fprintf(file, "%02x %02x %02x %02x %02x %02x\n",
data[i], data[i+1], data[i+2],
data[i+3], data[i+4], data[i+5]);
if (!time_set && pt_is_time(data + i)) {
pt_unpack_time(data + i, &time);
time_set = true;
}
}
fclose(file);
assert(time_set);
if (chmod(tmpname, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
perror("chmod");
assert(false);
}
char filename[64];
sprintf(filename, "%04d_%02d_%02d_%02d_%02d_%02d.raw",
time.tm_year + 1900, time.tm_mon + 1, time.tm_mday,
time.tm_hour, time.tm_min, time.tm_sec);
if (rename(tmpname, filename) == -1) {
perror("rename");
assert(false);
}
printf("Ride successfully downloaded to %s!\n", filename);
return 0;
}

34
src/pt/PowerTap.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* 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
*/
#ifndef _GC_PT_PowerTap_h
#define _GC_PT_PowerTap_h 1
#include "Device.h"
struct PowerTap
{
typedef void (*StatusCallback)(QString status);
static bool download(DevicePtr dev, QByteArray &version,
QVector<unsigned char> &records,
StatusCallback statusCallback, QString &err);
};
#endif // _GC_PT_PowerTap_h

127
src/pt/Serial.cpp Normal file
View File

@@ -0,0 +1,127 @@
/*
* 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 "Serial.h"
#include "../lib/pt.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
bool SerialRegistered = Device::addListFunction(&Serial::myListDevices);
Serial::Serial(const QString &path) : path(path), fd(-1)
{
}
Serial::~Serial()
{
if (fd >= 0)
close();
}
bool
Serial::open(QString &err)
{
assert(fd < 0);
fd = ::open(path.toAscii().constData(), O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0) {
err = QString("open: ") + strerror(errno);
return false;
}
pt_make_async(fd);
return true;
}
void
Serial::close()
{
assert(fd >= 0);
::close(fd);
fd = -1;
}
int
Serial::read(void *buf, size_t nbyte, QString &err)
{
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
struct timeval tv;
memset(&tv, 0, sizeof(tv));
tv.tv_sec = 5;
int n = select(fd + 1, &fdset, NULL, NULL, &tv);
if (n == 0)
return 0;
if (n < 0) {
err = QString("select: ") + strerror(errno);
return -1;
}
int result = ::read(fd, buf, nbyte);
if (result < 0)
err = QString("read: ") + strerror(errno);
return result;
}
int
Serial::write(void *buf, size_t nbyte, QString &err)
{
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
struct timeval tv;
memset(&tv, 0, sizeof(tv));
tv.tv_sec = 5;
int n = select(fd + 1, NULL, &fdset, NULL, &tv);
if (n < 0) {
err = QString("select: ") + strerror(errno);
return -1;
}
if (n == 0) {
err = "timeout";
return 0;
}
int result = ::write(fd, buf, nbyte);
if (result < 0)
err = QString("write: ") + strerror(errno);
return result;
}
QString
Serial::name() const
{
return QString("Serial: ") + path;
}
QVector<DevicePtr>
Serial::myListDevices(QString &err)
{
static const int MAX_DEVICES = 100;
(void) err;
QVector<DevicePtr> result;
char *devices[MAX_DEVICES];
int devcnt = pt_find_device(devices, MAX_DEVICES);
for (int i = 0; i < devcnt; ++i) {
result.append(DevicePtr(new Serial(devices[i])));
free(devices[i]);
}
return result;
}

46
src/pt/Serial.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* 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
*/
#ifndef _GC_PT_Serial_h
#define _GC_PT_Serial_h 1
#include "Device.h"
class Serial : public Device
{
Serial(const Serial &);
Serial& operator=(const Serial &);
QString path;
int fd;
Serial(const QString &path);
public:
static QVector<DevicePtr> myListDevices(QString &err);
virtual ~Serial();
virtual bool open(QString &err);
virtual void close();
virtual int read(void *buf, size_t nbyte, QString &err);
virtual int write(void *buf, size_t nbyte, QString &err);
virtual QString name() const;
};
#endif // _GC_PT_Serial_h

100
src/pt/main.cpp Normal file
View File

@@ -0,0 +1,100 @@
/*
* 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 "PowerTap.h"
#include "../lib/pt.h"
#include <sys/types.h>
#include <sys/stat.h>
static void
statusCallback(QString status)
{
printf("STATUS: %s\n", status.toAscii().constData());
}
int
main()
{
QString err;
QVector<DevicePtr> devList = Device::listDevices(err);
if (devList.empty()) {
printf("ERROR: no devices\n");
exit(1);
}
printf("Available devices:\n");
for (int i = 0; i < devList.size(); ++i) {
DevicePtr dev = devList[i];
printf(" %s\n", dev->name().toAscii().constData());
}
if (devList.size() > 1) {
printf("ERROR: found %d devices\n", devList.size());
exit(1);
}
DevicePtr dev = devList[0];
printf("Downloading from device %s\n", dev->name().toAscii().constData());
QByteArray version;
QVector<unsigned char> records;
if (!PowerTap::download(dev, version, records, &statusCallback, err)) {
printf("%s\n", err.toAscii().constData());
exit(1);
}
char tmpname[32];
snprintf(tmpname, sizeof(tmpname), ".ptdl.XXXXXX");
int fd = mkstemp(tmpname);
if (fd == -1)
assert(false);
FILE *file = fdopen(fd, "w");
if (file == NULL)
assert(false);
unsigned char *data = records.data();
struct tm time;
bool time_set = false;
for (int i = 0; i < records.size(); i += 6) {
if (data[i] == 0)
continue;
fprintf(file, "%02x %02x %02x %02x %02x %02x\n",
data[i], data[i+1], data[i+2],
data[i+3], data[i+4], data[i+5]);
if (!time_set && pt_is_time(data + i)) {
pt_unpack_time(data + i, &time);
time_set = true;
}
}
fclose(file);
assert(time_set);
if (chmod(tmpname, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
perror("chmod");
assert(false);
}
char filename[64];
sprintf(filename, "%04d_%02d_%02d_%02d_%02d_%02d.raw",
time.tm_year + 1900, time.tm_mon + 1, time.tm_mday,
time.tm_hour, time.tm_min, time.tm_sec);
if (rename(tmpname, filename) == -1) {
perror("rename");
assert(false);
}
printf("Ride successfully downloaded to %s!\n", filename);
return 0;
}

View File

@@ -9,6 +9,6 @@ INCLUDEPATH += /sw/include
LIBS += -lftd2xx ../lib/libgc.a
# Input
HEADERS += D2XX.h
SOURCES += D2XX.cpp PowerTap.cpp
HEADERS += Device.h Serial.h D2XX.h PowerTap.h
SOURCES += Device.cpp Serial.cpp D2XX.cpp PowerTap.cpp main.cpp