diff options
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | README.cskprog | 25 | ||||
-rw-r--r-- | cskprog.c | 191 | ||||
-rw-r--r-- | cskprog.h | 89 | ||||
-rw-r--r-- | srecord.c | 139 | ||||
-rw-r--r-- | utils.c | 332 |
6 files changed, 783 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 47fdf76d..02b35002 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,7 +18,7 @@ endif bin_PROGRAMS = $(BUILD_PROGS) sirfmon rtcmdecode bin_SCRIPTS = gpsprof gpsfake sbin_PROGRAMS = gpsd -check_PROGRAMS = gpsmm_test bits +check_PROGRAMS = gpsmm_test bits cskprog # # Build xgps @@ -61,6 +61,12 @@ rtcmdecode_SOURCES = rtcmdecode.c rtcmdecode_LDADD = $(LIBM) -lncurses libgps.la -lm $(LIBPTHREAD) # +# Build cskprog +# +cskprog_SOURCES = cskprog.c cskprog.h srecord.c utils.c +cskprog_LDADD = $(LIBM) libgps.la -lm + +# # Build shared libraries # libgps_la_LDFLAGS = -version-number 15:0:0 diff --git a/README.cskprog b/README.cskprog new file mode 100644 index 00000000..53b9b325 --- /dev/null +++ b/README.cskprog @@ -0,0 +1,25 @@ +A few brief notes about my firmware flashing program... + +1) you will somehow need to obtain a copy of 'dlgsp2.bin' from SiRF. as +yet, we do not have permission to distribute this file, so don't ask us +for a copy. + +2) you will somehow need to obtain a firmware image compatible with your +receiver. don't ask me which firmwares are compatible with which receivers +as i toasted a few receivers when loading in what seemed to be a reasonable +firmware. use this program at your own risk. + +3) this loader will only work with firmwares in S-record format. that is +the format SiRF distributes the format in, and because S-records can be +"sparse" (they might only write some memory locations) i don't feel good +about converting to binary on the fly. if you get a binary firmware file +you can use objdump from binutils to convert it to S-records. + +4) realize that i'm not kidding around when i say that this could very well +break your receiver permanently and if it does, it's no one's fault but your +own. if that's ok with you must setenv a variable called I_READ_THE_README +with the string "why oh why didn't i take the blue pill". + +Usage: ./cskprog [-v] [-l <loader_file>] -p <tty> -f <firmware_file> + loader_file defaults to "dlgsp2.bin" +Receiver will be reset to 4800bps NMEA after flash diff --git a/cskprog.c b/cskprog.c new file mode 100644 index 00000000..01bd7cc6 --- /dev/null +++ b/cskprog.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2005 Chris Kuethe <chris.kuethe@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cskprog.h" + +char *progname; + +int +main(int argc, char **argv){ + + int ch; + int lfd, ffd, pfd; + int ls, fs; + int fflag = 0, lflag = 0, pflag = 0; + struct stat sb; + char *fname = NULL; + char *lname = "dlgsp2.bin"; + char *port = NULL; + char *warning; + struct termios *term; + sigset_t sigset; + + char *firmware = NULL; + char *loader = NULL; + + progname = argv[0]; + + while ((ch = getopt(argc, argv, "f:l:p:")) != -1) + switch (ch) { + case 'f': + fname = optarg; + fflag = 1; + break; + case 'l': + lname = optarg; + lflag = 1; + break; + case 'p': + port = optarg; + pflag = 1; + break; + default: + usage(); + /* NOTREACHED */ + } + + argc -= optind; + argv += optind; + + /* nasty little trick to hopefully make people read the manual */ + if ( ((warning = getenv("I_READ_THE_README")) == NULL) || + (strcmp(warning, "why oh why didn't i take the blue pill") == 0 )){ + fprintf(stderr, "RTFM, luser!\n"); + unlink(progname); + exit(1); + } + + /* make sure we have meaningful flags */ + if (!(fflag && pflag)) + usage(); + + /* Open the loader file */ + if((lfd = open(lname, O_RDONLY, 0444)) == -1) + err(1, "open(%s)", lname); + + /* fstat() its file descriptor. Need the size, and avoid races */ + if(fstat(lfd, &sb) == -1) + err(1, "fstat(%s)", lname); + + /* minimal sanity check on loader size. also prevents bad malloc() */ + ls = (int)sb.st_size; + if ((ls < MIN_LD_SIZE) || (ls > MAX_LD_SIZE)){ + fprintf(stderr, "preposterous loader size: %d\n", ls); + return 1; + } + + /* malloc a loader buffer */ + if ((loader = malloc(ls)) == NULL) + err(1, "malloc(%d)", ls); + + if ((read(lfd, loader, ls)) != ls) + err(1, "read(%d)", ls); + + /* don't care if close fails - kernel will force close on exit() */ + close(lfd); + + /* Open the firmware image file */ + if((ffd = open(fname, O_RDONLY, 0444)) == -1) + err(1, "open(%s)", fname); + + /* fstat() its file descriptor. Need the size, and avoid races */ + if(fstat(ffd, &sb) == -1) + err(1, "fstat(%s)", fname); + + /* minimal sanity check on firmware size. also prevents bad malloc() */ + fs = (int)sb.st_size; + if ((fs < MIN_FW_SIZE) || (fs > MAX_FW_SIZE)){ + fprintf(stderr, "preposterous firmware size: %d\n", fs); + return 1; + } + + /* malloc an image buffer */ + if ((firmware = malloc(fs+1)) == NULL) + err(1, "malloc(%d)", fs); + + if ((read(ffd, firmware, fs)) != fs) + err(1, "read(%d)", fs); + + firmware[fs] = '\0'; + + /* don't care if close fails - kernel will force close on exit() */ + close(ffd); + + /* did we just read some S-records? */ + if (!((firmware[0] == 'S') && ((firmware[1] >= '0') && (firmware[1] <= '9')))){ /* srec? */ + fprintf(stderr, "%s: not an S-record file\n", fname); + return(1); + } + + /* malloc a loader buffer */ + if ((term = malloc(sizeof(struct termios))) == NULL) + err(1, "malloc(%d)", sizeof(struct termios)); + + /* Open the serial port, blocking is OK */ + if((pfd = open(port, O_RDWR | O_NOCTTY , 0600)) == -1) + err(1, "open(%s)", port); + + /* the firware upload defaults to 38k4, so lets go there */ + if(sirfSetProto(pfd, term, PROTO_SIRF, 38400) == -1) + err(1, "sirfSetProto()"); + + /* once we get here, we are uninterruptable. handle signals */ + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGHUP); + sigaddset(&sigset, SIGQUIT); + sigaddset(&sigset, SIGTSTP); + sigaddset(&sigset, SIGSTOP); + sigaddset(&sigset, SIGKILL); + + if(sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) + err(1,"sigprocmask"); + + /* send the magic update command */ + if(sirfSendUpdateCmd(pfd) == -1) + err(1, "sirfSendUpdateCmd()"); + + /* wait a moment for the receiver to switch to boot rom */ + sleep(2); + + /* send the bootstrap/flash programmer */ + if(sirfSendLoader(pfd, term, loader, ls) == -1) + err(1, "sirfSendLoader()"); + + /* again we wait, this time for our uploaded code to start running */ + sleep(2); + + /* and now, poke the actual firmware over */ + if(sirfSendFirmware(pfd, firmware, fs) == -1) + err(1, "sirfSendFirmware()"); + + /* waitaminnit, and drop back to NMEA@4800 for luser apps */ + sleep(5); + if(sirfSetProto(pfd, term, PROTO_NMEA, 4800) == -1) + err(1, "sirfSetProto()"); + + /* return() from main(), to take advantage of SSP compilers */ + return 0; +} + +void +usage(void){ + fprintf(stderr, "Usage: %s [-v] [-l <loader_file>] -p <tty> -f <firmware_file>\n", progname); + fprintf(stderr, " loader_file defaults to \"dlgsp2.bin\"\n"); + fprintf(stderr, "Receiver will be reset to 4800bps NMEA after flash\n"); + exit(1); +} + diff --git a/cskprog.h b/cskprog.h new file mode 100644 index 00000000..71ea041b --- /dev/null +++ b/cskprog.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2005 Chris Kuethe <chris.kuethe@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CSKPROG_H_ +#define _CSKPROG_H_ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +/* + * I can't imagine a GPS firmware less than 256KB / 2Mbit. The latest build + * that I have (2.3.2) is 296KB. So 256KB is probably low enough to allow + * really old firmwares to load. + * + * As far as I know, USB receivers have 512KB / 4Mbit of flash. Application + * note APNT00016 (Alternate Flash Programming Algorithms) says that the S2AR + * reference design supports 4, 8 or 16 Mbit flash memories, but with current + * firmwares not even using 60% of a 4Mbit flash on a commercial receiver, + * I'm not going to stress over loading huge images. The define below is + * 524288 bytes, but that blows up nearly 3 times as S-records. + * 928K srec -> 296K binary + */ +#define MIN_FW_SIZE 262144 +#define MAX_FW_SIZE 1572864 + +/* a reasonable loader is probably 15K - 20K */ +#define MIN_LD_SIZE 15440 +#define MAX_LD_SIZE 20480 + +/* From the SiRF protocol manual... may as well be consistent */ +#define PROTO_SIRF 0 +#define PROTO_NMEA 1 + +#define BOOST_38400 0 +#define BOOST_57600 1 +#define BOOST_115200 2 + +/* block size when writing to the serial port. related to FIFO size */ +#define WRBLK 128 + +/* Prototypes */ +/* sirfflash.c */ +int main(int, char **); +void usage(void); + +/* utils.c */ +int serialConfig(int, struct termios *, int); +int serialSpeed(int, struct termios *, int); +int sirfSetProto(int, struct termios *, int, int); +int sirfSendUpdateCmd(int); +int sirfSendLoader(int, struct termios *, char *, int); +int sirfSendFirmware(int, char *, int); +int sirfWrite(int, unsigned char *); +unsigned char nmea_checksum(unsigned char *); + +/* srecord.c */ +void hexdump(int , unsigned char *, unsigned char *); +unsigned char sr_sum(int, int, unsigned char *); +int bin2srec(int, int, unsigned char *, unsigned char *); +int srec_hdr(unsigned char *); +int srec_fin(int, unsigned char *); +char hc(char); + + +#endif /* _CSKPROG_H_ */ diff --git a/srecord.c b/srecord.c new file mode 100644 index 00000000..2fb41cf5 --- /dev/null +++ b/srecord.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005 Chris Kuethe <chris.kuethe@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef _SRECORD_ +#include "cskprog.h" + +/* + * http://www.amelek.gda.pl/avr/uisp/srecord.htm + * S0: Comments + * S3: Memory Loadable Data, 4byte address + * S5: Count of S1, S2 and S3 Records + * S7: starting execution address intrepreted as a 4byte address + */ + +/* + * bin2srec: turn a chunk of binary into an srecord file + * offset: used to specify load address + * num: up to 16 bytes can be encoded at one time + * bytes are read from bbuf and a ready-to-go srecord is placed in sbuf + */ +int +bin2srec(int offset, int num, unsigned char *bbuf, unsigned char *sbuf){ + unsigned char abuf[34], sum; + int len; + + if ((num < 1) || (num > 16)) + return -1; + + len = 4 + num + 1; + bzero(abuf, 34); + bzero(sbuf, 64); + hexdump(num, bbuf, abuf); + sum = sr_sum(len, offset, bbuf); + snprintf((char *)sbuf, 49, "S3%02X%08X%s%02X\r\n", len, offset, abuf, sum); + return 0; +} + +int +srec_hdr(unsigned char *sbuf){ + /* dlgsp2.bin looks for this header */ + unsigned char hdr[] = "S00600004844521B\r\n"; + bzero(sbuf, 64); + snprintf((char *)sbuf, 19, "%s", hdr); + return 0; +} + +int +srec_fin(int num, unsigned char *sbuf){ + unsigned char bbuf[4], sum; + + bzero(bbuf, 4); + bzero(sbuf, 64); + + bbuf[0] = (unsigned char)(num & 0xff); + bbuf[1] = (unsigned char)((num >> 8) & 0xff); + sum = sr_sum(3, 0, bbuf); + snprintf((char *)sbuf, 13, "S503%04X%02X\r\n", num, sum); + return 0; +} + + +void +hexdump(int j, unsigned char *bbuf, unsigned char *abuf){ + int i; + + bzero(abuf, 34); + if (j > 32) + j = 32; + + for(i = 0; i < j; i++){ + abuf[i*2] = hc((bbuf[i] &0xf0) >> 4); + abuf[i*2+1] = hc(bbuf[i] &0x0f); + } +} + +char +hc(char x){ + switch(x){ + case 15: + case 14: + case 13: + case 12: + case 11: + case 10: + return ('A' + x - 10); + break; + case 9: + case 8: + case 7: + case 6: + case 5: + case 4: + case 3: + case 2: + case 1: + case 0: + return ('0' + x); + break; + + default: + return '0'; + break; + } +} + +unsigned char +sr_sum(int count, int addr, unsigned char *bbuf){ + int i, j; + unsigned char k, sum = 0; + + sum = (count & 0xff); + sum += ((addr & 0x000000ff)); + sum += ((addr & 0x0000ff00) >> 8); + sum += ((addr & 0x00ff0000) >> 16); + sum += ((addr & 0xff000000) >> 24); + j = count - 5; + for(i = 0; i < j; i++){ + k = bbuf[i]; + sum += k; + } + return ~sum; +} + +#else +extern int errno; +#endif diff --git a/utils.c b/utils.c new file mode 100644 index 00000000..0aa08119 --- /dev/null +++ b/utils.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2005 Chris Kuethe <chris.kuethe@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cskprog.h" + +int +serialSpeed(int pfd, struct termios *term, int speed){ + int rv; + int r = 0; + + switch(speed){ + case 115200: + speed = B115200; + break; + case 57600: + speed = B57600; + break; + case 38400: + speed = B38400; + break; + case 28800: + speed = B28800; + break; + case 19200: + speed = B19200; + break; + case 14400: + speed = B14400; + break; + case 9600: + speed = B9600; + break; + case 4800: + speed = B9600; + break; + default: + errno = EINVAL; + return -1; + /* NOTREACHED */ + break; + } + + /* set UART speed */ + tcgetattr(pfd, term); + cfsetispeed(term, speed); + cfsetospeed(term, speed); + while ((rv = tcsetattr(pfd, TCSAFLUSH, term) == -1) && \ + (errno == EINTR) && (r < 3)) { + /* retry up to 3 times on EINTR */ + usleep (1000); + r++; + } + + if(rv == -1) + return -1; + else + return 0; +} + + +int +serialConfig(int pfd, struct termios *term, int speed){ + int rv; + int r = 0; + + + /* get the current terminal settings */ + tcgetattr(pfd, term); + /* set the port into "raw" mode. */ + cfmakeraw(term); + term->c_lflag &=~ (ICANON); + /* Enable serial I/O, ignore modem lines */ + term->c_cflag |= (CLOCAL | CREAD); + /* No output postprocessing */ + term->c_oflag &=~ (OPOST); + /* 8 data bits */ + term->c_cflag |= CS8; + term->c_iflag &=~ (ISTRIP); + /* No parity */ + term->c_iflag &=~ (INPCK); + term->c_cflag &=~ (PARENB | PARODD); + /* 1 Stop bit */ + term->c_cflag &=~ (CSIZE | CSTOPB); + /* No flow control */ + term->c_iflag &=~ (IXON | IXOFF); + term->c_oflag &=~ (CCTS_OFLOW | CRTS_IFLOW | MDMBUF); + + /* we'd like to read back at least 2 characters in .2sec */ + term->c_cc[VMIN] = 2; + term->c_cc[VTIME] = 2; + + /* apply all the funky control settings */ + while ((rv = tcsetattr(pfd, TCSAFLUSH, term) == -1) && \ + (errno == EINTR) && (r < 3)) { + /* retry up to 3 times on EINTR */ + usleep (1000); + r++; + } + + if(rv == -1) + return -1; + + /* and if that all worked, try change the UART speed */ + return serialSpeed(pfd, term, speed); +} + +int +sirfSendUpdateCmd(int pfd){ + unsigned char msg[] = {0xa0,0xa2, /* header */ + 0x00,0x01, /* message length */ + 0x94, /* 0x94: firmware update */ + 0x00,0x00, /* checksum */ + 0xb0,0xb3}; /* trailer */ + return (sirfWrite(pfd, msg)); +} + +int +sirfSendLoader(int pfd, struct termios *term, char *loader, int ls){ + unsigned int x; + int speed = 115200; + unsigned char boost[] = {'S', BOOST_115200}; + unsigned char *msg; + int r, nbr, nbs, nbx; + + if((msg = malloc(ls+10)) == NULL){ + return -1; /* oops. bail out */ + } + + x = htonl((unsigned int)ls); + msg[0] = 'S'; + msg[1] = (unsigned char)0; + memcpy(msg+2, &x, 4); /* length */ + memcpy(msg+6, loader, ls); /* loader */ + memset(msg+6+ls, 0, 4); /* reset vector */ + + /* send the command to jack up the speed */ + if((r = write(pfd, boost, 2)) != 2) + return -1; /* oops. bail out */ + + /* wait for the serial speed change to take effect */ + tcdrain(pfd); + usleep(1000); + + /* now set up the serial port at this higher speed */ + serialSpeed(pfd, term, speed); + + /* send the real loader */ + nbr = ls+10; nbs = WRBLK ; nbx = 0; + while(nbr){ + if(nbr > WRBLK ) + nbs = WRBLK ; + else + nbs = nbr; + +r0: if((r = write(pfd, msg+nbx, nbs)) == -1){ + if (errno == EAGAIN){ /* retry */ + tcdrain(pfd); /* wait a moment */ + errno = 0; /* clear errno */ + nbr -= r; /* number bytes remaining */ + nbx += r; /* number bytes sent */ + goto r0; + } else { + return -1; /* oops. bail out */ + } + } + nbr -= r; + nbx += r; + } + return 0; +} + +int +sirfSendFirmware(int pfd, char *fw, int len){ + int l, r, i; + char *sendbuf, recvbuf[8]; + + /* srecord loading is interactive. send line, get reply */ + /* when sending S-records, check for SA/S5 or SE */ + + if((sendbuf = malloc(85)) == NULL) + err(1, NULL); + + bzero(recvbuf,8); + i = 0; + while(strlen(fw)){ + /* grab a line of firmware, ignore line endings */ + if ((r = strlen(fw))){ + bzero(sendbuf,85); + if((r = sscanf(fw, "%80s", sendbuf)) == EOF) + return 0; + + l = strlen(sendbuf); + if ((l < 1) || (l > 80)) + return -1; + + fw += l; + len -= l; + + while((fw[0] != 'S') && (fw[0] != '\0')) + fw++; + + sendbuf[l] = '\r'; + sendbuf[l+1] = '\n'; + l += 2; + + if ((++i % 1000) == 0) + printf ("%6d\n", i); + + tcflush(pfd, TCIFLUSH); + if((r = write(pfd, sendbuf, l+2)) != l+2) + return -1; /* oops. bail out */ + + tcdrain(pfd); + if((r = read(pfd, recvbuf, 7)) == -1) + return -1; /* oops. bail out */ + + if (!((recvbuf[0] == 'S') && ((recvbuf[1] == 'A') || (recvbuf[1] == '5')))) + return -1; /* oops. bail out */ + } + } + return 0; +} + +int +sirfSetProto(int pfd, struct termios *term, int speed, int proto){ + int l, r, i; + int spd[8] = {115200, 57600, 38400, 28800, 19200, 14400, 9600, 4800}; + unsigned char *nmea, *tmp; + unsigned char sirf[] = {0xa0,0xa2, /* header */ + 0x00,0x01, /* message length */ + 0xa5, /* message 0xa5: UART config */ + 0x00,0,0, 0,0,0,0, 8,1,0, 0,0, /* port 0 */ + 0xff,0,0, 0,0,0,0, 0,0,0, 0,0, /* port 1 */ + 0xff,0,0, 0,0,0,0, 0,0,0, 0,0, /* port 2 */ + 0xff,0,0, 0,0,0,0, 0,0,0, 0,0, /* port 3 */ + 0x00,0x00, /* checksum */ + 0xb0,0xb3}; /* trailer */ + + + if((nmea = malloc(32)) == NULL) + return -1; + + if((tmp = malloc(32)) == NULL) + return -1; + + bzero(nmea, 32); + bzero(tmp, 32); + snprintf((char *)tmp, 31,"PSRF100,%u,%u,8,1,0", speed, proto); + snprintf((char *)nmea, 31,"%s%s*%02x", "$", tmp, nmea_checksum(tmp)); + l = strlen((char *)nmea); + + sirf[7] = sirf[6] = (char)proto; + i = htonl(speed); /* borrow "i" to put speed into proper byte order */ + bcopy(&i, sirf+8, 4); + + /* send at whatever baud we're currently using */ + sirfWrite(pfd, sirf); + if ((r = write(pfd, nmea, l)) != r) + return -1; + + tcdrain(pfd); + + /* now spam the receiver with the config messages */ + for(i = 0; i < 8; i++){ + serialSpeed(pfd, term, spd[i]); + sirfWrite(pfd, sirf); + write(pfd, nmea, l); + tcdrain(pfd); + usleep(100000); + } + + serialSpeed(pfd, term, speed); + tcflush(pfd, TCIOFLUSH); + + return 0; +} + +unsigned char +nmea_checksum(unsigned char *s){ + unsigned char c, r = 0; + while ((c = *s++)) + r += c; + + return r; +} + +/* ************************************************************************ */ +/* This stuff has mostly been stolen from gpsd's sirf.c */ +/* Not sure who wrote this function, but these guys in the credits... */ +/* Remco Treffkorn <remco@rvt.com> */ +/* Derrick J. Brashear <shadow@dementia.org> */ +/* Russ Nelson <nelson@crynwyr.com> */ +/* Eric S. Raymond <esr@thyrsus.com> */ +/* ************************************************************************ */ + +int +sirfWrite(int fd, unsigned char *msg) { + unsigned int crc; + size_t i, len; + + len = (size_t)((msg[2] << 8) | msg[3]); + + /* calculate CRC */ + crc = 0; + for (i = 0; i < len; i++) + crc += (int)msg[4 + i]; + crc &= 0x7fff; + + /* enter CRC after payload */ + msg[len + 4] = (unsigned char)((crc & 0xff00) >> 8); + msg[len + 5] = (unsigned char)( crc & 0x00ff); + + errno = 0; + if (write(fd, msg, len+8) != (len+8)) + return -1; + + (void)tcdrain(fd); + return 0; +} |