Subversion Repositories public

Rev

Rev 280 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/***************************************************************************
 *   Copyright (C) 2007 to 2009 by Andreas Theofilu                        *
 *   andreas@theosys.at                                                    *
 *                                                                         *
 *   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 version 3 of the License.                *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/*
 * This code is heavily based on the work of <paul@ant.sbrk.co.uk>
 */
#include "gant.h"
#include <sys/select.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <stdlib.h>
#include <assert.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <qfileinfo.h>

static char  G50_KEY[] = "A8A423B9F55E63C1"; // garmin network key

static char sendack1[] = "4402320499990000";
//                        44                message from pc?
//                        ..02              message type
//                        ....32            don't know
//                        ......04          don't know
//                        ........9999      my id
//                        ............0000  maybe id is 4 bytes
static char sendack2[] = "4404010099990000";
//                        ..04              message type
//                        ....01            don't know
//                        ......00          don't know
static char sendack3[] = "4406010000000000";
//                        ..06              message type
//                        ....01            don't know
static char sendack4[] = "4403000000000000";
//                        ..03              message type, go to idle
//                        ....01            don't know

ant::ant ()
{
        qFile = false;
        commenabled = 1;
        rfn = cfn = 0;
}

ant::~ant ()
{
        if (qFile)
           device.close ();

        qFile = false;
        rfn = cfn = 0;
}

/*
 * Set the device and store it inside the class
 */
bool ant::setDevice (const QFile &fl)
{
        if (qFile)
        {
           device.close();
           qFile = false;
        }

        device.setFileName (fl.fileName ());

        if (!device.exists ())
        {
           KMessageBox::error (0, i18n("Device %1 does not exist!").arg(fl.fileName()));
           return false;
        }

        if (!device.open (QIODevice::ReadWrite))
        {
           KMessageBox::error (0, i18n("Error opening device %1.").arg(fl.fileName()),
                device.errorString());
           return false;
        }

        qFile = true;
        return true;
}

bool ant::setDevice (const QString &fl)
{
QFile f;

        if (qFile)
        {
           device.close ();
           qFile = false;
        }

        f.setFileName (fl);
        return setDevice (f);
}

/*
 * Send a message to the opened device.
 */
bool ant::msg_send(uchar mesg, uchar *inbuf, uchar len)
{
uchar buf[MAXMSG];
ssize_t nw;
int i;
uchar chk = MESG_TX_SYNC;

        buf[0] = MESG_TX_SYNC;
        buf[1] = len;
        chk ^= len;
        buf[2] = mesg;
        chk ^= mesg;
        
        for (i = 0; i < len; i++)
        {
           buf[3+i] = inbuf[i];
           chk ^= inbuf[i];
        }
        
        buf[3+i] = chk;
        usleep (10 * 1000);

        if ((4 + i) != (nw = write(device.handle(), buf, 4 + i)))
        {
           KMessageBox::error (0, i18n("Error writing to device %1").arg(device.fileName()));
           return false;
        }

        return true;
}

// two argument send
bool ant::msg_send(uchar mesg, uchar data1, uchar data2)
{
uchar buf[2];

        buf[0] = data1;
        buf[1] = data2;
        return msg_send(mesg, buf, 2);
}

// three argument send
bool ant::msg_send(uchar mesg, uchar data1, uchar data2, uchar data3)
{
uchar buf[3];

        buf[0] = data1;
        buf[1] = data2;
        buf[2] = data3;
        return msg_send(mesg, buf, 3);
}

bool ant::commfn ()
{
fd_set readfds, writefds, exceptfds;
int ready, fd;
struct timeval to;

        fd = device.handle ();

        for(;;)
        {
           FD_ZERO(&readfds);
           FD_ZERO(&writefds);
           FD_ZERO(&exceptfds);
           FD_SET(fd, &readfds);
           to.tv_sec = 1;
           to.tv_usec = 0;
           ready = select(fd+1, &readfds, &writefds, &exceptfds, &to);

           if (ready)
           {
              if (!get_data(fd))
                 return false;
           }
        }

        return true;
}

