summaryrefslogtreecommitdiff
path: root/sirfflash.c
blob: 4ce2d0b291b7acb26825cc80cef1d6388329d1e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/* $Id$ */
/*
 * Copyright (c) 2005-2007 Chris Kuethe <chris.kuethe@gmail.com>
 * Copyright (c) 2005-2007 Eric S. Raymond <esr@thyrsus.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.
 */
/*
 * 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 <sys/types.h>
#include "gpsd_config.h"
#include "gpsd.h"
#include "gpsflash.h"

#if defined(SIRF_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(LOG_PROG, "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, 1))
	want = 21;
    else if (expect(fd,"\xa0\xa2\x00\x51\x06", 5, 1)) 
	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(LOG_PROG, "%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 UNUSED, const char *version UNUSED,
			    const char *loader UNUSED, size_t ls UNUSED,
			    const char *firmware UNUSED, 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(LOG_PROG, "waiting 2 seconds...\n");
    return (int)sleep(2);
}

static int wait5seconds(int fd UNUSED)
{
    /* wait for firmware upload to settle in */
    gpsd_report(LOG_PROG, "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(SIRF_ENABLE) && defined(BINARY_ENABLE) */