diff --git a/src/ANT.cpp b/src/ANT.cpp index 973c90475..840b5c0cc 100644 --- a/src/ANT.cpp +++ b/src/ANT.cpp @@ -90,11 +90,32 @@ ANT::ANT(QObject *parent, DeviceConfiguration *devConf) : QThread(parent) antIDs.clear(); // setup the channels - for (int i=0; ichannel_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; ichannelInfo(); + //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"<= 0 && channel < 4) { - // handle a channel event here! -//qDebug()<<"channel event on channel:"<receiveMessage(rxMessage); } } @@ -519,7 +567,6 @@ ANT::handleChannelEvent(void) { void ANT::processMessage(void) { -//qDebug()<<"processing ant message"<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; diff --git a/src/ANT.h b/src/ANT.h index 2fbee61ef..0ea231e6a 100644 --- a/src/ANT.h +++ b/src/ANT.h @@ -58,6 +58,7 @@ #include #include #include "USBXpress.h" // for Garmin USB1 sticks +#include "LibUsb.h" // for Garmin USB2 sticks #else #include // unix!! #include // 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; diff --git a/src/ANTChannel.cpp b/src/ANTChannel.cpp index d58c3dd07..565208933 100755 --- a/src/ANTChannel.cpp +++ b/src/ANTChannel.cpp @@ -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="<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"<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 diff --git a/src/ANTChannel.h b/src/ANTChannel.h index e335ae397..8d6028852 100755 --- a/src/ANTChannel.h +++ b/src/ANTChannel.h @@ -22,6 +22,7 @@ #include "ANT.h" #include "ANTMessage.h" +#include #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 diff --git a/src/ANTMessage.cpp b/src/ANTMessage.cpp index ffb4eabbf..fa37cac22 100755 --- a/src/ANTMessage.cpp +++ b/src/ANTMessage.cpp @@ -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"<>"<< type; break; // shouldn't get here! } } diff --git a/src/ANTMessage.h b/src/ANTMessage.h index 96400252b..3e5fed386 100755 --- a/src/ANTMessage.h +++ b/src/ANTMessage.h @@ -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 diff --git a/src/DeviceTypes.cpp b/src/DeviceTypes.cpp index b83124d7d..5deac4517 100644 --- a/src/DeviceTypes.cpp +++ b/src/DeviceTypes.cpp @@ -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 }, diff --git a/src/DeviceTypes.h b/src/DeviceTypes.h index 70e70675e..45016615c 100644 --- a/src/DeviceTypes.h +++ b/src/DeviceTypes.h @@ -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 { diff --git a/src/LibUsb.cpp b/src/LibUsb.cpp new file mode 100644 index 000000000..499d497c8 --- /dev/null +++ b/src/LibUsb.cpp @@ -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 +#include + +#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 diff --git a/src/LibUsb.h b/src/LibUsb.h new file mode 100644 index 000000000..d6820fed7 --- /dev/null +++ b/src/LibUsb.h @@ -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 // 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 diff --git a/src/Pages.cpp b/src/Pages.cpp index 95e79b9ed..3c780cfd7 100644 --- a/src/Pages.cpp +++ b/src/Pages.cpp @@ -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(); } diff --git a/src/USBXpress.cpp b/src/USBXpress.cpp index 317f65c2e..0fc0c139c 100644 --- a/src/USBXpress.cpp +++ b/src/USBXpress.cpp @@ -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 #include +#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 diff --git a/src/gcconfig.pri.in b/src/gcconfig.pri.in index b2ee9e5fe..779fd0053 100644 --- a/src/gcconfig.pri.in +++ b/src/gcconfig.pri.in @@ -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 diff --git a/src/src.pro b/src/src.pro index 531e33ac0..7187fa900 100644 --- a/src/src.pro +++ b/src/src.pro @@ -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