bool ant::get_data(int fd)
{
static uchar buf[500];
static int bufc = 0;
int nr;
int dlen;
int i;
int j;
unsigned char chk = 0;
uchar event;
int found;
int srch;
int next;

        nr = read(fd, buf+bufc, 20);
        
        if (nr > 0)
           bufc += nr;
        else
           return true;

        if (bufc > 300)
        {
           KMessageBox::error (0, i18n(">>buf<< too long!"));
           return false;
        }

        // some data in buf
        // search for possible valid messages
        srch = 0;
        
        while (srch < bufc)
        {
           found = 0;

           for (i = srch; i < bufc; i++)
           {
              if (buf[i] == MESG_TX_SYNC)
              {
                 if (i+1 < bufc && buf[i+1] >= 1 && buf[i+1] <= 13)
                 {
                    dlen = buf[i+1];

                    if ((i + 3 + dlen) < bufc)
                    {
                       chk = 0;

                       for (j = i; j <= i+3+dlen; j++)
                          chk ^= buf[j];

                       if (0 == chk)
                       {
                          found = 1; // got a valid message
                          break;
                       }
                       else
                          fprintf (stderr, "bad chk %02x\n", chk);
                    }
                 }
              }
           }

           if (found)
           {
              next = j;
              // got a valid message, see if any data needs to be discarded
              event = 0;

              switch (buf[i+2])
              {
                 case MESG_RESPONSE_EVENT_ID:
                    if (rfn)
                    {
                       memcpy(rbufp, buf+i+3, dlen);

                       if (!(this->*rfn)(buf[i+3], buf[i+5]))
                          return false;
                    }
                 break;

                 case MESG_BROADCAST_DATA_ID:
                    event = EVENT_RX_BROADCAST;
                 break;

                 case MESG_ACKNOWLEDGED_DATA_ID:
                    event = EVENT_RX_ACKNOWLEDGED;
                 break;

                 case MESG_BURST_DATA_ID:
                    event = EVENT_RX_BURST_PACKET;
                 break;

                 case MESG_EXT_BROADCAST_DATA_ID:
                    event = EVENT_RX_EXT_BROADCAST;
                 break;

                 case MESG_EXT_ACKNOWLEDGED_DATA_ID:
                    event = EVENT_RX_EXT_ACKNOWLEDGED;
                 break;

                 case MESG_EXT_BURST_DATA_ID:
                    event = EVENT_RX_EXT_BURST_PACKET;
                 break;

                 default:
                    if (rfn)
                    {
                       // should be this according to the docs, but doesn't fit
                       if (dlen > MESG_DATA_SIZE)
                       {
                          KMessageBox::error(0, i18n("rresponse buffer too small!"));
                          return false;
                       }

                       memcpy(rbufp, buf+i+3, dlen);

                       if (!(this->*rfn)(buf[i+3], buf[i+2]))
                          return false;
                    }
              }

              if (event)
              {
                 if (cfn)
                 {
                    if (dlen > MESG_DATA_SIZE)
                    {
                       KMessageBox::error(0, i18n("cresponse buffer too small!"));
                       return false;
                    }

                    memcpy(cbufp, buf+i+4, dlen);

                    if (!(this->*cfn)(buf[i+3], event))
                       return false;
                 }
              }

              srch = next;
           }
           else
              break;
        }

        if (next < bufc)
        {
           memmove(buf, buf+next, bufc-next);
           bufc -= next;
        }
        else
           bufc = 0;

        return true;
}

bool ant::ANT_ResetSystem()
{
uchar filler = 0;

        return msg_send(MESG_SYSTEM_RESET_ID, &filler, 1);
}

bool ant::ANT_Cmd55(uchar chan)
{
        return msg_send(0x55, &chan, 1);
}

bool ant::ANT_OpenRxScanMode (uchar chan)
{
        return msg_send(MESG_OPEN_RX_SCAN_ID, &chan, 1);
}

