/*
 * This is the SiRF-dependent part of the gpsflash program.
 *
 * If we ever compose our own S-records, dlgsp2.bin looks for this header
 * unsigned char hdr[] = "S00600004844521B\r\n";
 *
 * Here's what Carl Carter at SiRF told us when he sent us informattion
 * on how to build one of these:
 *
 * --------------------------------------------------------------------------
 * Regarding programming the flash, I will attach 2 things for you -- a
 * program called SiRFProg, the source for an older flash programming
 * utility, and a description of the ROM operation.  Note that while the
 * ROM description document is for SiRFstarIII, the interface applies to
 * SiRFstarII systems like you are using.  Here is a little guide to how
 * things work:
 * 
 * 1.  The receiver is put into "internal boot" mode -- this means that it
 * is running off the code contained in the internal ROM rather than the
 * external flash.  You do this by either putting a pull-up resistor on
 * data line 0 and cycling power or by giving a message ID 148.
 * 2.  The internal ROM provides a very primitive boot loader that permits
 * you to load a program into RAM and then switch to it.
 * 3.  The program in RAM is used to handle the erasing and programming
 * chores, so theoretically you could create any program of your own
 * choosing to handle things.  SiRFProg gives you an example of how to do
 * it using Motorola S record files as the programming source.  The program
 * that resides on the programming host handles sending down the RAM
 * program, then communicating with it to transfer the data to program.
 * 4.  Once the programming is complete, you transfer to it by switching to
 * "external boot" mode -- generally this requires a pull-down resistor on
 * data line 0 and either a power cycle or toggling the reset line low then
 * back high.  There is no command that does this.
 * 
 * Our standard utility operates much faster than SiRFProg by using a
 * couple tricks.  One, it transfers a binary image rather than S records
 * (which are ASCII and about 3x the size of the image).  Two, it
 * compresses the binary image using some standard compression algorithm.
 * Three, when transferring the file we boost the port baud rate.  Normally
 * we use 115200 baud as that is all the drivers in most receivers handle.
 * But when supported, we can boost up to 900 kbaud.  Programming at 38400
 * takes a couple minutes.  At 115200 it takes usually under 30 seconds.
 * At 900 k it takes about 6 seconds.
 * --------------------------------------------------------------------------
 *
 * Copyright (c) 2005 Chris Kuethe <chris.kuethe@gmail.com>
 */

#include "gpsd.h"
#include "gpsflash.h"

#if defined(SIRFII_ENABLE) && defined(BINARY_ENABLE)

/* 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

static int
sirfSendUpdateCmd(int pfd){
	bool status;
	/*@ +charint @*/
	static unsigned char msg[] =	{
	    			0xa0,0xa2,	/* header */
				0x00,0x01,	/* message length */
				0x94,		/* 0x94: firmware update */
				0x00,0x00,	/* checksum */
				0xb0,0xb3};	/* trailer */
	/*@ -charint @*/
	status = sirf_write(pfd, msg);
	/* wait a moment for the receiver to switch to boot rom */
	(void)sleep(2);
	return status ? 0 : -1;
}

static int
sirfSendLoader(int pfd, struct termios *term, char *loader, size_t ls){
	unsigned int x;
	int r, speed = 38400;
	/*@i@*/unsigned char boost[] = {'S', BOOST_38400};
	unsigned char *msg;

	if((msg = malloc(ls+10)) == NULL){
		return -1; /* oops. bail out */
	}

	/*@ +charint @*/
#ifdef B115200
	speed = 115200;
	boost[1] = BOOST_115200;
#else
#ifdef B57600
	speed = 57600;
	boost[1] = BOOST_57600;
#endif
#endif
	/*@ -charint @*/

	x = (unsigned)htonl(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 = (int)write(pfd, boost, 2)) != 2) {
		free(msg);
		return -1; /* oops. bail out */
	}

	/* wait for the serial speed change to take effect */
	(void)tcdrain(pfd);
	(void)usleep(1000);

	/* now set up the serial port at this higher speed */
	(void)serialSpeed(pfd, term, speed);

	/* ship the actual data */
	r = binary_send(pfd, (char *)msg, ls+10);
	free(msg);
	return r;
}

