Native ANT+ part 2 - USB2 Support and minor improvements

This patch adds support for the Garmin USB2 stick using
libusb-win library. Instructions are included in gcconfig.pri.in
for configuring and installing the neccessary libs.

To enable support for USB1 and USB2 support in the same binary
stubs are created when UsbXpress/Libusb are not available and the
device i/o attempts to use USB2 before falling back to USB1.

Since I was also in the middle of some coding changes I merged
my developments (Mark) with Darren's patch whilst fixing it up
for commit, namely:

1. the configuration screen no longer demands a COMx port
   when using Native ANT+ on Windows.
2.  new signals in ANTChannel notify the ANT class when info is
   stale or lost (but they are not used at present).
3. The previous debug messages have been removed, although new
   debug messages are added for stale/drop/timeout signals.
This commit is contained in:
Darren Hague
2011-03-19 20:50:42 +00:00
committed by Mark Liversedge
parent 22d04f8524
commit f8d60bfbe2
14 changed files with 563 additions and 209 deletions

View File

@@ -90,11 +90,32 @@ ANT::ANT(QObject *parent, DeviceConfiguration *devConf) : QThread(parent)
antIDs.clear();
// setup the channels
for (int i=0; i<ANT_MAX_CHANNELS; i++) antChannel[i] = new ANTChannel(i, this);
for (int i=0; i<ANT_MAX_CHANNELS; i++) {
// create the channel
antChannel[i] = new ANTChannel(i, this);
// connect up its signals
connect(antChannel[i], SIGNAL(dropInfo(int)), this, SLOT(dropInfo(int)));
connect(antChannel[i], SIGNAL(lostInfo(int)), this, SLOT(lostInfo(int)));
connect(antChannel[i], SIGNAL(staleInfo(int)), this, SLOT(staleInfo(int)));
connect(antChannel[i], SIGNAL(searchTimeout(int)), this, SLOT(searchTimeout(int)));
connect(antChannel[i], SIGNAL(searchComplete(int)), this, SLOT(searchComplete(int)));
}
// on windows we use libusb to read from USB2
// sticks, if it is not available we use stubs
#ifdef WIN32
usbMode = USBNone;
usb2 = new LibUsb();
#endif
}
ANT::~ANT()
{
#ifdef WIN32
delete usb2;
#endif
}
void ANT::setDevice(QString x)
@@ -114,7 +135,6 @@ void ANT::setBaud(int x)
void ANT::run()
{
//qDebug() << "Starting ANT thread...";
int status; // control commands from controller
bool isPortOpen = false;
@@ -254,7 +274,6 @@ ANT::stop()
int
ANT::quit(int code)
{
//qDebug()<<"Stopping ANT thread...";
// event code goes here!
closePort();
exit(code);
@@ -287,7 +306,7 @@ ANT::addDevice(int device_number, int device_type, int channel_number)
if (((antChannel[i]->channel_type & 0xf ) == device_type) &&
(antChannel[i]->device_number == device_number)) {
// send the channel found...
antChannel[i]->channelInfo();
//XXX antChannel[i]->channelInfo();
return 1;
}
}
@@ -375,7 +394,8 @@ void
ANT::report()
{
for (int i=0; i<ANT_MAX_CHANNELS; i++)
antChannel[i]->channelInfo();
//XXX antChannel[i]->channelInfo();
;
}
void
@@ -440,6 +460,36 @@ ANT::discover(DeviceConfiguration *, QProgressDialog *)
return false;
}
void
ANT::dropInfo(int number) // we dropped a connection
{
qDebug()<<"drop info for channel"<<number;
}
void
ANT::lostInfo(int number) // we lost informa
{
qDebug()<<"lost info for channel"<<number;
}
void
ANT::staleInfo(int number) // info is now stale
{
qDebug()<<"stale info for channel"<<number;
}
void
ANT::searchTimeout(int number) // search timed out
{
qDebug()<<"search timeout on channel"<<number;
}
void
ANT::searchComplete(int number) // search completed successfully
{
qDebug()<<"search completed on channel"<<number;
}
/*----------------------------------------------------------------------
* Message I/O
*--------------------------------------------------------------------*/
@@ -509,9 +559,7 @@ ANT::handleChannelEvent(void) {
int channel = rxMessage[ANT_OFFSET_DATA] & 0x7;
if(channel >= 0 && channel < 4) {
// handle a channel event here!
//qDebug()<<"channel event on channel:"<<channel;
// handle a channel event here!
antChannel[channel]->receiveMessage(rxMessage);
}
}
@@ -519,7 +567,6 @@ ANT::handleChannelEvent(void) {
void
ANT::processMessage(void) {
//qDebug()<<"processing ant message"<<rxMessage[ANT_OFFSET_ID];
ANTMessage(this, rxMessage); // for debug!
QDataStream out(&antlog);
@@ -569,7 +616,18 @@ ANT::processMessage(void) {
int ANT::closePort()
{
#ifdef WIN32
return (int)!CloseHandle(devicePort);
switch (usbMode) {
case USB2 :
usb2->close();
return 0;
break;
case USB1 :
return (int)!CloseHandle(devicePort);
break;
default :
return -1;
break;
}
#else
tcflush(devicePort, TCIOFLUSH); // clear out the garbage
return close(devicePort);
@@ -578,8 +636,22 @@ int ANT::closePort()
int ANT::openPort()
{
#ifndef WIN32
#ifdef WIN32
int rc;
// on windows we try on USB2 then on USB1 then fail...
if ((rc=usb2->open()) != -1) {
usbMode = USB2;
return rc;
} else if ((rc= USBXpress::open(&devicePort)) != -1) {
usbMode = USB1;
return rc;
} else {
usbMode = USBNone;
return -1;
}
#else
// LINUX AND MAC USES TERMIO / IOCTL / STDIO
#if defined(Q_OS_MACX)
@@ -620,59 +692,6 @@ int ANT::openPort()
if(tcsetattr(devicePort, TCSANOW, &deviceSettings) == -1) return errno;
tcgetattr(devicePort, &deviceSettings);
#else
#ifdef GC_HAVE_USBXPRESS
return USBXpress::open(&devicePort);
#else
// WINDOWS USES SET/GETCOMMSTATE AND READ/WRITEFILE
COMMTIMEOUTS timeouts; // timeout settings on serial ports
// if deviceFilename references a port above COM9
// then we need to open "\\.\COMX" not "COMX"
QString portSpec;
int portnum = deviceFilename.midRef(3).toString().toInt();
if (portnum < 10)
portSpec = deviceFilename;
else
portSpec = "\\\\.\\" + deviceFilename;
wchar_t deviceFilenameW[32]; // \\.\COM32 needs 9 characters, 32 should be enough?
MultiByteToWideChar(CP_ACP, 0, portSpec.toAscii(), -1, (LPWSTR)deviceFilenameW,
sizeof(deviceFilenameW));
// win32 commport API
devicePort = CreateFile (deviceFilenameW, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_DELETE|FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (devicePort == INVALID_HANDLE_VALUE) return -1;
if (GetCommState (devicePort, &deviceSettings) == false) return -1;
// so we've opened the comm port lets set it up for
deviceSettings.BaudRate = CBR_2400;
deviceSettings.fParity = NOPARITY;
deviceSettings.ByteSize = 8;
deviceSettings.StopBits = ONESTOPBIT;
deviceSettings.EofChar = 0x0;
deviceSettings.ErrorChar = 0x0;
deviceSettings.EvtChar = 0x0;
deviceSettings.fBinary = true;
deviceSettings.fRtsControl = RTS_CONTROL_HANDSHAKE;
deviceSettings.fOutxCtsFlow = TRUE;
if (SetCommState(devicePort, &deviceSettings) == false) {
CloseHandle(devicePort);
return -1;
}
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutConstant = 1000;
timeouts.ReadTotalTimeoutMultiplier = 50;
timeouts.WriteTotalTimeoutConstant = 2000;
timeouts.WriteTotalTimeoutMultiplier = 0;
SetCommTimeouts(devicePort, &timeouts);
#endif
#endif
// success
@@ -684,13 +703,19 @@ int ANT::rawWrite(uint8_t *bytes, int size) // unix!!
int rc=0;
#ifdef WIN32
#ifdef GC_HAVE_USBXPRESS
rc = USBXpress::write(&devicePort, bytes, size);
#else
DWORD cBytes;
rc = WriteFile(devicePort, bytes, size, &cBytes, NULL);
#endif
if (!rc) return -1;
switch (usbMode) {
case USB1:
rc = USBXpress::write(&devicePort, bytes, size);
break;
case USB2:
rc = usb2->write((char *)bytes, size);
break;
default:
rc = 0;
break;
}
if (!rc) rc = -1; // return -1 if nothing written
return rc;
#else
@@ -704,9 +729,9 @@ int ANT::rawWrite(uint8_t *bytes, int size) // unix!!
if (rc != -1) tcdrain(devicePort); // wait till its gone.
ioctl(devicePort, FIONREAD, &ibytes);
return rc;
#endif
return rc;
}
@@ -715,16 +740,18 @@ int ANT::rawRead(uint8_t bytes[], int size)
int rc=0;
#ifdef WIN32
#ifdef GC_HAVE_USBXPRESS
return USBXpress::read(&devicePort, bytes, size);
#else
switch (usbMode) {
case USB1:
return USBXpress::read(&devicePort, bytes, size);
break;
case USB2:
return usb2->read((char *)bytes, size);
break;
default:
rc = 0;
break;
}
// Readfile deals with timeouts and readyread issues
DWORD cBytes;
rc = ReadFile(devicePort, bytes, size, &cBytes, NULL);
if (rc) return (int)cBytes;
else return (-1);
#endif
#else
int timeout=0, i=0;

View File

@@ -58,6 +58,7 @@
#include <windows.h>
#include <winbase.h>
#include "USBXpress.h" // for Garmin USB1 sticks
#include "LibUsb.h" // for Garmin USB2 sticks
#else
#include <termios.h> // unix!!
#include <unistd.h> // unix!!
@@ -240,6 +241,11 @@ public slots:
// channel management
bool discover(DeviceConfiguration *, QProgressDialog *); // confirm Server available at portSpec
void dropInfo(int number); // we dropped a connection
void lostInfo(int number); // we lost informa
void staleInfo(int number); // info is now stale
void searchTimeout(int number); // search timed out
void searchComplete(int number); // search completed successfully
// get telemetry
RealtimeData getRealtimeData(); // return current realtime data
@@ -310,6 +316,9 @@ private:
#ifdef WIN32
HANDLE devicePort; // file descriptor for reading from com3
DCB deviceSettings; // serial port settings baud rate et al
LibUsb *usb2; // used for USB2 support
enum UsbMode { USBNone, USB1, USB2 };
enum UsbMode usbMode;
#else
int devicePort; // unix!!
struct termios deviceSettings; // unix!!
@@ -324,8 +333,8 @@ private:
unsigned char rxMessage[ANT_MAX_MESSAGE_SIZE];
// state machine whilst receiving bytes
enum States {ST_WAIT_FOR_SYNC, ST_GET_LENGTH, ST_GET_MESSAGE_ID, ST_GET_DATA, ST_VALIDATE_PACKET};
enum States state;
enum States {ST_WAIT_FOR_SYNC, ST_GET_LENGTH, ST_GET_MESSAGE_ID, ST_GET_DATA, ST_VALIDATE_PACKET} state;
//enum States state;
int length;
int bytes;
int checksum;

View File

@@ -25,7 +25,7 @@ static float timeout_drop=2.0; // time before reporting dropped message
static float timeout_scan=10.0; // time to do initial scan
static float timeout_lost=30.0; // time to do more thorough scan
ANTChannel::ANTChannel(int number, ANT *parent) : number(number), parent(parent)
ANTChannel::ANTChannel(int number, ANT *parent) : parent(parent), number(number)
{
channel_type=CHANNEL_TYPE_UNUSED;
channel_type_flags=0;
@@ -97,7 +97,7 @@ void ANTChannel::open(int device, int chan_type)
// close an ant channel assignment
void ANTChannel::close()
{
lostInfo();
emit lostInfo(number);
lastMessage = ANTMessage();
parent->sendMessage(ANTMessage::close(number));
}
@@ -133,7 +133,7 @@ void ANTChannel::receiveMessage(unsigned char *ant_message)
if (get_timestamp() > blanking_timestamp + timeout_blanking) {
if (!blanked) {
blanked=1;
staleInfo(); //XXX does nothing for now....
emit staleInfo(number);
}
} else blanked=0;
}
@@ -159,12 +159,14 @@ void ANTChannel::channelEvent(unsigned char *ant_message) {
// timeouts are normal for search channel
if (channel_type_flags & CHANNEL_TYPE_QUICK_SEARCH) {
channel_type_flags &= ~CHANNEL_TYPE_QUICK_SEARCH;
channel_type_flags |= CHANNEL_TYPE_WAITING;
channel_type_flags &= ~CHANNEL_TYPE_QUICK_SEARCH;
channel_type_flags |= CHANNEL_TYPE_WAITING;
emit searchTimeout(number);
} else {
lostInfo(); //XXX does nothing for now
emit lostInfo(number);
channel_type=CHANNEL_TYPE_UNUSED;
channel_type_flags=0;
@@ -182,7 +184,7 @@ void ANTChannel::channelEvent(unsigned char *ant_message) {
double t=get_timestamp();
if (t > (last_message_timestamp + timeout_drop)) {
if (channel_type != CHANNEL_TYPE_UNUSED) dropInfo();
if (channel_type != CHANNEL_TYPE_UNUSED) emit dropInfo(number);
// this is a hacky way to prevent the drop message from sending multiple times
last_message_timestamp+=2*timeout_drop;
}
@@ -248,7 +250,6 @@ void ANTChannel::broadcastEvent(unsigned char *ant_message)
last_message_timestamp=timestamp;
if (state != MESSAGE_RECEIVED) {
qDebug()<<"who are you? sent";
// first message! who are we talking to?
parent->sendMessage(ANTMessage::requestMessage(number, ANT_CHANNEL_ID));
blanking_timestamp=get_timestamp();
@@ -278,7 +279,6 @@ qDebug()<<"who are you? sent";
//
// We got some telemetry on this channel
//
qDebug()<<"broadcast datapage="<<antMessage.data_page;
if (lastMessage.type != 0) {
switch (channel_type) {
@@ -315,7 +315,6 @@ qDebug()<<"broadcast datapage="<<antMessage.data_page;
srm_offset = antMessage.srmOffset;
parent->setWatts(0);
parent->setCadence(0);
qDebug()<<"got new offset!"<<srm_offset;
break;
case 0x02: // slope
@@ -563,9 +562,9 @@ void ANTChannel::channelId(unsigned char *ant_message) {
state=MESSAGE_RECEIVED;
setId();
channelInfo();
emit channelInfo();
// if we were searching,
// if we were searching,
if (channel_type_flags & CHANNEL_TYPE_QUICK_SEARCH) {
parent->sendMessage(ANTMessage::setSearchTimeout(number, (int)(timeout_lost/2.5)));
}
@@ -634,7 +633,6 @@ void ANTChannel::attemptTransition(int message_id)
switch (state) {
case ANT_CLOSE_CHANNEL:
//qDebug()<<"transition ... close channel...";
// next step is unassign and start over
// but we must wait until event_channel_closed
// which is its own channel event
@@ -642,7 +640,6 @@ void ANTChannel::attemptTransition(int message_id)
break;
case ANT_UNASSIGN_CHANNEL:
//qDebug()<<"transition ... unassign channel...";
channel_assigned=0;
if (st->type==CHANNEL_TYPE_UNUSED) {
// we're shutting the channel down
@@ -652,23 +649,21 @@ void ANTChannel::attemptTransition(int message_id)
device_id |= 0x80;
}
setId();
//qDebug()<<"st network is"<<st->network;
parent->sendMessage(ANTMessage::assignChannel(number, 0, st->network)); // recieve channel on network 1
// XXX commented out since newer host controllers do not exhibit this issue
// XXX but may be relevant for Arduino/Sparkfun guys, but we don't really support
// XXX those devices anyway as they are too slow
// XXX parent->sendMessage(ANTMessage::boostSignal(number)); // "boost" signal on REV C AP2 devices
// commented out since newer host controllers do not exhibit this issue
// but may be relevant for Arduino/Sparkfun guys, but we don't really support
// those devices anyway as they are too slow
// parent->sendMessage(ANTMessage::boostSignal(number)); // "boost" signal on REV C AP2 devices
}
break;
case ANT_ASSIGN_CHANNEL:
//qDebug()<<"transition ... assign channel...";
channel_assigned=1;
parent->sendMessage(ANTMessage::setChannelID(number, device_number, device_id, 0));
break;
case ANT_CHANNEL_ID:
//qDebug()<<"transition ... channel id...";
if (channel_type & CHANNEL_TYPE_QUICK_SEARCH) {
parent->sendMessage(ANTMessage::setSearchTimeout(number, (int)(timeout_scan/2.5)));
} else {
@@ -677,7 +672,6 @@ void ANTChannel::attemptTransition(int message_id)
break;
case ANT_SEARCH_TIMEOUT:
//qDebug()<<"transition ... search timeout...";
if (previous_state==ANT_CHANNEL_ID) {
// continue down the intialization chain
parent->sendMessage(ANTMessage::setChannelPeriod(number, st->period));
@@ -689,18 +683,15 @@ void ANTChannel::attemptTransition(int message_id)
break;
case ANT_CHANNEL_PERIOD:
//qDebug()<<"transition ... channel period...";
parent->sendMessage(ANTMessage::setChannelFreq(number, st->frequency));
break;
case ANT_CHANNEL_FREQUENCY:
//qDebug()<<"transition ... channel frequency...";
parent->sendMessage(ANTMessage::open(number));
mi.initialise();
break;
case ANT_OPEN_CHANNEL:
//qDebug()<<"transition ... open channel...";
break;
default:
@@ -711,14 +702,16 @@ void ANTChannel::attemptTransition(int message_id)
// refactored out XXX fix this
int ANTChannel::setTimeout(char * /*type*/, float /*value*/, int /*connection*/) { return 0; }
#if 0 // ARE NOW SIGNALS
// These should emit signals to notify the channel manager
// but for now we just ignore XXX fix this
void ANTChannel::searchComplete() {}
void ANTChannel::reportTimeouts() {}
void ANTChannel::searchTimeout() {}
void ANTChannel::staleInfo() {}
void ANTChannel::lostInfo() {}
void ANTChannel::dropInfo() {}
void ANTChannel::channelInfo() {}
#endif
//
// Calibrate... XXX not used at present

View File

@@ -22,6 +22,7 @@
#include "ANT.h"
#include "ANTMessage.h"
#include <QObject>
#define CHANNEL_TYPE_QUICK_SEARCH 0x10 // or'ed with current channel type
/* after fast search, wait for slow search. Otherwise, starting slow
@@ -71,9 +72,39 @@ class ANTChannelInitialisation {
};
class ANTChannel {
class ANTChannel : public QObject {
private:
Q_OBJECT
ANT *parent;
ANTMessage lastMessage, lastStdPwrMessage;
int dualNullCount, nullCount;
double last_message_timestamp;
double blanking_timestamp;
int blanked;
char id[10]; // short identifier
bool channel_assigned;
ANTChannelInitialisation mi;
int messages_received; // for signal strength metric
int messages_dropped;
unsigned char rx_burst_data[RX_BURST_DATA_LEN];
int rx_burst_data_index;
unsigned char rx_burst_next_sequence;
void (*rx_burst_disposition)(struct ant_channel *);
void (*tx_ack_disposition)(struct ant_channel *);
// what we got
int manufacturer_id;
int product_id;
int product_version;
public:
enum channeltype {
CHANNEL_TYPE_UNUSED,
CHANNEL_TYPE_HR,
@@ -124,14 +155,6 @@ class ANTChannel {
void attemptTransition(int message_code);
int setTimeout(char *type, float value, int connection);
// Should be signals ? XXX
void channelInfo();
void dropInfo();
void lostInfo();
void staleInfo();
void reportTimeouts();
void searchComplete();
// search
int isSearching();
@@ -140,33 +163,13 @@ class ANTChannel {
void sendCinqoSuccess();
void checkCinqo();
private:
ANT *parent;
ANTMessage lastMessage, lastStdPwrMessage;
int dualNullCount, nullCount;
double last_message_timestamp;
double blanking_timestamp;
int blanked;
char id[10]; // short identifier
bool channel_assigned;
ANTChannelInitialisation mi;
int messages_received; // for signal strength metric
int messages_dropped;
unsigned char rx_burst_data[RX_BURST_DATA_LEN];
int rx_burst_data_index;
unsigned char rx_burst_next_sequence;
void (*rx_burst_disposition)(struct ant_channel *);
void (*tx_ack_disposition)(struct ant_channel *);
// what we got
int manufacturer_id;
int product_id;
int product_version;
signals:
void channelInfo(); // we got a channel info message
void dropInfo(int number); // we dropped a connection
void lostInfo(int number); // we lost informa
void staleInfo(int number); // info is now stale
void searchTimeout(int number); // search timed out
void searchComplete(int number); // search completed successfully
};
#endif

View File

@@ -185,9 +185,9 @@ ANTMessage::ANTMessage(void) {
init();
}
// static helper to conver message codes to an english string
// when outputing diagnostics for received messages
static const char *channelEventMessage(unsigned char c)
// convert message codes to an english string
const char *
ANTMessage::channelEventMessage(unsigned char c)
{
switch (c) {
case 0 : return "No error";
@@ -237,35 +237,29 @@ ANTMessage::ANTMessage(ANT *parent, const unsigned char *message) {
switch(type) {
case ANT_UNASSIGN_CHANNEL:
channel = message[3];
qDebug()<<"unassign channel"<<channel;
break;
case ANT_ASSIGN_CHANNEL:
channel = message[3];
channelType = message[4];
networkNumber = message[5];
qDebug()<<"assign channel"<<channel;
break;
case ANT_CHANNEL_ID:
channel = message[3];
deviceNumber = message[4] + (message[5]<<8);
deviceType = message[6];
transmissionType = message[7];
qDebug()<<"assign channel id"<<deviceNumber;
break;
case ANT_CHANNEL_PERIOD:
channel = message[3];
channelPeriod = message[4] + (message[5]<<8);
qDebug()<<"channel period"<<channel<<channelPeriod;
break;
case ANT_SEARCH_TIMEOUT:
channel = message[3];
searchTimeout = message[4];
qDebug()<<"search timeout"<<channel<<searchTimeout;
break;
case ANT_CHANNEL_FREQUENCY:
channel = message[3];
frequency = message[4];
qDebug()<<"channel frequency"<<channel<<frequency;
break;
case ANT_SET_NETWORK:
channel = message[3];
@@ -280,44 +274,31 @@ qDebug()<<"channel frequency"<<channel<<frequency;
break;
case ANT_TX_POWER:
transmitPower = message[4];
qDebug()<<"transmit power"<<transmitPower;
break;
case ANT_ID_LIST_ADD:
qDebug()<<"ant id list add ";
break;
case ANT_ID_LIST_CONFIG:
qDebug()<<"ant list config ";
break;
case ANT_CHANNEL_TX_POWER:
qDebug()<<"ant channel txpower ";
break;
case ANT_LP_SEARCH_TIMEOUT:
qDebug()<<"ant lp search timeout ";
break;
case ANT_SET_SERIAL_NUMBER:
qDebug()<<"serial number";
break;
case ANT_ENABLE_EXT_MSGS:
qDebug()<<"enable extended messages";
break;
case ANT_ENABLE_LED:
qDebug()<<"enable led";
break;
case ANT_SYSTEM_RESET:
qDebug()<<"system reset";
break; // nothing to do, this is ok
case ANT_OPEN_CHANNEL:
channel = message[3];
qDebug()<<"open channel"<<channel;
break;
case ANT_CLOSE_CHANNEL:
qDebug()<<"close channel";
break;
case ANT_OPEN_RX_SCAN_CH:
qDebug()<<"open rx scan channel";
break;
case ANT_REQ_MESSAGE:
qDebug()<<"request message";
break;
//
@@ -372,8 +353,6 @@ qDebug()<<"request message";
// 0x50 - Manufacturer UD
// 0x52 - Battery Voltage
qDebug()<<"broadcast data, channel="<<message[3]<<"type="<<message[4]<<"calid?"<<message[5];
data_page = message[4];
// we need to handle ant sport messages here
@@ -518,35 +497,24 @@ qDebug()<<"broadcast data, channel="<<message[3]<<"type="<<message[4]<<"calid?"<
}
break;
case ANT_ACK_DATA:
qDebug()<<"ack data";
break;
case ANT_BURST_DATA:
qDebug()<<"burst data";
break;
case ANT_CHANNEL_EVENT:
qDebug()<<"channel event" << "ID"<<message[4] << channelEventMessage(message[5]);
break;
case ANT_CHANNEL_STATUS:
qDebug()<<"channel status";
break;
case ANT_VERSION:
qDebug()<<"ant version";
break;
case ANT_CAPABILITIES:
qDebug()<<"ant capabilities";
break;
case ANT_SERIAL_NUMBER:
qDebug()<<"ant serial number";
break;
case ANT_CW_INIT:
qDebug()<<"cw init";
break;
case ANT_CW_TEST:
qDebug()<<"cw test";
break;
default:
qDebug()<<"message id>>"<< type;
break; // shouldn't get here!
}
}

View File

@@ -68,6 +68,9 @@ class ANTMessage {
static ANTMessage open(const unsigned char channel);
static ANTMessage close(const unsigned char channel);
// convert a channel event message id to human readable string
const char * channelEventMessage(unsigned char c);
// to avoid a myriad of tedious set/getters the data fields
// are plain public members. This is unlikely to change in the
// future since the whole purpose of this class is to encode

View File

@@ -28,11 +28,15 @@
static DeviceType SupportedDevices[] =
{
{ DEV_CT, DEV_SERIAL, (char *) "Computrainer", true, false },
{ DEV_ANTPLUS, DEV_QUARQ, (char *) "ANT+ via Quarqd", true, false },
#ifdef Q_OS_WIN32
{ DEV_ANTLOCAL, DEV_USB, (char *) "Native ANT+", true, false },
#else
{ DEV_ANTLOCAL, DEV_SERIAL, (char *) "Native ANT+", true, false },
#endif
{ DEV_CT, DEV_SERIAL, (char *) "Computrainer", true, false },
{ DEV_GSERVER, DEV_TCP, (char *) "Golden Cheetah Server", false, false },
{ DEV_NULL, DEV_TCP, (char *) "Null device (testing)", false, false },
{ DEV_ANTPLUS, DEV_QUARQ, (char *) "ANT+ via Quarqd", true, false },
// { DEV_PT, DEV_SERIAL, (char *) "Powertap Head Unit", false, true },
// { DEV_SRM, DEV_SERIAL, (char *) "SRM PowerControl V/VI", false, true },
// { DEV_GCLIENT, DEV_TCP, (char *) "Golden Cheetah Client", false, false },

View File

@@ -37,6 +37,7 @@
#define DEV_QUARQ 0x01 // ants use id:hostname:port
#define DEV_SERIAL 0x02 // use filename COMx or /dev/cuxxxx
#define DEV_TCP 0x03 // tcp port is hostname:port NOT IMPLEMENTED IN THIS RELEASE
#define DEV_USB 0x04 // use filename COMx or /dev/cuxxxx
class DeviceType
{

236
src/LibUsb.cpp Normal file
View File

@@ -0,0 +1,236 @@
/*
* Copyright (c) 2011 Darren Hague & Eric Brandt
*
* 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
*/
#ifdef WIN32
#include <QString>
#include <QDebug>
#include "LibUsb.h"
#ifdef GC_HAVE_LIBUSB // only include if windows and have libusb installed
LibUsb::LibUsb()
{
intf = NULL;
readBufIndex = 0;
readBufSize = 0;
usb_set_debug(255);
// Initialize the library.
usb_init();
// Find all busses.
usb_find_busses();
// Find all connected devices.
usb_find_devices();
}
int LibUsb::open()
{
// Search busses & devices for USB2 ANT+ stick
device = OpenAntStick();
if (device == NULL)
return -1;
int rc = usb_clear_halt(device, writeEndpoint);
if (rc < 0)
qDebug()<<"usb_clear_halt writeEndpoint Error: "<< usb_strerror();
rc = usb_clear_halt(device, readEndpoint);
if (rc < 0)
qDebug()<<"usb_clear_halt readEndpoint Error: "<< usb_strerror();
return rc;
}
void LibUsb::close()
{
if (device) {
usb_release_interface(device, 0);
usb_close(device);
}
device = NULL;
}
int LibUsb::read(char *buf, int bytes)
{
// The USB2 stick really doesn't like you reading 1 byte when more are available
// so we need a buffered reader
int bufRemain = readBufSize - readBufIndex;
// Can we entirely satisfy the request from the buffer?
if (bufRemain > bytes)
{
// Yes, so do it
memcpy(buf, readBuf+readBufIndex, bytes);
readBufIndex += bytes;
return bytes;
}
// No, so partially satisfy by emptying the buffer, then refill the buffer for the rest
memcpy(buf, readBuf+readBufIndex, bufRemain);
readBufSize = 0;
readBufIndex = 0;
int rc = usb_bulk_read(device, readEndpoint, readBuf, 64, 1000);
if (rc < 0)
{
qDebug()<<"usb_bulk_read Error reading: "<< usb_strerror();
return rc;
}
readBufSize = rc;
int bytesToGo = bytes - bufRemain;
if (bytesToGo < readBufSize)
{
// If we have enough bytes in the buffer, return them
memcpy(buf+bufRemain, readBuf, bytesToGo);
readBufIndex += bytesToGo;
rc = bytes;
} else {
// Otherwise, just return what we can
memcpy(buf+bufRemain, readBuf, readBufSize);
rc = bufRemain + readBufSize;
readBufSize = 0;
readBufIndex = 0;
}
return rc;
}
int LibUsb::write(char *buf, int bytes)
{
int rc = usb_interrupt_write(device, writeEndpoint, buf, bytes, 1000);
if (rc < 0)
{
qDebug()<<"usb_interrupt_write Error writing: "<< usb_strerror();
}
return rc;
}
struct usb_dev_handle* LibUsb::OpenAntStick()
{
struct usb_bus* bus;
struct usb_device* dev;
struct usb_dev_handle* udev;
for (bus = usb_get_busses(); bus; bus = bus->next)
{
for (dev = bus->devices; dev; dev = dev->next)
{
if (dev->descriptor.idVendor == GARMIN_USB2_VID && dev->descriptor.idProduct == GARMIN_USB2_PID)
{
qDebug() << "Found a Garmin USB2 ANT+ stick";
if ((udev = usb_open(dev)))
{
if (dev->descriptor.bNumConfigurations)
{
if ((intf = usb_find_interface(&dev->config[0])) != NULL)
{
int rc = usb_set_configuration(udev, 1);
if (rc < 0)
qDebug()<<"usb_set_configuration Error: "<< usb_strerror();
rc = usb_claim_interface(udev, 0);
if (rc < 0)
qDebug()<<"usb_claim_interface Error: "<< usb_strerror();
rc = usb_set_altinterface(udev, 0);
if (rc < 0)
qDebug()<<"usb_set_altinterface Error: "<< usb_strerror();
return udev;
}
}
usb_close(udev);
}
}
}
}
return NULL;
}
struct usb_interface_descriptor* LibUsb::usb_find_interface(struct usb_config_descriptor* config_descriptor)
{
struct usb_interface_descriptor* intf;
readEndpoint = -1;
writeEndpoint = -1;
if (!config_descriptor)
return NULL;
if (!config_descriptor->bNumInterfaces)
return NULL;
if (!config_descriptor->interface[0].num_altsetting)
return NULL;
intf = &config_descriptor->interface[0].altsetting[0];
if (intf->bNumEndpoints != 2)
return NULL;
for (int i = 0 ; i < 2; i++)
{
if (intf->endpoint[i].bEndpointAddress & USB_ENDPOINT_DIR_MASK)
readEndpoint = intf->endpoint[i].bEndpointAddress;
else
writeEndpoint = intf->endpoint[i].bEndpointAddress;
}
if (readEndpoint < 0 || writeEndpoint < 0)
return NULL;
return intf;
}
#else
// if we don't have libusb use stubs
LibUsb::LibUsb() {}
int LibUsb::open()
{
return -1;
}
void LibUsb::close()
{
}
int LibUsb::read(char *, int)
{
return -1;
}
int LibUsb::write(char *, int)
{
return -1;
}
#endif // Have LIBUSB
#endif // WIN32

53
src/LibUsb.h Normal file
View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2011 Darren Hague & Eric Brandt
*
* 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_LibUsb_h
#define gc_LibUsb_h
#if defined WIN32
#ifdef GC_HAVE_LIBUSB
#include <usb.h> // for the constants etc
#endif
#define GARMIN_USB2_VID 0x0fcf
#define GARMIN_USB2_PID 0x1008
class LibUsb {
public:
LibUsb();
int open();
void close();
int read(char *buf, int bytes);
int write(char *buf, int bytes);
private:
#ifdef GC_HAVE_LIBUSB
struct usb_dev_handle* OpenAntStick();
struct usb_interface_descriptor* usb_find_interface(struct usb_config_descriptor* config_descriptor);
struct usb_dev_handle* device;
struct usb_interface_descriptor* intf;
int readEndpoint, writeEndpoint;
char readBuf[64];
int readBufIndex;
int readBufSize;
#endif
};
#endif // WIN32
#endif // gc_LibUsb_h

View File

@@ -866,44 +866,55 @@ DevicePage::setConfigPane()
switch (Supported.getType(typeSelector->itemData(typeSelector->currentIndex()).toInt()).connector) {
case DEV_QUARQ:
specHint->show();
specLabel->show();
deviceSpecifier->show();
specHint->setText("hostname:port");
profHint->setText("antid 1, antid 2 ...");
profHint->show();
pairButton->show();
profLabel->show();
deviceProfile->show();
break;
case DEV_SERIAL:
#ifdef WIN32
specHint->setText("COMx");
#else
specHint->setText("/dev/xxxx");
#endif
// we have ANT+ sticks on serial and we have Computrainers
// on serial, which one is it?
if (Supported.getType(typeSelector->itemData(typeSelector->currentIndex())
.toInt()).type == DEV_ANTLOCAL) {
pairButton->show();
profHint->setText("antid 1, antid 2 ...");
profHint->show();
profLabel->show();
deviceProfile->show();
} else {
pairButton->hide();
profHint->hide();
profLabel->hide();
deviceProfile->hide();
}
break;
case DEV_TCP:
specHint->setText("hostname:port");
pairButton->hide();
specHint->show();
specLabel->show();
deviceSpecifier->show();
profHint->hide();
profLabel->hide();
deviceProfile->hide();
break;
case DEV_TCP:
specHint->show();
specLabel->show();
deviceSpecifier->show();
specHint->setText("hostname:port");
profHint->hide();
profLabel->hide();
deviceProfile->hide();
break;
case DEV_USB:
specHint->hide();
specLabel->hide();
deviceSpecifier->hide();
profHint->setText("antid 1, antid 2 ...");
profHint->show();
profLabel->show();
deviceProfile->show();
break;
}
//specHint->setTextFormat(Qt::Italic); // mmm need to read the docos
// pair button only valid for ANT+ (Quarqd or Native)
int type = Supported.getType(typeSelector->itemData(typeSelector->currentIndex()).toInt()).type;
if (type == DEV_ANTLOCAL || type == DEV_ANTPLUS) pairButton->show();
else pairButton->hide();
}

View File

@@ -16,12 +16,15 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if defined (WIN32) && defined (GC_HAVE_USBXPRESS) // only include if windows and have USBXpress installed
#include "USBXpress.h"
#ifdef WIN32
#include <QString>
#include <QDebug>
#ifdef GC_HAVE_USBXPRESS
// if we have usbxpress installed then use it...
#include "USBXpress.h"
USBXpress::USBXpress() {} // nothing to do - all members are static
int USBXpress::open(HANDLE *handle)
@@ -97,4 +100,31 @@ int USBXpress::write(HANDLE *handle, unsigned char *buf, int bytes)
return -1;
}
#endif
#else
// if we don't have USBXpress installed then stubs return fail
USBXpress::USBXpress() {} // nothing to do - all members are static
int USBXpress::open(HANDLE *)
{
return -1;
}
int USBXpress::close(HANDLE *)
{
return -1;
}
int USBXpress::read(HANDLE *, unsigned char *, int)
{
return -1;
}
int USBXpress::write(HANDLE *, unsigned char *, int)
{
return -1;
}
#endif // USBXpress
#endif // Win32

View File

@@ -79,6 +79,12 @@ D2XX_INCLUDE = /usr/local/include/D2XX
# pathname (SiLabs in your root directory)
#USBXPRESS_INSTALL=/c/SiLabs/MCU/USBXpress/USBXpress_API/Host/
# ** note this is also only required on windowsto work with
# ** Garmin USB2 sticks whose driver uses libusb-win32.
# You must make sure you install v0.1.12.2 of libusb-win from:
# http://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/0.1.12.2/
#LIBUSB_INSTALL=/c/libusb-win32-device-bin-0.1.12.2
# We recommend a debug build for development, and a static build for releases.
CONFIG += debug
#CONFIG += static

View File

@@ -76,14 +76,20 @@ qwt3d {
LIBS += $${ICAL_LIBS}
}
# are we supporting USB1 devices on Windows?
!isEmpty( USBXPRESS_INSTALL ) {
LIBS += $${USBXPRESS_INSTALL}/x86/SiUSBXp.lib
INCLUDEPATH += $${USBXPRESS_INSTALL}
SOURCES += USBXpress.cpp
HEADERS += USBXpress.h
DEFINES += GC_HAVE_USBXPRESS
}
# are we supporting USB2 devices on Windows?
!isEmpty( LIBUSB_INSTALL ) {
LIBS += $${LIBUSB_INSTALL}/lib/msvc/libusb.lib
INCLUDEPATH += $${LIBUSB_INSTALL}/include
DEFINES += GC_HAVE_LIBUSB
}
macx {
LIBS += -lobjc -framework Carbon -framework AppKit
HEADERS += QtMacSegmentedButton.h
@@ -101,6 +107,10 @@ win32 {
-Wl,--script,win32/i386pe.x-no-rdata,--enable-auto-import
//QMAKE_CXXFLAGS += -fdata-sections
RC_FILE = windowsico.rc
# Windows only USB support
SOURCES += USBXpress.cpp LibUsb.cpp
HEADERS += USBXpress.h LibUsb.h
}
# local qxt widgets - rather than add another dependency on libqxt