bool ant::ANT_Init ()
{
//char dev[40];
struct termios tp;

        if (!qFile)
           return false;

        if (tcgetattr(device.handle(), &tp) < 0)
        {
           KMessageBox::error(0, i18n("Error getting terminal attributes!"));
           return false;
        }

        tp.c_iflag &=
        ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|INPCK|IUCLC);
        tp.c_oflag &= ~OPOST;
        tp.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN|ECHOE);
        tp.c_cflag &= ~(CSIZE|PARENB);
        tp.c_cflag |= CS8 | CLOCAL | CREAD | CRTSCTS;

        if (cfsetispeed(&tp, B115200) < 0)
        {
           KMessageBox::error(0, i18n("Error setting input speed of line terminal!"));
           return false;
        }

        if (cfsetospeed(&tp, B115200) < 0)
        {
           KMessageBox::error(0, i18n("Error setting output speed of line terminal!"));
           return false;
        }

        tp.c_cc[VMIN] = 1;
        tp.c_cc[VTIME] = 0;

        if (tcsetattr(device.handle (), TCSANOW, &tp) < 0)
        {
           KMessageBox::error(0, i18n("Error setting terminal attributes!"));
           return false;
        }

//      return commfn ();
        return true;
}

bool ant::ANT_RequestMessage (uchar chan, uchar mesg)
{
        return msg_send(MESG_REQUEST_ID, chan, mesg);
}

bool ant::ANT_SetNetworkKeya (uchar net, uchar *key)
{
uchar buf[9];
int i;

        if (strlen((char *)key) != 16)
        {
          fprintf(stderr, "Bad key length %s\n", key);
          return 0;
        }

        buf[0] = net;

        for (i = 0; i < 8; i++)
           buf[1+i] = hexval(key[i*2])*16+hexval(key[i*2+1]);

        return msg_send(MESG_NETWORK_KEY_ID, buf, 9);
}

bool ant::ANT_SetNetworkKey (uchar net, uchar *key)
{
uchar buf[9];
//int i;

        buf[0] = net;
        memcpy(buf+1, key, 8);
        return msg_send(MESG_NETWORK_KEY_ID, buf, 9);
}

bool ant::ANT_AssignChannel (uchar chan, uchar chtype, uchar net)
{
        return msg_send(MESG_ASSIGN_CHANNEL_ID, chan, chtype, net);
}

bool ant::ANT_UnAssignChannel (uchar chan)
{
        return msg_send(MESG_UNASSIGN_CHANNEL_ID, &chan, 1);
}

bool ant::ANT_SetChannelId (uchar chan, ushort dev, uchar devtype, uchar manid)
{
uchar buf[5];

        buf[0] = chan;
        buf[1] = dev%256;
        buf[2] = dev/256;
        buf[3] = devtype;
        buf[4] = manid;
        return msg_send(MESG_CHANNEL_ID_ID, buf, 5);
}

bool ant::ANT_SetChannelRFFreq (uchar chan, uchar freq)
{
        return msg_send(MESG_CHANNEL_RADIO_FREQ_ID, chan, freq);
}

bool ant::ANT_SetChannelPeriod (uchar chan, ushort period)
{
uchar buf[3];

        buf[0] = chan;
        buf[1] = period%256;
        buf[2] = period/256;
        return msg_send(MESG_CHANNEL_MESG_PERIOD_ID, buf, 3);
}

bool ant::ANT_SetChannelSearchTimeout (uchar chan, uchar timeout)
{
        return msg_send(MESG_CHANNEL_SEARCH_TIMEOUT_ID, chan, timeout);
}

bool ant::ANT_SetSearchWaveform (uchar chan, ushort waveform)
{
uchar buf[3];

        buf[0] = chan;
        buf[1] = waveform % 256;
        buf[2] = waveform / 256;
        return msg_send(MESG_SEARCH_WAVEFORM_ID, buf, 3);
}

bool ant::ANT_SendAcknowledgedDataA (uchar chan, uchar *data) // ascii version
{
uchar buf[9];
int i;

        if (strlen((char *)data) != 16)
        {
           fprintf(stderr, "Bad data length %s\n", data);
           return false;
        }

        buf[0] = chan;

        for (i = 0; i < 8; i++)
           buf[1+i] = hexval (data[i*2]) * 16 + hexval(data[i*2+1]);

        return msg_send(MESG_ACKNOWLEDGED_DATA_ID, buf, 9);
}

bool ant::ANT_SendAcknowledgedData (uchar chan, uchar *data)
{
uchar buf[9];
//int i;

        buf[0] = chan;
        memcpy(buf+1, data, 8);
        return msg_send(MESG_ACKNOWLEDGED_DATA_ID, buf, 9);
}

