From: Jordan Miller Date: Mon, 23 Mar 2009 03:15:07 +0000 (-0500) Subject: First Import X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a80a2e2c7937e2638db1c12e66f43473eec213dc;p=gant First Import --- a80a2e2c7937e2638db1c12e66f43473eec213dc diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..439bd9f --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CFLAGS=-g -Werror +LDFLAGS=-lpthread -lm + +all: gant + +gant: gant.o antlib.o + +gant.o: gant.c antdefs.h + +antlib.o: antlib.c antdefs.h + +clean: + rm *.o gant diff --git a/README b/README new file mode 100644 index 0000000..c258f23 --- /dev/null +++ b/README @@ -0,0 +1,32 @@ +Test version of 405 download +Needs lots of work! + +Usage: + +Put 405 in pairing mode +./gant -f paul -a auth405 + +This gives your garmin a name of "paul" and an auth file of "auth405" +The 405 will ask you if you wish to pair with "paul". Obviously +you might want to use a different name :) + +Download with: +./gant -nza auth405 > output + +or -Dnza if you want lots of debug output + +It doesn't seem to be very reliable at starting the download and +I haven't investigated why yet. So, if it doesn't start downloading +press ctrl-c and try again. I find it helps to take the watch out +of power save mode. + +If you've already downloaded the data, then you'll need to set +"force send" to yes on the watch. + +Compare output with .TCX file produce by windows ANT Agent. + +Patch gant.c +Send me patches! + +Paul + diff --git a/antdefs.h b/antdefs.h new file mode 100644 index 0000000..b258438 --- /dev/null +++ b/antdefs.h @@ -0,0 +1,44 @@ +// copyright 2008 paul@ant.sbrk.co.uk. released under GPLv3 +// vers 0.4t +typedef unsigned char uchar; +typedef uchar (*RESPONSE_FUNC)(uchar chan, uchar msgid); +typedef uchar (*CHANNEL_EVENT_FUNC)(uchar chan, uchar event); + +#define EVENT_RX_ACKNOWLEDGED 0x9b +#define EVENT_RX_BROADCAST 0x9a +#define EVENT_RX_BURST_PACKET 0x9c +#define EVENT_RX_EXT_ACKNOWLEDGED 0x9e +#define EVENT_RX_EXT_BROADCAST 0x9d +#define EVENT_RX_EXT_BURST_PACKET 0x9f +#define EVENT_RX_FAKE_BURST 0xdd +#define EVENT_TRANSFER_TX_COMPLETED 0x05 +#define INVALID_MESSAGE 0x28 +#define MESG_ACKNOWLEDGED_DATA_ID 0x4f +#define MESG_ASSIGN_CHANNEL_ID 0x42 +#define MESG_BROADCAST_DATA_ID 0x4e +#define MESG_BURST_DATA_ID 0x50 +#define MESG_CAPABILITIES_ID 0x54 +#define MESG_CHANNEL_ID_ID 0x51 +#define MESG_CHANNEL_MESG_PERIOD_ID 0x43 +#define MESG_CHANNEL_RADIO_FREQ_ID 0x45 +#define MESG_CHANNEL_SEARCH_TIMEOUT_ID 0x44 +#define MESG_CHANNEL_STATUS_ID 0x52 +#define MESG_CLOSE_CHANNEL_ID 0x4c +#define MESG_DATA_SIZE 13 // due to dodgy msg from suunto +#define MESG_EXT_ACKNOWLEDGED_DATA_ID 0x5e +#define MESG_EXT_BROADCAST_DATA_ID 0x5d +#define MESG_EXT_BURST_DATA_ID 0x5f +#define MESG_NETWORK_KEY_ID 0x46 +#define MESG_OPEN_CHANNEL_ID 0x4b +#define MESG_OPEN_RX_SCAN_ID 0x5b +#define MESG_REQUEST_ID 0x4d +#define MESG_RESPONSE_EVENT_ID 0x40 +#define MESG_RESPONSE_EVENT_SIZE 3 +#define MESG_SEARCH_WAVEFORM_ID 0x49 +#define MESG_SYSTEM_RESET_ID 0x4a +#define MESG_TX_SYNC 0xa4 +#define MESG_UNASSIGN_CHANNEL_ID 0x41 +#define RESPONSE_NO_ERROR 0x00 +#define EVENT_RX_FAIL 0x02 + +/* vim: se sw=8 ts=8: */ diff --git a/antlib.c b/antlib.c new file mode 100644 index 0000000..bc0f85c --- /dev/null +++ b/antlib.c @@ -0,0 +1,620 @@ +// copyright 2008 paul@ant.sbrk.co.uk. released under GPLv3 +// vers 0.4t +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __declspec(X) + +#include "antdefs.h" +//#include "antdefines.h" +//#include "ANT_Interface.h" +//#include "antmessage.h" +//#include "anttypes.h" + +#define S(e) if (-1 == (e)) {perror(#e);exit(1);} else + +#define MAXMSG 30 // SYNC,LEN,MSG,data[9+],CHKSUM +#define MAXCHAN 32 +#define BSIZE 8*10000 + +#define uchar unsigned char + +#define hexval(c) ((c >= '0' && c <= '9') ? (c-'0') : ((c&0xdf)-'A'+10)) + +static int fd = -1; +static int dbg = 0; +static pthread_t commthread;; +static int commenabled = 1; + +static RESPONSE_FUNC rfn = 0; +static uchar *rbufp; +static CHANNEL_EVENT_FUNC cfn = 0; +static uchar *cbufp; + +static uchar *blast; +static int blsize; + +struct msg_queue { + uchar msgid; + uchar len; +}; + +// send message over serial port +uchar +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(fd, buf, 4+i))) { + if (dbg) { + perror("failed write"); + } + } + return 1; +} + +// two argument send +uchar +msg_send2(uchar mesg, uchar data1, uchar data2) +{ + uchar buf[2]; + buf[0] = data1; + buf[1] = data2; + return msg_send(mesg, buf, 2); +} + +// three argument send +uchar +msg_send3(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); +} + +void *commfn(void* arg) +{ + fd_set readfds, writefds, exceptfds; + int ready; + struct timeval to; + + 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) { + get_data(fd); + } + } +} + +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; + if (bufc > 30) { + if (dbg) + fprintf(stderr, "bufc %d\n", bufc); + } + if (bufc > 300) { + fprintf(stderr, "buf too long %d\n", bufc); + for (j = 0; j < bufc; j++) + fprintf(stderr, "%02x", buf[j]); + fprintf(stderr, "\n"); + exit(1); + } + + // some data in buf + // search for possible valid messages + srch = 0; + while (srch < bufc) { + found = 0; + //printf("srch %d bufc %d\n", srch, bufc); + for (i = srch; i < bufc; i++) { + if (buf[i] == MESG_TX_SYNC) { + //fprintf(stderr, "bufc %d sync %d\n", bufc, i); + 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); + for (j = i; j <= i+3+dlen; j++) + fprintf(stderr, "%02x", buf[j]); + fprintf(stderr, "\n"); + } + } + } + } + } + if (found) { + next = j; + //printf("next %d %02x\n", next, buf[j-1]); + // got a valid message, see if any data needs to be discarded + if (i > srch) { + fprintf(stderr, "\nDiscarding: "); + for (j = 0; j < i; j++) + fprintf(stderr, "%02x", buf[j]); + fprintf(stderr, "\n"); + } + + if (dbg) { + fprintf(stderr, "data: "); + for(j = i; j < i+dlen+4; j++) { + fprintf(stderr, "%02x", buf[j]); + } + fprintf(stderr, "\n"); + } + event = 0; + switch (buf[i+2]) { + case MESG_RESPONSE_EVENT_ID: + //if (cfn) { + // memcpy(cbufp, buf+i+4, dlen); + // (*cfn)(buf[i+3], buf[i+5]); + // else + if (rfn) { + memcpy(rbufp, buf+i+3, dlen); + (*rfn)(buf[i+3], buf[i+5]); + } else { + if (dbg) + fprintf(stderr, "no rfn or cfn\n"); + } + 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; + // coalesce these and generate a fake event on last packet + // in case client wishes to ignore these events and capture the fake one + { + static uchar *burstbuf[MAXCHAN]; + static int bused[MAXCHAN]; + static int lseq[MAXCHAN]; + int k; + + uchar seq; + uchar last; + uchar chan = *(buf+i+3); + if (dbg) { + fprintf(stderr, "burst %02x ", chan); + for (k = 0; k < 12; k++) + fprintf(stderr, "%02x", buf[i+k]); + fprintf(stderr, "\n"); + } + seq = (chan & 0x60) >> 5; + last = (chan & 0x80) >> 7; + chan &= 0x1f; + if (dbg) fprintf(stderr, "ch %x seq %d last %d\n", chan, seq, last); + if (!burstbuf[chan]) { + if (seq != 0) + fprintf(stderr, "out of sequence ch# %d %d\n", chan, seq); + else { + burstbuf[chan] = malloc(BSIZE); + bzero(burstbuf[chan], BSIZE); + memcpy(burstbuf[chan], buf+i+4, 8); + bused[chan] = 8; + lseq[chan] = seq; + if (dbg) + fprintf(stderr, "init ch# %d %d\n", chan, lseq[chan]); + } + } else { + if (lseq[chan]+1 != seq) { + fprintf(stderr, "out of sequence ch# %d %d l %d\n", chan, seq, lseq[chan]); + free(burstbuf[chan]); + burstbuf[chan] = 0; + if (seq == 0) { + burstbuf[chan] = malloc(BSIZE); + bzero(burstbuf[chan], BSIZE); + memcpy(burstbuf[chan], buf+i+4, 8); + bused[chan] = 8; + lseq[chan] = seq; + fprintf(stderr, "reinit ch# %d %d\n", chan, lseq); + } + } else { + if ((bused[chan] % BSIZE) == 0) { + burstbuf[chan] = realloc(burstbuf[chan], bused[chan]+BSIZE); + bzero(burstbuf[chan]+bused[chan], BSIZE); + } + memcpy(burstbuf[chan]+bused[chan], buf+i+4, 8); + bused[chan] += 8; + if (dbg) fprintf(stderr, "seq0 %d lseq %d\n", seq, lseq[chan]); + if (seq == 3) + lseq[chan] = 0; + else + lseq[chan] = seq; + if (dbg) fprintf(stderr, "seq1 %d lseq %d\n", seq, lseq[chan]); + } + } + if (burstbuf[chan] && last) { + blast = burstbuf[chan]; + blsize = bused[chan]; + if (dbg) fprintf(stderr, "BU %d %lx\n", blsize, blast); + if (dbg) { + fprintf(stderr, "bused ch# %d %d\n", chan, bused[chan]); + for (k = 0; k < bused[chan]; k++) + fprintf(stderr, "%02x", burstbuf[chan][k]); + fprintf(stderr, "\n"); + } + bused[chan] = 0; + burstbuf[chan] = 0; + } + } + 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_RESPONSE_EVENT_SIZE) { + if (dlen > MESG_DATA_SIZE) { + fprintf(stderr, "cresponse buffer too small %d > %d\n", dlen, MESG_DATA_SIZE); + for (j = 0; j < dlen; j++) + fprintf(stderr, "%02x", *(buf+i+3+j)); + fprintf(stderr, "\n"); + exit(1); + } + memcpy(rbufp, buf+i+3, dlen); + (*rfn)(buf[i+3], buf[i+2]); + } else { + if (dbg) + fprintf(stderr, "no rfn\n"); + } + } + if (event) { + uchar chan = *(buf+3) & 0x1f; + if (cfn) { + if (dlen > MESG_DATA_SIZE) { + fprintf(stderr, "rresponse buffer too small %d > %d\n", + dlen, MESG_DATA_SIZE); + for (j = 0; j < dlen+1; j++) + fprintf(stderr, "%02x", *(buf+i+4+j)); + fprintf(stderr, "\n"); + exit(1); + } + memcpy(cbufp, buf+i+4, dlen); + if (dbg) { + fprintf(stderr, "xch0#%d %d %lx\n", chan, blsize, blast); + for (j = 0; j < blsize; j++) + fprintf(stderr, "%02x", *(blast+j)); + fprintf(stderr, "\n"); + } + (*cfn)(chan, event); + if (dbg) { + fprintf(stderr, "xch1#%d %d %lx\n", chan, blsize, blast); + for (j = 0; j < blsize; j++) + fprintf(stderr, "%02x", *(blast+j)); + fprintf(stderr, "\n"); + } + // FAKE BURST message + if (event == EVENT_RX_BURST_PACKET && blast && blsize) { + if (dbg) { + fprintf(stderr, "Fake burst ch#%d %d %lx\n", chan, blsize, blast); + for (j = 0; j < blsize; j++) + fprintf(stderr, "%02x", *(blast+j)); + fprintf(stderr, "\n"); + } + *(int *)(cbufp+4) = blsize; + memcpy(cbufp+8, &blast, 4); + (*cfn)(chan, EVENT_RX_FAKE_BURST); + free(blast); + blast = 0; + blsize = 0; + } + } else + if (dbg) + fprintf(stderr, "no cfn\n"); + } + srch = next; + } else + break; + } + if (next < bufc) { + memmove(buf, buf+next, bufc-next); + bufc -= next; + } else + bufc = 0; +} + +uchar +ANT_ResetSystem(void) +{ + uchar filler = 0; + return msg_send(MESG_SYSTEM_RESET_ID, &filler, 1); +} + +uchar +ANT_Cmd55(uchar chan) +{ + return msg_send(0x55, &chan, 1); +} + +uchar +ANT_OpenRxScanMode(uchar chan) +{ + return msg_send(MESG_OPEN_RX_SCAN_ID, &chan, 1); +} + +uchar +ANT_Initf(char *devname, ushort baud) +{ + struct termios tp; + + fd = open(devname, O_RDWR); + if (fd < 0) { + perror(devname); + return 0; + } + + S(tcgetattr(fd, &tp)); + 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; + + S(cfsetispeed(&tp, B115200)); + S(cfsetospeed(&tp, B115200)); + tp.c_cc[VMIN] = 1; + tp.c_cc[VTIME] = 0; + S(tcsetattr(fd, TCSANOW, &tp)); + + if (pthread_create(&commthread, 0, commfn, 0)); + return 1; +} + +uchar +ANT_Init(uchar devno, ushort baud) +{ + char dev[40]; + + sprintf(dev, "/dev/ttyUSB%d", devno); + return ANT_Initf(dev, devno); +} + +uchar +ANT_RequestMessage(uchar chan, uchar mesg) +{ + return msg_send2(MESG_REQUEST_ID, chan, mesg); +} + +uchar +ANT_SetNetworkKeya(uchar net, uchar *key) +{ + uchar buf[9]; + int i; + + if (strlen(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); +} + +uchar +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); +} + +uchar +ANT_AssignChannel(uchar chan, uchar chtype, uchar net) +{ + return msg_send3(MESG_ASSIGN_CHANNEL_ID, chan, chtype, net); +} + +uchar +ANT_UnAssignChannel(uchar chan) +{ + return msg_send(MESG_UNASSIGN_CHANNEL_ID, &chan, 1); +} + +uchar +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); +} + +uchar +ANT_SetChannelRFFreq(uchar chan, uchar freq) +{ + return msg_send2(MESG_CHANNEL_RADIO_FREQ_ID, chan, freq); +} + +uchar +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); +} + +uchar +ANT_SetChannelSearchTimeout(uchar chan, uchar timeout) +{ + return msg_send2(MESG_CHANNEL_SEARCH_TIMEOUT_ID, chan, timeout); +} + +uchar +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); +} + +uchar +ANT_SendAcknowledgedDataA(uchar chan, uchar *data) // ascii version +{ + uchar buf[9]; + int i; + + if (strlen(data) != 16) { + fprintf(stderr, "Bad data length %s\n", data); + return 0; + } + 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); +} + +uchar +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); +} + +ushort +ANT_SendBurstTransferA(uchar chan, uchar *data, ushort numpkts) +{ + uchar buf[9]; + int i; + int j; + int seq = 0; + + if (dbg) fprintf(stderr, "numpkts %d data %s\n", numpkts, data); + if (strlen(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; +} + +ushort +ANT_SendBurstTransfer(uchar chan, uchar *data, ushort 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; +} + +uchar +ANT_OpenChannel(uchar chan) +{ + return msg_send(MESG_OPEN_CHANNEL_ID, &chan, 1); +} + +uchar +ANT_CloseChannel(uchar chan) +{ + return msg_send(MESG_CLOSE_CHANNEL_ID, &chan, 1); +} + +void +ANT_AssignResponseFunction(RESPONSE_FUNC rf, uchar* rbuf) +{ + rfn = rf; + rbufp = rbuf; +} + +void +ANT_AssignChannelEventFunction(uchar chan, CHANNEL_EVENT_FUNC rf, uchar* rbuf) +{ + cfn = rf; + cbufp = rbuf; +} + +int ANT_fd() +{ + return fd; +} + +// vim: se ts=2 sw=2: diff --git a/gant.c b/gant.c new file mode 100644 index 0000000..0cc41bd --- /dev/null +++ b/gant.c @@ -0,0 +1,888 @@ +// copyright 2008 paul@ant.sbrk.co.uk. released under GPLv3 +// vers 0.4t +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "antdefs.h" + +double round(double); + +int gottype; +int sentauth; +int gotwatchid; +int nopairing; +int nowriteauth; +int reset; +int dbg = 0; +int seenphase0 = 0; +int lastphase; +int sentack2; +int newfreq = 0; +int period = 0x1000; // garmin specific broadcast period +int donebind = 0; +int sentgetv; +char *fname = "garmin"; + +static char ANTSPT_KEY[] = "A8A423B9F55E63C1"; // ANT+Sport key + +static uchar ebuf[MESG_DATA_SIZE]; // response event data gets stored here +static uchar cbuf[MESG_DATA_SIZE]; // channel event data gets stored here + +int passive; +int semipassive; +int verbose; + +int downloadfinished = 0; +int downloadstarted = 0; +int sentid = 0; + +uint mydev = 0; +uint peerdev; +uint myid; +uint devid; +ulong myauth1; +ulong myauth2; +char authdata[32]; +uint pairing; +uint isa50; +uint isa405; +uint waitauth; +int nphase0; + +//char *getversion = "440dffff00000000fe00000000000000"; +//char *getgpsver = "440dffff0000000006000200ff000000"; +char *acks[] = { + "fe00000000000000", // get version - 255, 248, 253 + "0e02000000000000", // device short name (fr405a) - 525 +//"1c00020000000000", // no data + "0a0002000e000000", // unit id - 38 + "0a000200ad020000", // 4 byte something? 0x10270000 = 10000 dec - 1523 + "0a000200c6010000", // 3 x 4 ints? - 994 + "0a00020035020000", // guessing this is # trackpoints per run - 1066 + "0a00020097000000", // load of software versions - 247 + "0a000200c2010000", // download runs - 27 (#runs), 990?, 12? + "0a00020075000000", // download laps - 27 (#laps), 149 laps, 12? + "0a00020006000000", // download trackpoints - 1510/99(run marker), ..1510,12 + "0a000200ac020000", + "" +}; +int sentcmd; + +uchar clientid[3][8]; + +int authfd = -1; +char *authfile; +int outfd; // output file +char *fn = "default_output_file"; +char *progname; + +#define BSIZE 8*100 +uchar *burstbuf = 0; +uchar *blast = 0; +int blsize = 0; +int bused = 0; +int lseq = -1; + +char * +timestamp(void) +{ + struct timeval tv; + static char time[50]; + struct tm *tmp; + + gettimeofday(&tv, 0); + tmp = gmtime(&tv.tv_sec); + + sprintf(time, "%02d:%02d:%02d.%02d", + tmp->tm_hour, tmp->tm_min, tmp->tm_sec, tv.tv_usec/10000); + return time; +} + +uint +randno(void) +{ + uint r; + + int fd = open("/dev/urandom", O_RDONLY); + if (fd > 0) { + read(fd, &r, sizeof r); + close(fd); + } + return r; +} + +#pragma pack(1) +struct ack_msg { + uchar code; + uchar atype; + uchar c1; + uchar c2; + uint id; +}; + +struct auth_msg { + uchar code; + uchar atype; + uchar phase; + uchar u1; + uint id; + ulong auth1; + ulong auth2; + ulong fill1; + ulong fill2; +}; + +struct pair_msg { + uchar code; + uchar atype; + uchar phase; + uchar u1; + uint id; + char devname[16]; +}; + +#pragma pack() +#define ACKSIZE 8 // above structure must be this size +#define AUTHSIZE 24 // ditto +#define PAIRSIZE 16 + +decode(ushort bloblen, ushort pkttype, ushort pktlen, int dsize, uchar *data) +{ + int i; + int j; + int hr; + int cad; + int u1, u2; + int doff = 20; + char model[256]; + char gpsver[256]; + char devname[256]; + float alt; + float dist; + uint tv; + time_t ttv; + char tbuf[100]; + struct tm *tmp; + double lat, lon; + ushort part; + ushort ver; + uint unitid; + uint nruns; + printf("decode %d %d %d %d\n", bloblen, pkttype, pktlen, dsize); + switch (pkttype) { + case 255: + memset(model, 0, sizeof model); + memcpy(model, data+doff+4, dsize-4); + part=data[doff]+data[doff+1]*256; + ver=data[doff+2]+data[doff+3]*256; + printf("%d Part#: %d ver: %d Name: %s\n", pkttype, + part, ver, model); + break; + case 248: + memset(gpsver, 0, sizeof gpsver); + memcpy(gpsver, data+doff, dsize); + printf("%d GPSver: %s\n", pkttype, + gpsver); + break; + case 253: + printf("%d Unknown\n", pkttype); + for (i = 0; i < pktlen; i += 3) + printf("%d.%d.%d\n", data[doff+i], data[doff+i+1], data[doff+i+2]); + break; + case 525: + memset(devname, 0, sizeof devname); + memcpy(devname, data+doff, dsize); + printf("%d Devname %s\n", pkttype, devname); + break; + case 12: + case 990: + printf("%d shorts?", pkttype); + for (i = 0; i < pktlen; i += 4) + printf(" %u", data[doff+i] + data[doff+i+1]*256); + printf("\n"); + break; + case 38: + unitid = data[doff] + data[doff+1]*256 + + data[doff+2]*256*256 + data[doff+3]*256*256*256; + printf("%d unitid %u\n", pkttype, unitid); + break; + case 27: + nruns = data[doff] + data[doff+1] * 256; + printf("%d nruns %u\n", pkttype, nruns); + break; + case 1523: + case 994: + case 1066: + printf("%d ints?", pkttype); + for (i = 0; i < pktlen; i += 4) + printf(" %u", data[doff+i] + data[doff+i+1]*256 + + data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256); + printf("\n"); + break; + case 1510: + printf("%d waypoints", pkttype); + for (i = 0; i < 4 && i < pktlen; i += 4) + printf(" %u", data[doff+i] + data[doff+i+1]*256 + + data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256); + printf("\n"); + for (i = 4; i < pktlen; i += 24) { + lat = (data[doff+i] + data[doff+i+1]*256 + + data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256)*180.0/0x80000000; + lon = (data[doff+i+4] + data[doff+i+5]*256 + + data[doff+i+6]*256*256 + data[doff+i+7]*256*256*256)*180.0/0x80000000; + hr = data[doff+i+20]; + cad = data[doff+i+21]; + u1 = data[doff+i+22]; + u2 = data[doff+i+23]; + tv = (data[doff+i+8] + data[doff+i+9]*256 + + data[doff+i+10]*256*256 + data[doff+i+11]*256*256*256); + ttv = tv+631065600; // garmin epoch offset + tmp = gmtime(&ttv); + strftime(tbuf, sizeof tbuf, "%Y-%m-%dT%H:%M:%SZ", tmp); // format for printing + memcpy((void *)&alt, data+doff+i+12, 4); + memcpy((void *)&dist, data+doff+i+16, 4); + if (dbg) printf("lat %.10g lon %.10g hr %d cad %d u1 %d u2 %d tv %d %s alt %f dist %f\n", lat, lon, + hr, cad, u1, u2, tv, tbuf, alt, dist); + printf(" \n"); + printf(" \n",tbuf); + printf(" \n"); + printf(" %d.%07d\n", + (int)lat, (int)(round(10000000*fabs(lat-(int)lat)))); + printf(" %d.%07d\n", + (int)lon, (int)(round(10000000*fabs(lon-(int)lon)))); + printf(" \n"); + printf(" %.7f\n", alt+0.000000005); + printf(" %.7f\n", dist+0.000000005); + if (hr > 0) { + printf(" \n"); + printf(" %d\n", hr); + printf(" \n"); + } + printf(" %s\n", + u1 ? "Present" : "Absent"); + printf(" \n"); + if (cad != 255) { + printf(" \n"); + printf(" %d\n", cad); + printf(" \n"); + } else + printf(" \n"); + printf(" \n"); + printf(" \n"); + } + break; + default: + printf("don't know how to decode packet type %d\n", pkttype); + for (i = doff; i < dsize && i < doff+pktlen; i++) + printf("%02x", data[i]); + printf("\n"); + for (i = doff; i < dsize && i < doff+pktlen; i++) + if (isprint(data[i])) + printf("%c", data[i]); + else + printf("."); + printf("\n"); + } +} + +void +usage(void) +{ + fprintf(stderr, "Usage: %s -a authfile\n" + "[ -o outfile ]\n" + "[ -d devno ]\n" + "[ -i id ]\n" + "[ -m mydev ]\n" + "[ -p ]\n", + progname + ); + exit(1); +} + +uchar +chevent(uchar chan, uchar event) +{ + uchar seq; + uchar last; + uchar status; + uchar phase; + uint newdata; + struct ack_msg ack; + struct auth_msg auth; + struct pair_msg pair; + uint id; + int i; + uint cid; + if (dbg) printf("chevent %02x %02x\n", chan, event); + + if (event == EVENT_RX_BROADCAST) { + status = cbuf[1] & 0xd7; + newdata = cbuf[1] & 0x20; + phase = cbuf[2]; + } + cid = cbuf[4]+cbuf[5]*256+cbuf[6]*256*256+cbuf[7]*256*256*256; + memcpy((void *)&id, cbuf+4, 4); + + if (dbg) + fprintf(stderr, "cid %08x myid %08x\n", cid, myid); + 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"); + } + + switch (event) { + case EVENT_RX_BROADCAST: + lastphase = phase; // store the last phase we see the watch broadcast + if (dbg) printf("lastphase %d\n", lastphase); + if (!pairing && !nopairing) + pairing = cbuf[1] & 8; + if (!gottype) { + gottype = 1; + isa50 = cbuf[1] & 4; + isa405 = cbuf[1] & 1; + if ((isa50 && isa405) || (!isa50 && !isa405)) { + fprintf(stderr, "50 %d and 405 %d\n", isa50, isa405); + exit(1); + } + } + if (verbose) { + switch (phase) { + case 0: + fprintf(stderr, "%s BC0 %02x %d %d %d PID %d %d %d %c%c\n", + timestamp(), + cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3], + cbuf[4]+cbuf[5]*256, cbuf[6], cbuf[7], + (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' ' + ); + break; + case 1: + fprintf(stderr, "%s BC1 %02x %d %d %d CID %08x %c%c\n", + timestamp(), + cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3], cid, + (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' ' + ); + break; + fprintf(stderr, "%s BCX %02x %d %d %d PID %d %d %d %c%c\n", + timestamp(), + cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3], + cbuf[4]+cbuf[5]*256, cbuf[6], cbuf[7], + (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' ' + ); + default: + break; + } + } + + if (dbg) + printf("watch status %02x stage %d id %08x\n", status, phase, id); + + if (!sentid) { + sentid = 1; + ANT_RequestMessage(chan, MESG_CHANNEL_ID_ID); /* request sender id */ + } + + // if we don't see a phase 0 message first, reset the watch + if (reset || (phase != 0 && !seenphase0)) { + fprintf(stderr, "resetting\n"); + ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00; ack.c2 = 0x00; ack.id = 0; + ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin we're finished + sleep(1); + exit(1); + } + switch (phase) { + case 0: + seenphase0 = 1; + nphase0++; + if (nphase0 % 10 == 0) + donebind = 0; + if (newfreq) { + // switch to new frequency + ANT_SetChannelPeriod(chan, period); + ANT_SetChannelSearchTimeout(chan, 3); + ANT_SetChannelRFFreq(chan, newfreq); + newfreq = 0; + } + // phase 0 seen after reset at end of download + if (downloadfinished) { + fprintf(stderr, "finished\n"); + exit(0); + } + // generate a random id if pairing and user didn't specify one + if (pairing && !myid) { + myid = randno(); + fprintf(stderr, "pairing, using id %08x\n", myid); + } + // need id codes from auth file if not pairing + // TODO: handle multiple watches + // BUG: myauth1 should be allowed to be 0 + if (!pairing && !myauth1) { + int nr; + printf("reading auth data from %s\n", authfile); + authfd = open(authfile, O_RDONLY); + if (authfd < 0) { + perror(authfile); + fprintf(stderr, "No auth data. Need to pair first\n"); + exit(1); + } + nr = read(authfd, authdata, 32); + close(authfd); + if (nr != 32 && nr != 24) { + fprintf(stderr, "bad auth file len %d != 32 or 24\n", nr); + exit(1); + } + // BUG: auth file not portable + 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 (dbg) + fprintf(stderr, "dev %08x auth %08x %08x id %08x\n", + mydev, myauth1, myauth2, myid); + } + // bind to watch + if (!donebind && devid) { + donebind = 1; + if (isa405) + newfreq = 0x32; + ack.code = 0x44; ack.atype = 2; ack.c1 = isa50 ? 0x32 : newfreq; ack.c2 = 0x04; + ack.id = myid; + ANT_SendAcknowledgedData(chan, (void *)&ack); // bind + } else { + if (dbg) printf("donebind %d devid %x\n", donebind, devid); + } + break; + case 1: + if (dbg) printf("case 1 %x\n", peerdev); + if (peerdev) { + if (dbg) printf("case 1 peerdev\n"); + // if watch has sent id + if (mydev != 0 && peerdev != mydev) { + fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev); + } else if (!sentauth && !waitauth) { + if (dbg) printf("case 1 diffdev\n"); + 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; + sentauth = 1; + ANT_SendBurstTransfer(chan, (void *)&auth, (sizeof auth)/8); // send our auth data + } + } + if (dbg) printf("case 1 cid %x myid %x\n", cid, myid); + if (!sentack2 && cid == myid && !waitauth) { + sentack2 = 1; + if (dbg) printf("sending ack2\n"); + // if it did bind to me before someone else + ack.code = 0x44; ack.atype = 4; ack.c1 = 0x01; ack.c2 = 0x00; + ack.id = myid; + ANT_SendAcknowledgedData(chan, (void *)&ack); // request id + } + break; + case 2: + // successfully authenticated + if (!downloadstarted) { + downloadstarted = 1; + if (dbg) printf("starting download\n"); + ack.code = 0x44; ack.atype = 6; ack.c1 = 0x01; ack.c2 = 0x00; ack.id = 0; + //ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin to start upload + } + if (downloadfinished) { + if (dbg) printf("finished download\n"); + ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00; ack.c2 = 0x00; ack.id = 0; + if (!passive) ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin we're finished + } + break; + case 3: + if (pairing) { + // waiting for the user to pair + printf("Please press \"View\" on watch to confirm pairing\n"); + waitauth = 2; // next burst data is auth data + } else { + if (dbg) printf("not sure why in phase 3\n"); + if (!sentgetv) { + sentgetv = 1; + //ANT_SendBurstTransferA(chan, getversion, strlen(getversion)/16); + } + } + break; + default: + if (dbg) fprintf(stderr, "Unknown phase %d\n", phase); + break; + } + break; + case EVENT_RX_BURST_PACKET: + // now handled in coalesced burst below + if (dbg) printf("burst\n"); + break; + case EVENT_RX_FAKE_BURST: + if (dbg) printf("rxfake burst pairing %d blast %d waitauth %d\n", + pairing, blast, waitauth); + blsize = *(int *)(cbuf+4); + memcpy(&blast, cbuf+8, 4); + if (dbg) { + printf("fake burst %d %lx ", blsize, blast); + for (i = 0; i < blsize && i < 64; i++) + printf("%02x", blast[i]); + printf("\n"); + for (i = 0; i < blsize; i++) + if (isprint(blast[i])) + printf("%c", blast[i]); + else + printf("."); + printf("\n"); + } + if (sentauth) { + static int nacksent = 0; + char *ackdata; + static uchar ackpkt[100]; + // ack the last packet + ushort bloblen = blast[14]+256*blast[15]; + ushort pkttype = blast[16]+256*blast[17]; + ushort pktlen = blast[18]+256*blast[19]; + if (bloblen == 0) { + if (dbg) printf("bloblen %d, get next data\n", bloblen); + // request next set of data + ackdata = acks[nacksent++]; + if (!strcmp(ackdata, "")) { // finished + printf("acks finished, resetting\n"); + ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00; + ack.c2 = 0x00; ack.id = 0; + ANT_SendAcknowledgedData(chan, (void *)&ack); // go to idle + sleep(1); + exit(1); + } + if (dbg) printf("got type 0, sending ack %s\n", ackdata); + sprintf(ackpkt, "440dffff00000000%s", ackdata); + } else if (bloblen == 65535) { + // repeat last ack + if (dbg) printf("repeating ack %s\n", ackpkt); + ANT_SendBurstTransferA(chan, ackpkt, strlen(ackpkt)/16); + } else { + if (dbg) printf("non-0 bloblen %d\n", bloblen); + decode(bloblen, pkttype, pktlen, blsize, blast); + sprintf(ackpkt, "440dffff0000000006000200%02x%02x0000", pkttype%256, pkttype/256); + } + if (dbg) printf("received pkttype %d len %d\n", pkttype, pktlen); + if (dbg) printf("acking %s\n", ackpkt); + ANT_SendBurstTransferA(chan, ackpkt, strlen(ackpkt)/16); + } else if (!nopairing && pairing && blast) { + memcpy(&peerdev, blast+12, 4); + if (dbg) + printf("watch id %08x waitauth %d\n", peerdev, waitauth); + if (mydev != 0 && peerdev != mydev) { + fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev); + exit(1); + } + if (waitauth == 2) { + int nw; + // should be receiving auth data + if (nowriteauth) { + printf("Not overwriting auth data\n"); + exit(1); + } + printf("storing auth data in %s\n", authfile); + authfd = open(authfile, O_WRONLY|O_CREAT, 0644); + if (authfd < 0) { + perror(authfile); + exit(1); + } + nw = write(authfd, blast, blsize); + if (nw != blsize) { + fprintf(stderr, "auth write failed fd %d %d\n", authfd, nw); + perror("write"); + exit(1); + } + close(authfd); + //exit(1); + pairing = 0; + waitauth = 0; + reset = 1; + } + if (pairing && !waitauth) { + //assert(sizeof pair == PAIRSIZE); + pair.code = 0x44; pair.atype = 4; pair.phase = 2; pair.id = myid; + bzero(pair.devname, sizeof pair.devname); + //if (peerdev <= 9999999) // only allow 7 digits + //sprintf(pair.devname, "%u", peerdev); + sprintf(pair.devname, fname); + //else + // fprintf(stderr, "pair dev name too large %08x \"%d\"\n", peerdev, peerdev); + pair.u1 = strlen(pair.devname); + printf("sending pair data for dev %s\n", pair.devname); + waitauth = 1; + if (isa405 && pairing) { + // go straight to storing auth data + waitauth = 2; + } + ANT_SendBurstTransfer(chan, (void *)&pair, (sizeof pair)/8) ; // send pair data + } else { + if (dbg) printf("not pairing\n"); + } + } else if (!gotwatchid && (lastphase == 1)) { + static int once = 0; + gotwatchid = 1; + // garmin sending authentication/identification data + if (!once) { + int i; + once = 1; + if (dbg) + fprintf(stderr, "id data: "); + } + if (dbg) + for (i = 0; i < blsize; i++) + fprintf(stderr, "%02x", blast[i]); + if (dbg) + fprintf(stderr, "\n"); + memcpy(&peerdev, blast+12, 4); + if (dbg) + printf("watch id %08x\n", peerdev); + if (mydev != 0 && peerdev != mydev) { + fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev); + exit(1); + } + } else if (lastphase == 2) { + int nw; + static int once = 0; + printf("once %d\n", once); + // garmin uploading in response to sendack3 + // in this state we're receiving the workout data + if (!once) { + printf("receiving\n"); + once = 1; + outfd = open(fn, O_WRONLY|O_CREAT, 0644); + if (outfd < 0) { + perror(fn); + exit(1); + } + } + if (last) { + nw = write(outfd, blast, blsize); + if (nw != blsize) { + fprintf(stderr, "data write failed fd %d %d\n", outfd, nw); + perror("write"); + exit(1); + } + close(outfd); + downloadfinished = 1; + } + } else if (0 && last) { + if (dbg) { + fprintf(stderr, "auth response: "); + for (i = 0; i < blsize; i++) + fprintf(stderr, "%02x", cbuf[i]); + fprintf(stderr, "\n"); + } + if (blast[10] == 2) { + fprintf(stderr, "authentication failed\n"); + exit(1); + } + } else if (last) { + fprintf(stderr, "data in state xx: "); + int i; + for (i = 0; i < blsize; i++) + fprintf(stderr, "%02x", blast[i]); + fprintf(stderr, "\n"); + sentcmd = 1000; + switch (sentcmd) { + case 1000: + break; + case 0: + sentcmd++; + //ANT_SendBurstTransferA(chan, getgpsver, strlen(getgpsver)/16); + break; + case 999: + printf("finished\n"); + exit(1); + default: + sleep(1); + sentcmd = 1; + //printf("sending command %d %s\n", sentcmd-1, cmds[sentcmd-1]); + //ANT_SendBurstTransferA(chan, cmds[sentcmd-1], + // strlen(cmds[sentcmd-1])/16); + sentcmd++; + //if(!strcmp(cmds[sentcmd-1], "END")) + // sentcmd = 999; + break; + } + } + if (dbg) printf("continuing after burst\n"); + break; + } + return 1; +} + +uchar +revent(uchar chan, uchar event) +{ + struct ack_msg ack; + int i; + + if (dbg) printf("revent %02x %02x\n", chan, event); + switch (event) { + case EVENT_TRANSFER_TX_COMPLETED: + if (dbg) printf("Transfer complete %02x\n", ebuf[1]); + break; + case INVALID_MESSAGE: + printf("Invalid message %02x\n", ebuf[1]); + break; + case RESPONSE_NO_ERROR: + switch (ebuf[1]) { + case MESG_ASSIGN_CHANNEL_ID: + ANT_AssignChannelEventFunction(chan, chevent, cbuf); + break; + case MESG_OPEN_CHANNEL_ID: + printf("channel open, waiting for broadcast\n"); + break; + default: + if (dbg) 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) { + if (dbg) + printf("devid %08x myid %08x\n", devid, myid); + } else { + printf("Ignoring unknown device %08x, mydev %08x\n", devid, mydev); + devid = sentid = 0; // reset + } + break; + case MESG_NETWORK_KEY_ID: + case MESG_SEARCH_WAVEFORM_ID: + case MESG_OPEN_CHANNEL_ID: + printf("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: + if (dbg) + printf("capabilities chans %d nets %d opt %02x adv %02x\n", + ebuf[0], ebuf[1], ebuf[2], ebuf[3]); + break; + case MESG_CHANNEL_STATUS_ID: + if (dbg) + printf("channel status %d\n", ebuf[1]); + break; + case EVENT_RX_FAIL: + // ignore this + break; + default: + printf("Unhandled response event %02x\n", event); + break; + } + return 1; +} + +main(int ac, char *av[]) +{ + 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 srchto = 255; // max timeout + int waveform = 0x0053; // aids search somehow + int c; + extern char *optarg; + extern int optind, opterr, optopt; + + // default auth file // + if (getenv("HOME")) { + authfile = malloc(strlen(getenv("HOME"))+strlen("/.gant")+1); + if (authfile) + sprintf(authfile, "%s/.gant", getenv("HOME")); + } + progname = av[0]; + while ((c = getopt(ac, av, "a:o:d:i:m:PpvDrnzf:")) != -1) { + switch(c) { + case 'a': + authfile = optarg; + break; + case 'f': + fname = optarg; + break; + case 'o': + fn = optarg; + break; + case 'd': + devnum = atoi(optarg); + break; + case 'i': + myid = atoi(optarg); + break; + case 'm': + mydev = atoi(optarg); + break; + case 'p': + passive = 1; + semipassive = 0; + break; + case 'P': + passive = 1; + break; + case 'v': + verbose = 1; + break; + case 'D': + dbg = 1; + break; + case 'r': + reset = 1; + break; + case 'n': + nowriteauth = 1; + break; + case 'z': + nopairing = 1; + break; + default: + fprintf(stderr, "unknown option %s\n", optarg); + usage(); + } + } + + ac -= optind; + av += optind; + + if ((!passive && !authfile) || ac) + usage(); + + if (!ANT_Init(devnum, 0)) { // should be 115200 but doesn't fit into a short + fprintf(stderr, "open dev %d failed\n", devnum); + exit(1); + } + ANT_ResetSystem(); + ANT_AssignResponseFunction(revent, ebuf); + ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID); //informative + ANT_SetNetworkKeya(net, ANTSPT_KEY); + ANT_AssignChannel(chan, chtype, net); + ANT_SetChannelId(chan, devno, devtype, manid); + ANT_RequestMessage(chan, MESG_CAPABILITIES_ID); //informative + ANT_SetChannelRFFreq(chan, freq); + ANT_SetChannelPeriod(chan, period); + ANT_SetChannelSearchTimeout(chan, srchto); + ANT_SetSearchWaveform(chan, waveform); + ANT_OpenChannel(chan); + ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID); //informative + + // everything handled in event functions + for(;;) + sleep(10); +}