Files
GoldenCheetah/src/ANT.cpp
Mark Liversedge 43b1c61d6b Add Device Wizard
Introduce a wizard for adding realtime devices since it
is prone to user error, is relatively complicated and is
something most users will only ever do once or twice.

There are several logical updates within this patch:

First; Fix intermittent ANT+ not working
* LibUsb     - check bufRemaining is > 0 not non-zero
* LibUsb     - Always reset the USB stick on open
* ANT.cpp    - Support > 4 channels on USB2
* ANTChannel -  Do not use pairing, but always
*               unassign, assign and set channel id.

Second; Fix device discovery
* Find and discover support in realtime controllers
* Extend Serial.cpp to cover more Serial devices
* Support for 4 or 8 ANT channels for USB1/USB2

Third; Introduce Add Device Wizard with
* General and Specific wizard pages for each device type
* Device pairing with telemetry display
* fixed compile time warnings

Fourth; Update Device Config Page
* use wizard to add new device
* remove edit fields, replaced by wizard
* remove pair, firmware buttons replaced by wizard

Fifth; Deprecate/Remove Device Types
* Null Device - used by developers only
* Quarqd client - replaced by Native ANT+
* GC Server - not implemented yet

I have also introduced a device specific wheel size
and flags for controlling the default selection and
sources for sensor data when in multi-device mode. These
are not yet supported in the code.

Fixes #611.
Fixes #497.
2012-01-21 20:28:33 +00:00

967 lines
26 KiB
C++