unsigned short ant::ANT_SendBurstTransferA(uchar chan, uchar *data, unsigned short numpkts)
{
uchar buf[9];
int i;
int j;
int seq = 0;

        fprintf(stderr, "numpkts %d data %s\n", numpkts, data);

        if (strlen((char *)data) != (16 * numpkts))
        {
           fprintf(stderr, "Bad data length %s numpkts %d\n", data, numpkts);
           return 0;
        }

        for (j = 0; j < numpkts; j++)
        {
           buf[0] = chan | (seq << 5) | ((j == (numpkts - 1)) ? 0x80 : 0);

           for (i = 0; i < 8; i++)
              buf[1+i] = hexval (data[j*16+i*2]) * 16 + hexval (data[j*16+i*2+1]);
        
           usleep(20 * 1000);
           msg_send(MESG_BURST_DATA_ID, buf, 9);
           seq++;

           if (seq > 3)
              seq = 1;
        }

        return numpkts;
}

unsigned short ant::ANT_SendBurstTransfer (uchar chan, uchar *data, unsigned short numpkts)
{
uchar buf[9];
//int i;
int j;
int seq = 0;

        for (j = 0; j < numpkts; j++)
        {
           buf[0] = chan | (seq << 5) | ((j == (numpkts - 1)) ? 0x80 : 0);
           memcpy (buf+1, data+j*8, 8);
           usleep (20*1000);
           msg_send (MESG_BURST_DATA_ID, buf, 9);
           seq++;

           if (seq > 3)
              seq = 1;
        }

        return numpkts;
}

bool ant::ANT_OpenChannel (uchar chan)
{
        return msg_send(MESG_OPEN_CHANNEL_ID, &chan, 1);
}

bool ant::ANT_CloseChannel (uchar chan)
{
        return msg_send(MESG_CLOSE_CHANNEL_ID, &chan, 1);
}

void ant::ANT_AssignResponseFunction (RESPONSE_FUNC rf, uchar* rbuf)
{
        rfn = rf;
        rbufp = rbuf;
}

void ant::ANT_AssignChannelEventFunction (uchar, CHANNEL_EVENT_FUNC rf, uchar* rbuf)
{
        cfn = rf;
        cbufp = rbuf;
}

int ant::ANT_fd()
{
        return device.handle();
}

/*
 * Following functions control the device and uses the class "ant".
 * This is class "gant".
 */

gant::gant (const QString &af)
{
        mydev = 0;
        peerdev = 0;
        myid = 0;
        state = 0;
        authfd = false;

        faf.setFileName (af);

        if (!faf.open (QIODevice::ReadWrite))
        {
           KMessageBox::error (0, i18n("Error opening device %1.").arg(af),
                faf.errorString());
        }

        authfile = af;
        authfd = true;
        tmpOut.setFileName(QString(tmpnam(NULL)));
}

gant::~gant ()
{
        if (authfd)
           faf.close();

        // if the file is still open, this function closes it before
        // it is removed.
        tmpOut.remove();
}

uint gant::randno ()
{
        return (uint)random ();
}

