summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am8
-rw-r--r--README.cskprog25
-rw-r--r--cskprog.c191
-rw-r--r--cskprog.h89
-rw-r--r--srecord.c139
-rw-r--r--utils.c332
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;
+}