/*
* Copyright (c) 2009 Mark Rages
* Copyright (c) 2011 Mark Liversedge (liversedge@gmail.com)
*
* 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
*/
//------------------------------------------------------------------------
// This code has been created by folding the ANT.cpp source with
// the Quarqd source provided by Mark Rages and the Serial device
// code from Computrainer.cpp
//------------------------------------------------------------------------
#include "ANT.h"
#include "ANTMessage.h"
#include <QMessageBox>
#include <QTime>
#include <QProgressDialog>
#include <QtDebug>
#include "RealtimeData.h"
#ifdef Q_OS_LINUX // to get stat /dev/xxx for major/minor
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
/* Control status */
#define ANT_RUNNING 0x01
#define ANT_PAUSED 0x02
// network key
const unsigned char ANT::key[8] = { 0xB9, 0xA5, 0x21, 0xFB, 0xBD, 0x72, 0xC3, 0x45 };
// supported sensor types
const ant_sensor_type_t ANT::ant_sensor_types[] = {
{ ANTChannel::CHANNEL_TYPE_UNUSED, 0, 0, 0, 0, "Unused", '?', "" },
{ ANTChannel::CHANNEL_TYPE_HR, ANT_SPORT_HR_PERIOD, ANT_SPORT_HR_TYPE,
ANT_SPORT_FREQUENCY, ANT_SPORT_NETWORK_NUMBER, "Heartrate", 'h', ":images/IconHR.png" },
{ ANTChannel::CHANNEL_TYPE_POWER, ANT_SPORT_POWER_PERIOD, ANT_SPORT_POWER_TYPE,
ANT_SPORT_FREQUENCY, ANT_SPORT_NETWORK_NUMBER, "Power", 'p', ":images/IconPower.png" },
{ ANTChannel::CHANNEL_TYPE_SPEED, ANT_SPORT_SPEED_PERIOD, ANT_SPORT_SPEED_TYPE,
ANT_SPORT_FREQUENCY, ANT_SPORT_NETWORK_NUMBER, "Speed", 's', ":images/IconSpeed.png" },
{ ANTChannel::CHANNEL_TYPE_CADENCE, ANT_SPORT_CADENCE_PERIOD, ANT_SPORT_CADENCE_TYPE,
ANT_SPORT_FREQUENCY, ANT_SPORT_NETWORK_NUMBER, "Cadence", 'c', ":images/IconCadence.png" },
{ ANTChannel::CHANNEL_TYPE_SandC, ANT_SPORT_SandC_PERIOD, ANT_SPORT_SandC_TYPE,
ANT_SPORT_FREQUENCY, ANT_SPORT_NETWORK_NUMBER, "Speed + Cadence", 'd', ":images/IconCadence.png" },
{ ANTChannel::CHANNEL_TYPE_QUARQ, ANT_QUARQ_PERIOD, ANT_QUARQ_TYPE,
ANT_QUARQ_FREQUENCY, DEFAULT_NETWORK_NUMBER, "Quarq Channel", 'Q', ":images/IconPower.png" },
{ ANTChannel::CHANNEL_TYPE_FAST_QUARQ, ANT_FAST_QUARQ_PERIOD, ANT_FAST_QUARQ_TYPE,
ANT_FAST_QUARQ_FREQUENCY, DEFAULT_NETWORK_NUMBER, "Fast Quarq", 'q', ":images/IconPower.png" },
{ ANTChannel::CHANNEL_TYPE_FAST_QUARQ_NEW, ANT_FAST_QUARQ_PERIOD, ANT_FAST_QUARQ_TYPE_WAS,
ANT_FAST_QUARQ_FREQUENCY, DEFAULT_NETWORK_NUMBER, "Fast Quarq New", 'n', ":images/IconPower.png" },
{ ANTChannel::CHANNEL_TYPE_GUARD, 0, 0, 0, 0, "", '\0', "" }
};
//
// The ANT class is a worker thread, reading/writing to a local
// Garmin ANT+ serial device. It maintains local state and telemetry.
// It is controlled by an ANTController, which starts/stops and will
// request telemetry and send commands to assign channels etc
//
// ANTController sits between the RealtimeWindow and the ANT worker
// thread and is part of the GC architecture NOT related to the
// hardware controller.
//
ANT::ANT(QObject *parent, DeviceConfiguration *devConf) : QThread(parent)
{
// device status and settings
Status=0;
deviceFilename = devConf ? devConf->portSpec : "";
baud=115200;
powerchannels=0;
configuring = false;
// state machine
state = ST_WAIT_FOR_SYNC;
length = bytes = 0;
checksum = ANT_SYNC_BYTE;
// ant ids - may not be configured of course
if (devConf && devConf->deviceProfile.length())
antIDs = devConf->deviceProfile.split(",");
else
antIDs.clear();
// setup the channels
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(channelInfo(int,int,int)), this, SLOT(channelInfo(int,int,int)));
connect(antChannel[i], SIGNAL(dropInfo(int,int,int)), this, SLOT(dropInfo(int,int,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(slotSearchTimeout(int)));
connect(antChannel[i], SIGNAL(searchComplete(int)), this, SLOT(slotSearchComplete(int)));
}
// on windows and linux we use libusb to read from USB2
// sticks, if it is not available we use stubs
#if defined GC_HAVE_LIBUSB
usbMode = USBNone;
channels = 0;
usb2 = new LibUsb(TYPE_ANT);
#endif
}
ANT::~ANT()
{
#if defined GC_HAVE_LIBUSB
delete usb2;
#endif
}
void ANT::setDevice(QString x)
{
deviceFilename = x;
}
void ANT::setBaud(int x)
{
baud = x;
}
double ANT::channelValue2(int channel)
{
return antChannel[channel]->channelValue2();
}
double ANT::channelValue(int channel)
{
return antChannel[channel]->channelValue();
}
/*======================================================================
* Main thread functions; start, stop etc
*====================================================================*/
void ANT::run()
{
int status; // control commands from controller
powerchannels = 0;
Status = ANT_RUNNING;
QString strBuf;
#if defined GC_HAVE_LIBUSB
usbMode = USBNone;
channels = 0;
#endif
for (int i=0; i<ANT_MAX_CHANNELS; i++) antChannel[i]->init();
state = ST_WAIT_FOR_SYNC;
length = bytes = 0;
checksum = ANT_SYNC_BYTE;
if (openPort() == 0) {
antlog.setFileName("antlog.bin");
antlog.open(QIODevice::WriteOnly | QIODevice::Truncate);
sendMessage(ANTMessage::resetSystem());
sendMessage(ANTMessage::setNetworkKey(1, key));
// pair with specified devices on next available channel
if (antIDs.count()) {
foreach(QString antid, antIDs) {
if (antid.length()) {
unsigned char c = antid.at(antid.length()-1).toLatin1();
int ch_type = interpretSuffix(c);
int device_number = antid.mid(0, antid.length()-1).toInt();
addDevice(device_number, ch_type, -1);
}
}
} else {
if (!configuring) {
// not configured, just pair with whatever you can find
addDevice(0, ANTChannel::CHANNEL_TYPE_SPEED, 0);
addDevice(0, ANTChannel::CHANNEL_TYPE_POWER, 1);
addDevice(0, ANTChannel::CHANNEL_TYPE_CADENCE, 2);
addDevice(0, ANTChannel::CHANNEL_TYPE_HR, 3);
if (channels > 4) addDevice(0, ANTChannel::CHANNEL_TYPE_SandC, 4);
}
}
} else {
quit(0);
return;
}
while(1)
{
// read more bytes from the device
uint8_t byte;
if (rawRead(&byte, 1) > 0) receiveByte((unsigned char)byte);
else msleep(5);
//----------------------------------------------------------------------
// LISTEN TO CONTROLLER FOR COMMANDS
//----------------------------------------------------------------------
pvars.lock();
status = this->Status;
pvars.unlock();
// do we have a channel to search / stop
if (!channelQueue.isEmpty()) {
setChannelAtom x = channelQueue.dequeue();
if (x.device_number == -1) antChannel[x.channel]->close(); // unassign
else addDevice(x.device_number, x.channel_type, x.channel); // assign
}
/* time to shut up shop */
if (!(status&ANT_RUNNING)) {
// time to stop!
quit(0);
return;
}
}
}
int
ANT::start()
{
QThread::start();
return 0;
}
int
ANT::restart()
{
int status;
// get current status
pvars.lock();
status = this->Status;
pvars.unlock();
// what state are we in anyway?
if (status&ANT_RUNNING && status&ANT_PAUSED) {
status &= ~ANT_PAUSED;
pvars.lock();
this->Status = status;
pvars.unlock();
return 0; // ok its running again!
}
return 2;
}
int
ANT::pause()
{
int status;
// get current status
pvars.lock();
status = this->Status;
pvars.unlock();
if (status&ANT_PAUSED) return 2;
else if (!(status&ANT_RUNNING)) return 4;
else {
// ok we're running and not paused so lets pause
status |= ANT_PAUSED;
pvars.lock();
this->Status = status;
pvars.unlock();
return 0;
}
}
int
ANT::stop()
{
// what state are we in anyway?
pvars.lock();
Status = 0; // Terminate it!
pvars.unlock();
// close debug file
antlog.close();
return 0;
}
int
ANT::quit(int code)
{
// event code goes here!
closePort();
exit(code);
return 0;
}
void
ANT::getRealtimeData(RealtimeData &rtData)
{
int mode = rtData.mode;
long load = rtData.getLoad();
double slope = rtData.getSlope();
rtData = telemetry;
rtData.mode = mode;
rtData.setLoad(load);
rtData.setSlope(slope);
}
/*======================================================================
* Channel management
*====================================================================*/
// returns 1 for success, 0 for fail.
int
ANT::addDevice(int device_number, int device_type, int channel_number)
{
// if we're given a channel number, then use that one
if (channel_number>-1) {
//antChannel[channel_number]->close();
antChannel[channel_number]->open(device_number, device_type);
return 1;
}
// if we already have the device, then return.
// but only if the device number is given since
// we may choose to scan for multiple devices
// on separate channels (e.g. 0p on channel 0
// and 0p on channel 1
if (device_number != 0) {
for (int i=0; i<channels; i++) {
if (((antChannel[i]->channel_type & 0xf ) == device_type) &&
(antChannel[i]->device_number == device_number)) {
// send the channel found...
//XXX antChannel[i]->channelInfo();
return 1;
}
}
}
// look for an unused channel and use on that one
for (int i=0; i<channels; i++) {
if (antChannel[i]->channel_type == ANTChannel::CHANNEL_TYPE_UNUSED) {
//antChannel[i]->close();
antChannel[i]->open(device_number, device_type);
// this is an alternate channel for power
if (device_type == ANTChannel::CHANNEL_TYPE_POWER) {
// if we are not the first power channel then set to update
// the alternate power channel
if (powerchannels) antChannel[i]->setAlt(true);
// increment the number of power channels
powerchannels++;
}
return 1;
}
}
// there are no unused channels. fail.
return 0;
}
// returns 1 for successfully removed, 0 for none found.
int
ANT::removeDevice(int device_number, int channel_type)
{
int i;
for (i=0; i<channels; i++) {
ANTChannel *ac = antChannel[i];
if ((ac->channel_type == channel_type) && (ac->device_number == device_number)) {
if ((ac->control_channel!=ac) && ac->control_channel)
removeDevice(device_number, ac->control_channel->channel_type);
ac->close();
ac->channel_type=ANTChannel::CHANNEL_TYPE_UNUSED;
ac->device_number=0;
ac->setId();
return 1;
}
}
// device not found.
return 0;
}
ANTChannel *
ANT::findDevice(int device_number, int channel_type)
{
int i;
for (i=0; i<channels; i++) {
if (((antChannel[i]->channel_type) == channel_type) &&
(antChannel[i]->device_number==device_number)) {
return antChannel[i];
}
}
// device not found.
return NULL;
}
int
ANT::startWaitingSearch()
{
int i;
// are any fast searches in progress? if so, then bail
for (i=0; i<channels; i++) {
if (antChannel[i]->channel_type_flags & CHANNEL_TYPE_QUICK_SEARCH) {
return 0;
}
}
// start the first slow search
for (i=0; i<channels; i++) {
if (antChannel[i]->channel_type_flags & CHANNEL_TYPE_WAITING) {
antChannel[i]->channel_type_flags &= ~CHANNEL_TYPE_WAITING;
sendMessage(ANTMessage::unassignChannel(i));
return 1;
}
}
return 0;
}
void
ANT::report()
{
for (int i=0; i<channels; i++)
//XXX antChannel[i]->channelInfo();
;
}
void
ANT::associateControlChannels() {
// first, unassociate all control channels
for (int i=0; i<channels; i++) antChannel[i]->control_channel=NULL;
// then, associate cinqos:
// new cinqos get their own selves for control
// old cinqos, look for an open control channel
// if found and open, associate
// elif found and not open yet, nop
// elif not found, open one
for (int i=0; i<channels; i++) {
ANTChannel *ac=antChannel[i];
switch (ac->channel_type) {
case ANTChannel::CHANNEL_TYPE_POWER:
if (ac->is_cinqo) {
if (ac->is_old_cinqo) {
ANTChannel *my_ant_channel;
my_ant_channel=findDevice(ac->device_number, ANTChannel::CHANNEL_TYPE_QUARQ);
if (!my_ant_channel) my_ant_channel=findDevice(ac->device_number, ANTChannel::CHANNEL_TYPE_FAST_QUARQ);
if (!my_ant_channel) my_ant_channel=findDevice(ac->device_number, ANTChannel::CHANNEL_TYPE_FAST_QUARQ_NEW);
if (my_ant_channel) {
if (my_ant_channel->isSearching()) {
// ignore if searching
} else {
ac->control_channel=my_ant_channel;
ac->sendCinqoSuccess();
}
} else { // no ant channel, let's start one
addDevice(ac->device_number, ANTChannel::CHANNEL_TYPE_QUARQ, -1);
}
} else { // new cinqo
ac->control_channel=ac;
ac->sendCinqoSuccess();
}
} // is_cinqo
break;
case ANTChannel::CHANNEL_TYPE_FAST_QUARQ:
case ANTChannel::CHANNEL_TYPE_FAST_QUARQ_NEW:
case ANTChannel::CHANNEL_TYPE_QUARQ:
ac->is_cinqo=1;
ac->control_channel=ac;
break;
default:
;
} // channel_type case
} // for-loop
}
// For serial device discovery
bool
ANT::discover(QString name)
{
#ifdef Q_OS_LINUX
// All we can do for USB1 sticks is see if the cp210x driver module
// is loaded for this device, and if it is, we will use the device
// XXX need a better way of probing this device, but USB1 sticks
// are getting rarer, so maybe we can just make do with this
// until we deprecate them altogether
struct stat s;
if (stat(name.toLatin1(), &s) == -1) return false;
int maj = major(s.st_rdev);
int min = minor(s.st_rdev);
QString sysFile = QString("/sys/dev/char/%1:%2/device/driver/module/drivers/usb:cp210x").arg(maj).arg(min);
if (QFileInfo(sysFile).exists()) return true;
#endif
#ifdef Q_OS_MAC
// On MAC we only support the SILabs Virtual Com Port Drivers
// which will leave a device file /dev/cu.ANTUSBStick.slabvcp
if (name == "/dev/cu.ANTUSBStick.slabvcp") return true;
#endif
return false;
}
void
ANT::channelInfo(int channel, int device_number, int device_id)
{
emit foundDevice(channel, device_number, device_id);
//qDebug()<<"found device number"<<device_number<<"type"<<device_id<<"on channel"<<channel
//<< "is a "<<deviceTypeDescription(device_id) << "with code"<<deviceTypeCode(device_id);
}
void
ANT::dropInfo(int channel, int drops, int received) // we dropped a message
{
double reliability = 100.00f - (100.00f * double(drops) / double(received));
//qDebug()<<"Channel"<<channel<<"reliability is"<< (int)(reliability)<<"%";
emit signalStrength(channel, reliability);
return;
}
void
ANT::lostInfo(int number) // we lost the connection
{
if (number < 0 || number >= channels) return; // ignore out of bound
emit lostDevice(number);
//qDebug()<<"lost info for channel"<<number;
}
void
ANT::staleInfo(int number) // info is now stale - set to zero
{
if (number < 0 || number >= channels) return; // ignore out of bound
//qDebug()<<"stale info for channel"<<number;
}
void
ANT::slotSearchTimeout(int number) // search timed out
{
if (number < 0 || number >= channels) return; // ignore out of bound
emit searchTimeout(number);
//qDebug()<<"search timeout on channel"<<number;
}
void
ANT::slotSearchComplete(int number) // search completed successfully
{
if (number < 0 || number >= channels) return; // ignore out of bound
emit searchComplete(number);
//qDebug()<<"search completed on channel"<<number;
}
/*----------------------------------------------------------------------
* Message I/O
*--------------------------------------------------------------------*/
void
ANT::sendMessage(ANTMessage m) {
static const unsigned char padding[5] = { '\0', '\0', '\0', '\0', '\0' };
//fprintf(stderr, ">> send: ");
//for(int i=0; i<m.length+3; i++) fprintf(stderr, "%02x ", m.data[i]);
//fprintf(stderr, "\n");
rawWrite((uint8_t*)m.data, m.length);
// this padding is important, for some reason XXX find out why?
rawWrite((uint8_t*)padding, 5);
}
void
ANT::receiveByte(unsigned char byte) {
switch (state) {
case ST_WAIT_FOR_SYNC:
if (byte == ANT_SYNC_BYTE) {
state = ST_GET_LENGTH;
checksum = ANT_SYNC_BYTE;
rxMessage[0] = byte;
}
break;
case ST_GET_LENGTH:
if ((byte == 0) || (byte > ANT_MAX_LENGTH)) {
state = ST_WAIT_FOR_SYNC;
}
else {
rxMessage[ANT_OFFSET_LENGTH] = byte;
checksum ^= byte;
length = byte;
bytes = 0;
state = ST_GET_MESSAGE_ID;
}
break;
case ST_GET_MESSAGE_ID:
rxMessage[ANT_OFFSET_ID] = byte;
checksum ^= byte;
state = ST_GET_DATA;
break;
case ST_GET_DATA:
rxMessage[ANT_OFFSET_DATA + bytes] = byte;
checksum ^= byte;
if (++bytes >= length){
state = ST_VALIDATE_PACKET;
}
break;
case ST_VALIDATE_PACKET:
if (checksum == byte){
processMessage();
}
state = ST_WAIT_FOR_SYNC;
break;
}
}
//
// Pass inbound message to channel for handling
//
void
ANT::handleChannelEvent(void) {
int channel = rxMessage[ANT_OFFSET_DATA] & 0x7;
if(channel >= 0 && channel < channels) {
// handle a channel event here!
antChannel[channel]->receiveMessage(rxMessage);
}
}
void
ANT::processMessage(void) {
ANTMessage m(this, rxMessage); // for debug!
//fprintf(stderr, "<< receive: ");
//for(int i=0; i<m.length+3; i++) fprintf(stderr, "%02x ", m.data[i]);
//fprintf(stderr, "\n");
QDataStream out(&antlog);
for (int i=0; i<ANT_MAX_MESSAGE_SIZE; i++)
out<<rxMessage[i];
switch (rxMessage[ANT_OFFSET_ID]) {
case ANT_ACK_DATA:
case ANT_BROADCAST_DATA:
case ANT_CHANNEL_STATUS:
case ANT_CHANNEL_ID:
case ANT_BURST_DATA:
handleChannelEvent();
break;
case ANT_CHANNEL_EVENT:
switch (rxMessage[ANT_OFFSET_MESSAGE_CODE]) {
case EVENT_TRANSFER_TX_FAILED:
//XXX remember last message ... ANT_SendAckMessage();
break;
case EVENT_TRANSFER_TX_COMPLETED:
// fall through
default:
handleChannelEvent();
}
break;
case ANT_VERSION:
break;
case ANT_CAPABILITIES:
break;
case ANT_SERIAL_NUMBER:
break;
default:
break;
}
}
/*======================================================================
* Serial I/O
*====================================================================*/
int ANT::closePort()
{
#ifdef WIN32
switch (usbMode) {
case USB2 :
usb2->close();
return 0;
break;
case USB1 :
return (int)!CloseHandle(devicePort);
break;
default :
return -1;
break;
}
#else
#ifdef GC_HAVE_LIBUSB
if (usbMode == USB2) {
usb2->close();
return 0;
}
#endif
tcflush(devicePort, TCIOFLUSH); // clear out the garbage
return close(devicePort);
#endif
}
bool ANT::find()
{
#if defined WIN32 || defined GC_HAVE_LIBUSB
if (usb2->find() == true) return true;
#endif
#ifdef WIN32
if (USBXpress::find() == true) return true;
#endif
return false;
}
int ANT::openPort()
{
#ifdef WIN32
int rc;
// on windows we try on USB2 then on USB1 then fail...
if ((rc=usb2->open()) != -1) {
usbMode = USB2;
channels = 8;
return rc;
} else if ((rc= USBXpress::open(&devicePort)) != -1) {
usbMode = USB1;
channels = 4;
return rc;
} else {
usbMode = USBNone;
channels = 0;
return -1;
}
#else
// LINUX AND MAC USES TERMIO / IOCTL / STDIO
#if defined(Q_OS_MACX)
int ldisc=TTYDISC;
#else
int ldisc=N_TTY; // LINUX
#endif
#ifdef GC_HAVE_LIBUSB
int rc;
if ((rc=usb2->open()) != -1) {
usbMode = USB2;
channels = 8;
return rc;
} else {
usbMode = USB1;
channels = 4;
}
#endif
if ((devicePort=open(deviceFilename.toAscii(),O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1)
return errno;
tcflush(devicePort, TCIOFLUSH); // clear out the garbage
if (ioctl(devicePort, TIOCSETD, &ldisc) == -1) return errno;
// get current settings for the port
tcgetattr(devicePort, &deviceSettings);
// set raw mode i.e. ignbrk, brkint, parmrk, istrip, inlcr, igncr, icrnl, ixon
// noopost, cs8, noecho, noechonl, noicanon, noisig, noiexn
cfmakeraw(&deviceSettings);
cfsetspeed(&deviceSettings, B115200);
// further attributes
deviceSettings.c_iflag= IGNPAR;
deviceSettings.c_oflag=0;
deviceSettings.c_cflag &= (~CSIZE & ~CSTOPB);
#if defined(Q_OS_MACX)
deviceSettings.c_cflag |= (CS8 | CREAD | HUPCL | CCTS_OFLOW | CRTS_IFLOW);
#else
deviceSettings.c_cflag |= (CS8 | CREAD | HUPCL | CRTSCTS);
#endif
deviceSettings.c_lflag=0;
deviceSettings.c_cc[VMIN]=0;
deviceSettings.c_cc[VTIME]=0;
// set those attributes
if(tcsetattr(devicePort, TCSANOW, &deviceSettings) == -1) return errno;
tcgetattr(devicePort, &deviceSettings);
#endif
// success
return 0;
}
int ANT::rawWrite(uint8_t *bytes, int size) // unix!!
{
int rc=0;
#ifdef WIN32
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
#ifdef GC_HAVE_LIBUSB
if (usbMode == USB2) {
return usb2->write((char *)bytes, size);
}
#endif
int ibytes;
ioctl(devicePort, FIONREAD, &ibytes);
// timeouts are less critical for writing, since vols are low
rc= write(devicePort, bytes, size);
if (rc != -1) tcdrain(devicePort); // wait till its gone.
ioctl(devicePort, FIONREAD, &ibytes);
return rc;
#endif
}
int ANT::rawRead(uint8_t bytes[], int size)
{
int rc=0;
#ifdef WIN32
switch (usbMode) {
case USB1:
return USBXpress::read(&devicePort, bytes, size);
break;
case USB2:
return usb2->read((char *)bytes, size);
break;
default:
rc = 0;
break;
}
#else
#ifdef GC_HAVE_LIBUSB
if (usbMode == USB2) {
return usb2->read((char *)bytes, size);
}
#endif
int i=0;
uint8_t byte;
// read one byte at a time sleeping when no data ready
// until we timeout waiting then return error
for (i=0; i<size; i++) {
rc = read(devicePort, &byte, 1);
if (rc == -1 || rc == 0) return -1; // error!
else bytes[i] = byte;
}
return i;
#endif
}
// convert 'p' 'c' etc into ANT values for device type
int ANT::interpretSuffix(char c)
{
const ant_sensor_type_t *st=ant_sensor_types;
do {
if (st->suffix==c) return st->type;
} while (++st, st->type != ANTChannel::CHANNEL_TYPE_GUARD);
return -1;
}
// convert ANT value to 'p' 'c' values // XXX this and below are named wrong, legacy from quarqd code.
char ANT::deviceIdCode(int type)
{
const ant_sensor_type_t *st=ant_sensor_types;
do {
if (st->type==type) return st->suffix;
} while (++st, st->type != ANTChannel::CHANNEL_TYPE_GUARD);
return '-';
}
// convert ANT value to 'p' 'c' values
char ANT::deviceTypeCode(int type)
{
const ant_sensor_type_t *st=ant_sensor_types;
do {
if (st->device_id==type) return st->suffix;
} while (++st, st->type != ANTChannel::CHANNEL_TYPE_GUARD);
return '-';
}
// convert ANT value to human string
const char * ANT::deviceTypeDescription(int type)
{
const ant_sensor_type_t *st=ant_sensor_types;
do {
if (st->device_id==type) return st->descriptive_name;
} while (++st, st->type != ANTChannel::CHANNEL_TYPE_GUARD);
return "Unknown device type";
}