bool gant::chevent (uchar chan, uchar event)
{
uchar seq;
uchar last;
uchar status = cbuf[1];
uchar phase = cbuf[2];
struct ack_msg ack;
struct auth_msg auth;
struct pair_msg pair;
uint id;
//int i;

        //fprintf(stderr, "state %d\n", state);
/*      if (dbg && event != EVENT_RX_BURST_PACKET) {
                fprintf(stderr, "chan %d event %02x channel open: ", chan, event);
                for (i = 0; i < 8; i++)
                        fprintf(stderr, "%02x", cbuf[i]);
                fprintf(stderr, "\n");
        }
*/
        // transition between garmin phases handled via state machine
        switch (event)
        {
           case EVENT_RX_BROADCAST:
              memcpy((void *)&id, cbuf+4, 4);

              if (state == 1 && (phase & 7) == 0)
              {
                 // received broadcast from watch in phase 0
                 state++;

                 if ((status & 8) == 8)
                 {
                    // pairing
                    myid = randno();
                    fprintf(stderr, "pairing, generated id %08x\n", myid);
                 }
                 else
                 {
                    // garmin moved to phase 1 in response to sendack1
                    int nr;
//                  printf("reading auth data from %s\n", authfile);
                    authfd = open(authfile.toAscii(), O_RDONLY);

                    if (!authfd || faf.size () < 32)
                    {
                       KMessageBox::error(0, i18n("There is no authfile available or it has wrong size!"));
                       return false;
                    }

                    nr = read(faf.handle(), authdata, 32);

                    if (nr != 32)
                    {
                       KMessageBox::error(0, i18n ("Error reading auth file %1!").arg(authfile));
                       return false;
                    }

                    memcpy((void *)&myauth1, authdata+16, 4);
                    memcpy((void *)&myauth2, authdata+20, 4);
                    memcpy((void *)&mydev, authdata+12, 4);
                    memcpy((void *)&myid, authdata+4, 4);
                 }

                 if (status & 0x20)
                    fprintf(stderr, "watch contains NEW data\n");

                 ANT_RequestMessage(chan, MESG_CHANNEL_ID_ID); /* request sender id */
              }
              else if (state == 3 && phase == 1)
              {
                 state++;
                 ack.code = 0x44;
                 ack.atype = 4;
                 ack.c1 = 0x01;
                 ack.c2 = 0x00;
                 ack.id = myid;
                 ANT_SendAcknowledgedData(chan, (uchar *)&ack); // tell garmin to send id data
              }
              else if (state == 5 && phase == 1)
              {
                 if ((status & 0x0f) == 4)
                 {
                    // received id/version or whatever
                    state++;
                    assert(sizeof auth == AUTHSIZE);
                    auth.code = 0x44;
                    auth.atype = 4;
                    auth.phase = 3;
                    auth.u1 = 8;
                    auth.id = myid;
                    auth.auth1 = myauth1;
                    auth.auth2 = myauth2;
                    auth.fill1 = auth.fill2 = 0;
                    ANT_SendBurstTransfer(chan, (uchar *)&auth, (sizeof auth)/8); // send our auth data
                 }
                 else if ((status & 0x0f) == 0x0c)
                 {
                     // pairing
                     assert(sizeof pair == PAIRSIZE);
                     state = 10;
                     pair.code = 0x44;
                     pair.atype = 4;
                     pair.phase = 2;
                     pair.u1 = 7;
                     pair.id = myid;
                     bzero(pair.devname, sizeof pair.devname);

                    if (peerdev <= 99999999) // only allow 8 digits
                       sprintf(pair.devname, "%d", peerdev);
                    else
                       fprintf(stderr, "pair dev name too large %08x \"%d\"\n", peerdev, peerdev);

//                  printf("sending pair data for dev %s\n", pair.devname);
                    ANT_SendBurstTransfer(chan, (uchar *)&pair, (sizeof pair)/8) ; // send pair data
                 }
                 else
                 {
                    fprintf(stderr, "error in gant.cpp on line %d\n", __LINE__);
                    return false;
                 }
              }
              else if (state == 6 && (status & 0x0f) == 4 && phase == 2)
              {
                 // if our auth is ok, garmin has gone to phase 2
                 state++;
                 ack.code = 0x44;
                 ack.atype = 6;
                 ack.c1 = 0x01;
                 ack.c2 = 0x00;
                 ack.id = 0;
                 ANT_SendAcknowledgedData(chan, (uchar *)&ack); // tell garmin to start upload
              }
              else if (state == 8 && phase == 2)
              {
                 // upload finished. received broadcast that still in phase 2
                 state++;
                 ack.code = 0x44;
                 ack.atype = 3;
                 ack.c1 = 0x00;
                 ack.c2 = 0x00;
                 ack.id = 0;
                 ANT_SendAcknowledgedData(chan, (uchar *)&ack); // tell garmin we're finished
              }
              else if (state == 9 && phase == 0)
              {
                 // garmin replies it's back in phase 0
                 fprintf(stderr, "finished\n");
                 return true;
              }
              else if (state == 1 && phase != 0)
              {
                 // don't know what phase we're in. let's try and reset
                 fprintf(stderr, "resetting\n");
                 ack.code = 0x44;
                 ack.atype = 3;
                 ack.c1 = 0x00;
                 ack.c2 = 0x00;
                 ack.id = 0;
                 ANT_SendAcknowledgedData(chan, (uchar *)&ack); // tell garmin we're finished
                 sleep(1);
                 return false;
              }
              else if (state == 20 && phase == 2)
              {
                 // upload finished. tell garmin to delete logs
                 state++;
                 ack.code = 0x44;
                 ack.atype = 0x0b;
                 ack.c1 = 0x01;
                 ack.c2 = 0x00;
                 ack.id = 0;
                 ANT_SendAcknowledgedData(chan, (uchar *)&ack); // delete logs
              }
              else if (state == 21 && phase == 2)
              {
                 state = 8;
              }
           break;

           case EVENT_RX_BURST_PACKET:
              seq = (chan & 0x60) >> 5;
              last = (chan & 0x80) >> 7;
              chan &= 0x1f;

              if (state == 4)
              {
                 static int once = 0;
                 // garmin sending authentication/identification data

                 if (!once)
                    once = 1;

                 memcpy(clientid[seq], cbuf, 8);

                 if (last)
                 {
                    state++;

                    memcpy(&peerdev, clientid[1]+4, 4);

                    if (mydev != 0 && peerdev != mydev)
                    {
                       fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev);
                       return false;
                    }
                 }
              }
              else if (state == 7)
              {
                 static int once = 0;
                 int nw;

                 // garmin uploading in response to sendack3
                 // in this state we're receiving the workout data
                 if (!once)
                 {
                    fprintf(stderr, "receiving\n");
                    once = 1;
                    tmpOut.open (QIODevice::ReadWrite | QIODevice::Truncate);
                 }

                 // FIXME: Here we should fill our buffer, instead of writing
                 // everything into a file.
                 nw = write(tmpOut.handle(), cbuf, 8);

                 if (nw != 8)
                 {
                    KMessageBox::error(0, i18n ("Failed to write to a temporary file!"));
                    return false;
                 }

                 if (last)
                 {
                    state++;    // just to exit
                                //state = 20; // to delete logs
                    tmpOut.close();
                 }
              }
              else if (state == 10)
              {
                 // receiving auth data
                 static int once = 0;
                 int nw;

                 if (!once)
                 {
//                  printf("storing auth data in %s\n", authfile);
                    once = 1;
//                  authfd = open(authfile, O_WRONLY|O_CREAT, 0644);

/*                  if (authfd < 0)
                    {
                       perror(authfile);
                       exit(1);
                    } */
                 }

                 nw = write(faf.handle(), cbuf, 8);

                 if (nw != 8)
                 {
                    KMessageBox::error(0, i18n ("Write to auth file %1 failed!").arg(authfile));
                    return false;
                 }

                 if (last)
                 {
                    //state = 6; // to download during pairing
                    state = 8; // to finish at end of pairing
                 }
              }
              else if (state == 6)
              {
                 // response to authentication
                 static int once = 0;
//               int i;

                 if (!once)
                 {
                    once = 1;
                 }

                 if (last)
                 {
                    if (cbuf[2] == 2)
                    {
                       fprintf(stderr, "authentication failed\n");
                       return false;
                    }
                 }
              }
              else
              {
                 int i;

                 fprintf(stderr, "data in state %d: ", state);

                 for (i = 0; i < 8; i++)
                    fprintf(stderr, "%02x", cbuf[i]);

                 fprintf(stderr, "\n");
              }
           break;
        }

        return 1;
}