static int
sirfSetProto(int pfd, struct termios *term, unsigned int speed, unsigned int proto){
	int i;
	int spd[8] = {115200, 57600, 38400, 28800, 19200, 14400, 9600, 4800};
	/*@ +charint @*/
	static unsigned char sirf[] =	{
				0xa0,0xa2,	/* header */
				0x00,0x31,	/* 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 */
	/*@ -charint @*/

	if (serialConfig(pfd, term, 38400) == -1)
		return -1;

	sirf[7] = sirf[6] = (unsigned char)proto;
	/*@i@*/i = htonl(speed); /* borrow "i" to put speed into proper byte order */
	/*@i@*/bcopy(&i, sirf+8, 4);

	/* send at whatever baud we're currently using */
	(void)sirf_write(pfd, sirf);
	(void)nmea_send(pfd, "$PSRF100,%u,%u,8,1,0", speed, proto);

	/* now spam the receiver with the config messages */
	for(i = 0; i < (int)(sizeof(spd)/sizeof(spd[0])); i++) {
		(void)serialSpeed(pfd, term, spd[i]);
		(void)sirf_write(pfd, sirf);
		(void)nmea_send(pfd, "$PSRF100,%u,%u,8,1,0", speed, proto);
		(void)tcdrain(pfd);
		(void)usleep(100000);
	}

	(void)serialSpeed(pfd, term, (int)speed);
	(void)tcflush(pfd, TCIOFLUSH);

	return 0;
}

/*@ -nullstate @*/
static int sirfProbe(int fd, char **version)
/* try to elicit a return packet with the firmware version in it */
{
    /*@ +charint @*/
    static unsigned char versionprobe[] = {
				    0xa0, 0xa2, 0x00, 0x02,
				    0x84, 0x00,
				    0x00, 0x84, 0xb0, 0xb3};
    /*@ -charint @*/
    char buf[MAX_PACKET_LENGTH];
    ssize_t status, want;

    gpsd_report(4, "probing with %s\n", 
		gpsd_hexdump(versionprobe, sizeof(versionprobe)));
    if ((status = write(fd, versionprobe, sizeof(versionprobe))) != 10)
	return -1;
    /*
     * Older SiRF chips had a 21-character version message.  Newer 
     * ones (GSW 2.3.2 or later) have an 81-character version message.
     * Accept either.
     */
    want = 0;
    if (expect(fd,"\xa0\xa2\x00\x15\x06", 5, 5))
	want = 21;
    else if (expect(fd,"\xa0\xa2\x00\x51\x06", 5, 5)) 
	want = 81;

    if (want) {
	ssize_t len;
	memset(buf, 0, sizeof(buf));
	for (len = 0; len < want; len += status) {
	    status = read(fd, buf+len, sizeof(buf));
	    if (status == -1)
		return -1;
	}
	gpsd_report(4, "%d bytes = %s\n", len, gpsd_hexdump(buf, (size_t)len));
	*version = strdup(buf);
	return 0;
    } else {
	*version = NULL;
	return -1;
    }
}
/*@ +nullstate @*/

static int sirfPortSetup(int fd, struct termios *term)
{
    /* the firware upload defaults to 38k4, so let's go there */
    return sirfSetProto(fd, term, PROTO_SIRF, 38400);
}

static int sirfVersionCheck(int fd, const char *version UNUSED,
			    const char *loader, size_t ls UNUSED,
			    const char *firmware, size_t fs UNUSED)
{
    /*
     * This implies that any SiRF loader and firmware image is good for 
     * any SiRF chip.  We really want to do more checking here...
     */
    return 0;
}

static int wait2seconds(int fd UNUSED)
{
    /* again we wait, this time for our uploaded code to start running */
    gpsd_report(1, "waiting 2 seconds...\n");
    return (int)sleep(2);
}

static int wait5seconds(int fd UNUSED)
{
    /* wait for firmware upload to settle in */
    gpsd_report(1, "waiting 5 seconds...\n");
    return (int)sleep(5);
}

static int sirfPortWrapup(int fd, struct termios *term)
{
    /* waitaminnit, and drop back to NMEA@4800 for luser apps */
    return sirfSetProto(fd, term, PROTO_NMEA, 4800);
}

struct flashloader_t sirf_type = {
    .name = "SiRF binary",

    /* name of default flashloader */
    .flashloader = "dlgsp2.bin",
    /*
     * 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
     */
    .min_firmware_size = 262144,
    .max_firmware_size = 1572864,

    /* a reasonable loader is probably 15K - 20K */
    .min_loader_size = 15440,
    .max_loader_size = 20480,

    /* the command methods */
    .probe = sirfProbe,
    .port_setup = sirfPortSetup,	/* before signal blocking */
    .version_check = sirfVersionCheck,
    .stage1_command = sirfSendUpdateCmd,
    .loader_send  = sirfSendLoader,
    .stage2_command = wait2seconds,
    .firmware_send  = srecord_send,
    .stage3_command = wait5seconds,
    .port_wrapup = sirfPortWrapup,	/* after signals unblock */
};
#endif /* defined(SIRFII_ENABLE) && defined(BINARY_ENABLE) */


syntax highlighted by Code2HTML, v. 0.9.1