bool gant::revent (uchar chan, uchar event)
{
unsigned short devid;
struct ack_msg ack;
int i;

        switch (event)
        {
           case EVENT_TRANSFER_TX_COMPLETED:
              // ignore
              // printf("Transfer complete %02x\n", ebuf[1]);
           break;

           case INVALID_MESSAGE:
              fprintf(stderr, "Invalid message %02x\n", ebuf[1]);
           break;

           case RESPONSE_NO_ERROR:
              switch (ebuf[1])
              {
                 case MESG_ASSIGN_CHANNEL_ID:
                    ANT_AssignChannelEventFunction((uchar)chan, (CHANNEL_EVENT_FUNC)&gant::chevent, (uchar *)&cbuf);
                 break;

                 case MESG_OPEN_CHANNEL_ID:
                    state++;
                    fprintf(stderr, "channel open (%d), waiting for broadcast\n", state);
                 break;

                 default:
                    // ignore
                    // printf("Message %02x NO_ERROR\n", ebuf[1]);
                 break;
              }
           break;

           case MESG_CHANNEL_ID_ID:
              devid = ebuf[1]+ebuf[2]*256;

              if (mydev == 0 || devid == mydev%65536)
              {
                 state++;
                 ack.code = 0x44; ack.atype = 2; ack.c1 = 0x32; ack.c2 = 0x04;
                 ack.id = myid;
                 ANT_SendAcknowledgedData(chan, (uchar *)&ack); // tell watch go to phase 1
              }
              else
              {
                 fprintf(stderr, "Ignoring unknown device %08x, mydev %08x\n", devid, mydev);
              }
           break;

           case MESG_NETWORK_KEY_ID:
           case MESG_SEARCH_WAVEFORM_ID:
           case MESG_OPEN_CHANNEL_ID:
              fprintf(stderr, "response event %02x code %02x\n", event, ebuf[2]);

              for (i = 0; i < 8; i++)
                 fprintf(stderr, "%02x", ebuf[i]);

              fprintf(stderr, "\n");
           break;

           case MESG_CAPABILITIES_ID:
           break;

           case MESG_CHANNEL_STATUS_ID:
           break;

           default:
              fprintf(stderr, "Unhandled response event %02x\n", event);
           break;
        }

        return true;
}

bool gant::read_device ()
{
//int devnum = 0;
int chan = 0;
int net = 0;
int chtype = 0; // wildcard
int devno = 0; // wildcard
int devtype = 0; // wildcard
int manid = 0; // wildcard
int freq = 0x32; // garmin specific radio frequency
int period = 0x1000; // garmin specific broadcast period
int srchto = 255; // max timeout
int waveform = 0x0053; // aids search somehow

        // Check if we already have a device set.
//      if (!qFile)
//         return false;

        // TODO: Do this properly...
/*      if (ac > 1) {
                authfile = av[1];
        }
        else {
                fprintf(stderr, "need auth file\n");
                exit(1);
        }
                
        if (ac > 2)
                fn = av[2]; // store the output filename for event function
        if (ac > 3)
                devnum = atoi(av[3]);
        if (ac > 4)
                myid = atoi(av[4]);
        if (ac > 5)
                mydev = atoi(av[5]);
*/
        if (!ANT_Init())
           return false;

        if (!ANT_ResetSystem())
           return false;

        ANT_AssignResponseFunction((RESPONSE_FUNC)&gant::revent, (uchar *)&ebuf);

        if (!ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID))  //informative
           return false;

        if (!ANT_SetNetworkKeya(net, (uchar *)G50_KEY))
           return false;

        if (!ANT_AssignChannel(chan, chtype, net))
           return false;

        if (!ANT_SetChannelId(chan, devno, devtype, manid))
           return false;

        if (!ANT_RequestMessage(chan, MESG_CAPABILITIES_ID))    //informative
           return false;

        if (!ANT_SetChannelRFFreq(chan, freq))
           return false;

        if (!ANT_SetChannelPeriod(chan, period))
           return false;

        if (!ANT_SetChannelSearchTimeout(chan, srchto))
           return false;

        if (!ANT_SetSearchWaveform(chan, waveform))
           return false;

        if (!ANT_OpenChannel(chan))     // success for this bumps us to state 1
           return false;

        if (!ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID))  //informative
           return false;

        // everything handled in event functions
//      for(;;)
//              sleep(10);

        return commfn ();
}

uchar *gant::getBuffer (int *size)
{
QFileInfo fi;
uchar *buffer;
long sz;

        fi.setFile (tmpOut);
        
        if (!fi.exists() || fi.size() > 1000000)
           return 0;

        sz = fi.size();
        *size = 0;

        if (!(buffer = new uchar[sz+1]))
        {
           KMessageBox::error(0, i18n("Error allocating %1 bytes of memory!").arg(sz+1));
           return 0;
        }

        if (tmpOut.handle() == -1)
        {
           if (!tmpOut.open(QIODevice::ReadOnly))
           {
              KMessageBox::error(0, i18n("Error opening a temporary file for reading!"), tmpOut.errorString());
              delete buffer;
              return 0;
           }
        }

        if (read (tmpOut.handle(), buffer, sz) < sz)
        {
           KMessageBox::error(0, i18n("Error reading from a temporary file!"));
           tmpOut.close();
           delete buffer;
           return 0;
        }

        *size = (int)sz;
        return buffer;
}