lm4flash.c 20.1 KB
Newer Older
Fabio Utzig's avatar
Fabio Utzig committed
1
/* lm4flash - TI Stellaris Launchpad ICDI flasher
Fabio Utzig's avatar
Fabio Utzig committed
2
 * Copyright (C) 2012 Fabio Utzig <fabio@utzig.net>
3
 * Copyright (C) 2012 Peter Stuge <peter@stuge.se>
Fabio Utzig's avatar
Fabio Utzig committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

Fabio Utzig's avatar
Fabio Utzig committed
20
21
#include <stdio.h>
#include <stdlib.h>
22
23
#include <stdint.h>
#include <string.h>
RickKimball's avatar
RickKimball committed
24
#include <ctype.h>
Fabio Utzig's avatar
Fabio Utzig committed
25
26
27
28
29

#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

Fabio Utzig's avatar
Fabio Utzig committed
30
#include <libusb.h>
Fabio Utzig's avatar
Fabio Utzig committed
31

Fabio Utzig's avatar
Fabio Utzig committed
32
//#define DEBUG 1
33

34
35
36
#define ICDI_VID 0x1cbe
#define ICDI_PID 0x00fd

Fabio Utzig's avatar
Fabio Utzig committed
37
38
// FlashPatch Control Register: see ARM Av7mRM C1.11.3
static const uint32_t FP_CTRL  = 0xe0002000;
39

Fabio Utzig's avatar
Fabio Utzig committed
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Debug Halting Control and Status Register: see ARM Av7mRM C1.6.2
static const uint32_t DHCSR    = 0xe000edf0;

// Device Identification: see Stellaris LM4F120H5QR Microcontroller Section 5.5
static const uint32_t DID0     = 0x400fe000;
static const uint32_t DID1     = 0x400fe004;

// Device Identification: see Stellaris LM4F120H5QR Microcontroller Section 5.5
static const uint32_t DC0      = 0x400fe008;

// Run-Mode Clock Configuration: Stellaris LM4F120H5QR Microcontroller Section 5.5
static const uint32_t RCC      = 0x400fe060;

// Non-Volatile Memory Information: Stellaris LM4F120H5QR Microcontroller Section 5.6
static const uint32_t NVMSTAT  = 0x400fe1a0;
Fabio Utzig's avatar
Fabio Utzig committed
55
56

// Rom Control: see Stellaris LM4F120H5QR Microcontroller Page 531
Fabio Utzig's avatar
Fabio Utzig committed
57
static const uint32_t ROMCTL   = 0x400fe0f0;
Fabio Utzig's avatar
Fabio Utzig committed
58
59

// Flash Memory Address: see Stellaris LM4F120H5QR Microcontroller Page 497
Fabio Utzig's avatar
Fabio Utzig committed
60
static const uint32_t FMA      = 0x400fd000;
61
62
63
64
65

static const uint8_t INTERFACE_NR = 0x02;
static const uint8_t ENDPOINT_IN  = 0x83;
static const uint8_t ENDPOINT_OUT = 0x02;

66
67
68
69
70
71
72
73
74
75
76
77
#define START "$"
#define END "#"

#ifdef WIN32
#define snprintf _snprintf
#define SNPRINTF_OFFSET 1
#else
#define SNPRINTF_OFFSET 0
#endif

#define START_LEN strlen(START)
#define END_LEN (strlen(END) + 2)
78

79
#define FLASH_BLOCK_SIZE 512
80
#define FLASH_ERASE_SIZE 1024
81

82
83
84
85
86
87
88
89
90
91
/* Prefix + potentially every flash byte escaped */
#define BUF_SIZE 64 + 2*FLASH_BLOCK_SIZE

static uint8_t flash_block[FLASH_BLOCK_SIZE];
static union {
	char c[BUF_SIZE];
	uint8_t u8[BUF_SIZE];
	uint32_t u32[BUF_SIZE / 4];
} buf;

Fabio Utzig's avatar
Fabio Utzig committed
92
93
94
void show_version(void)
{
	printf("%s",
Fabio Utzig's avatar
Fabio Utzig committed
95
	       "LM4Flash version 0.1.3 - Flasher for Stellaris Launchpad ICDI boards\n"
Fabio Utzig's avatar
Fabio Utzig committed
96
97
98
99
100
101
102
103
	       "Copyright (C) 2012 Fabio Utzig <fabio@utzig.net>\n"
	       "Copyright (C) 2012 Peter Stuge <peter@stuge.se>\n"
	       "This is free software; see the source for copying conditions.  There is NO\n"
	       "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR "
	       "PURPOSE.\n"
	);
}

104
static uint32_t le32_to_cpu(const uint32_t x)
105
{
106
107
108
109
110
111
112
113
114
	union {
		uint8_t  b8[4];
		uint32_t b32;
	} _tmp;
	_tmp.b8[3] = x >> 24;
	_tmp.b8[2] = x >> 16;
	_tmp.b8[1] = x >> 8;
	_tmp.b8[0] = x & 0xff;
	return _tmp.b32;
115
116
}

Fabio Utzig's avatar
Fabio Utzig committed
117
static int do_verify = 0;
118
static int erase_used = 0;
119
static uint32_t start_addr = 0;
Fabio Utzig's avatar
Fabio Utzig committed
120

121
122
#define cpu_to_le32 le32_to_cpu

Fabio Utzig's avatar
Fabio Utzig committed
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
#ifdef DEBUG
static void pretty_print_buf(uint8_t *b, int size)
{
#define PP_LINESIZE    80
#define PP_NUM_P_LINE  16
#define PP_HEX_COL     7
#define PP_ASC_COL     56

	int i, pos;
	char linebuf[PP_LINESIZE];

	memset(linebuf, ' ', sizeof linebuf);
	linebuf[PP_ASC_COL + PP_NUM_P_LINE] = 0;
	for (i = 0; i < size; i++) {
		if (((i % PP_NUM_P_LINE) == 0)) {
			if (i) {
				printf("%s\n", linebuf);
				memset(linebuf, ' ', sizeof linebuf);
				linebuf[PP_ASC_COL + PP_NUM_P_LINE] = 0;
			}
			sprintf(linebuf, "%04x : ", i);
			linebuf[PP_ASC_COL] = ' ';
			linebuf[PP_HEX_COL] = ' ';
		}
		pos = PP_HEX_COL + ((i % PP_NUM_P_LINE) * 3);
		sprintf(linebuf + pos, "%02x", b[i]);
		linebuf[pos + 2] = ' ';
		linebuf[(i % PP_NUM_P_LINE) + PP_ASC_COL] = isprint(b[i]) ? b[i] : '.';
	}
	printf("%s\n", linebuf);
}
#endif

156
157
158
159
160
161
static int send_command(libusb_device_handle *handle, int size)
{
	int transferred = 0;
	int retval;

#ifdef DEBUG
Fabio Utzig's avatar
Fabio Utzig committed
162
163
	printf(">>> sending %d bytes\n", size);
	pretty_print_buf(buf.u8, size);
164
165
#endif

166
	retval = libusb_bulk_transfer(handle, ENDPOINT_OUT, buf.u8, size, &transferred, 0);
167
168
169
170
171
172
173
	if (retval != 0 || size != transferred) {
		printf("Error transmitting data %d\n", retval);
	}

	return retval;
}

174
static int wait_response(libusb_device_handle *handle, int *has_ack, int *size)
175
176
{
	int retval;
177
	int transferred = 0;
178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
	*has_ack = 0;
	*size = 0;

	do {
		retval = libusb_bulk_transfer(handle,
		                              ENDPOINT_IN,
		                              &buf.u8[*size],
		                              BUF_SIZE - *size,
		                              &transferred,
		                              0);
		if (retval != 0) {
			printf("Error receiving data %d\n", retval);
			return retval;
		}

		if (transferred >= 1 && buf.c[0] == '+')
			*has_ack = 1;

		*size += transferred;

	} while ((*size < 3) || (buf.c[*size - 3] != '#'));
200
201

#ifdef DEBUG
Fabio Utzig's avatar
Fabio Utzig committed
202
203
	printf("<<< received %d bytes\n", *size);
	pretty_print_buf(buf.u8, *size);
204
205
206
207
208
#endif

	return retval;
}

Fabio Utzig's avatar
Fabio Utzig committed
209
static int checksum_and_send(libusb_device_handle *handle, size_t idx, int *xfer)
210
{
211
212
	size_t i;
	uint8_t sum = 0;
Fabio Utzig's avatar
Fabio Utzig committed
213
	int retval, transferred;
214
	int has_ack;
215

216
217
	if (idx + SNPRINTF_OFFSET + END_LEN > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
218

219
220
	for (i = 1; i < idx; i++)
		sum += buf.u8[i];
221

222
	idx += sprintf(buf.c + idx, END "%02x", sum);
223
224

	retval = send_command(handle, idx);
225
226
	if (retval)
		return retval;
227

Fabio Utzig's avatar
Fabio Utzig committed
228
	retval = wait_response(handle, &has_ack, &transferred);
229
230
	if (retval)
		return retval;
231

232
	if (!has_ack)
233
		return LIBUSB_ERROR_OTHER;
234

Fabio Utzig's avatar
Fabio Utzig committed
235
	if (xfer)
Fabio Utzig's avatar
Fabio Utzig committed
236
		*xfer = transferred;
237

Fabio Utzig's avatar
Fabio Utzig committed
238
	/* FIXME: validate transferred here? */
239
240
241
242

	return retval;
}

Fabio Utzig's avatar
Fabio Utzig committed
243

244
245
246
static int send_u8_hex(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
{
	size_t i, idx;
247

248
249
250
251
252
	/* Make sure that everything fits!
	 * START + prefix + hex bytes + END + hex checksum + '\0'
	 */
	if (START_LEN + (prefix ? strlen(prefix) : 0) + (2 * num_bytes) + END_LEN + 1 > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
253

254
	idx = sprintf(buf.c, START "%s", prefix);
255

256
257
	for (i = 0; bytes && i < num_bytes; i++)
		idx += sprintf(buf.c + idx, "%02x", bytes[i]);
258

Fabio Utzig's avatar
Fabio Utzig committed
259
	return checksum_and_send(handle, idx, NULL);
260
261
}

262
static int send_u8_binary(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
263
264
265
{
	size_t idx;

266
267
268
269
270
	/* Make sure that everything fits!
	 * START + prefix + bytes + END + hex checksum + '\0'
	 */
	if (START_LEN + (prefix ? strlen(prefix) : 0) + num_bytes + END_LEN + 1 > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
271

272
	idx = sprintf(buf.c, START "%s", prefix);
273

274
275
	memcpy(buf.c + idx, bytes, num_bytes);
	idx += num_bytes;
276

Fabio Utzig's avatar
Fabio Utzig committed
277
	return checksum_and_send(handle, idx, NULL);
278
279
}

280
static int send_u32(libusb_device_handle *handle, const char *prefix, const uint32_t val, const char *suffix)
281
{
282
283
284
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s",
			prefix ? prefix : "", val,
			suffix ? suffix : "");
285

Fabio Utzig's avatar
Fabio Utzig committed
286
	return checksum_and_send(handle, idx, NULL);
287
288
}

289
static int send_u32_u32(libusb_device_handle *handle, const char *prefix, const uint32_t val1, const char *infix, const uint32_t val2, const char *suffix)
290
{
291
292
293
294
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s%08x%s",
			prefix ? prefix : "", val1,
			infix ? infix : "", val2,
			suffix ? suffix : "");
295

Fabio Utzig's avatar
Fabio Utzig committed
296
	return checksum_and_send(handle, idx, NULL);
297
}
298
299


300
301
302
static int send_mem_write(libusb_device_handle *handle, const uint32_t addr, const uint32_t val)
{
	return send_u32_u32(handle, "X", addr, ",4:", val, NULL);
303
304
}

305
static int send_mem_read(libusb_device_handle *handle, const uint32_t addr, uint32_t *val)
Fabio Utzig's avatar
Fabio Utzig committed
306
{
307
308
309
	int retval = send_u32(handle, "x", addr, ",4");
	if (retval)
		return retval;
310
311
312
313
314
315
316
317
	if (val) {
		uint32_t u = 0;
		u |= buf.u8[8] << 24;
		u |= buf.u8[7] << 16;
		u |= buf.u8[6] << 8;
		u |= buf.u8[5];
		*val = le32_to_cpu(u);
	}
Fabio Utzig's avatar
Fabio Utzig committed
318

319
320
	return 0;
}
Fabio Utzig's avatar
Fabio Utzig committed
321

322
323
324
325
static int send_flash_erase(libusb_device_handle *handle, const uint32_t start, const uint32_t end)
{
	return send_u32_u32(handle, "vFlashErase:", start, ",", end, NULL);
}
Fabio Utzig's avatar
Fabio Utzig committed
326

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
static int send_flash_write(libusb_device_handle *handle, const uint32_t addr, const uint8_t *bytes, size_t len)
{
	size_t i;
	char prefix[] = "vFlashWrite:12345678:";
	char by, rawbuf[1024], *buf = rawbuf;

	sprintf(strchr(prefix, ':') + 1, "%08x:", addr);

	for (i = 0; i < len; i++)
		switch (by = bytes[i]) {
		case '#':
		case '$':
		case '}':
			*buf++ = '}';
			by ^= 0x20;
			/* fall through */
		default:
			if (buf >= rawbuf + sizeof(rawbuf))
				return LIBUSB_ERROR_NO_MEM;

			*buf++ = by;
			break;
Fabio Utzig's avatar
Fabio Utzig committed
349
350
		}

351
	i = buf - rawbuf;
Fabio Utzig's avatar
Fabio Utzig committed
352

353
	return send_u8_binary(handle, prefix, rawbuf, i) ? -1 : i;
Fabio Utzig's avatar
Fabio Utzig committed
354
355
}

Fabio Utzig's avatar
Fabio Utzig committed
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
static int decode_buffer(char *inbuf, int insize, char *outbuf, int outsize)
{
	int i;
	char by, *bp = outbuf;

	for (i = 0; i < insize; i++) {
		switch (by = inbuf[i]) {
			case '}':
				by = inbuf[++i] ^ 0x20;
				/* fall through */
			default:
				if (bp >= outbuf + outsize)
					return LIBUSB_ERROR_NO_MEM;
				*bp++ = by;
				break;
		}
	}

	return 0;
}

Fabio Utzig's avatar
Fabio Utzig committed
377
378
379
static int send_flash_verify(libusb_device_handle *handle, const uint32_t addr, const uint8_t *bytes, size_t len)
{
	size_t i, j;
Fabio Utzig's avatar
Fabio Utzig committed
380
	char rawbuf[1024];
Fabio Utzig's avatar
Fabio Utzig committed
381
	int retval, transferred;
Fabio Utzig's avatar
Fabio Utzig committed
382
383
384

	size_t idx = snprintf(buf.c, BUF_SIZE, START "x%x,%x", addr, (uint32_t)len);

Fabio Utzig's avatar
Fabio Utzig committed
385
	retval = checksum_and_send(handle, idx, &transferred);
386
387
	if (retval)
		return retval;
Fabio Utzig's avatar
Fabio Utzig committed
388

Fabio Utzig's avatar
Fabio Utzig committed
389
	decode_buffer(buf.c, transferred, rawbuf, sizeof rawbuf);
Fabio Utzig's avatar
Fabio Utzig committed
390

391
	if (strncmp(rawbuf, "+$OK:", 5) != 0)
Fabio Utzig's avatar
Fabio Utzig committed
392
393
		return LIBUSB_ERROR_OTHER;

394
	for (i = 0, j = strlen("+$OK:"); i < len; i++, j++) {
Fabio Utzig's avatar
Fabio Utzig committed
395
396
397
398
399
400
401
402
		if (bytes[i] != (uint8_t)rawbuf[j]) {
			return LIBUSB_ERROR_OTHER;
		}
	}

	return 0;
}

Fabio Utzig's avatar
Fabio Utzig committed
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
static int print_icdi_version(libusb_device_handle *handle)
{
	int retval = 0;
	char rawbuf[32];
	size_t i, idx;
	int transferred = 0;
	const char *cmd = "version";

	idx = sprintf(buf.c, START "qRcmd,");

	for (i = 0; i < strlen(cmd); i++)
		idx += sprintf(buf.c + idx, "%02x", cmd[i]);

	retval = checksum_and_send(handle, idx, &transferred);
	if (retval)
		return retval;

	decode_buffer(buf.c, transferred, rawbuf, sizeof rawbuf);

	if (strncmp(rawbuf, "+$", 2) != 0)
		return LIBUSB_ERROR_OTHER;

	printf("ICDI version: ");
	for (i = strlen("+$"); rawbuf[i] != '#'; i += 2) {
		char r = rawbuf[i];
		char c = (r <= '9') ? (r - '0') << 4 : (r - 'a' + 10) << 4;
		r = rawbuf[i+1];
		c |= (r <= '9') ? r - '0' : r - 'a' + 10;
		printf("%c", c);
	}

	return 0;
}

437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
#define SEND_COMMAND(cmd) do { \
	int r = send_u8_hex(handle, "qRcmd,", (cmd), sizeof((cmd)) - 1); \
	if (r) \
		return r; \
} while (0)

#define SEND_STRING(str) do { \
	int r = send_u8_hex(handle, (str), NULL, 0); \
	if (r) \
		return r; \
} while (0)

#define MEM_WRITE(address, value) do { \
	int r = send_mem_write(handle, (address), (value)); \
	if (r) \
		return r; \
} while (0)

#define MEM_READ(address, value) do { \
	int r = send_mem_read(handle, (address), (value)); \
	if (r) \
		return r; \
} while (0)

#define FLASH_ERASE(start, end) do { \
	int r = send_flash_erase(handle, (start), (end)); \
	if (r) \
		return r; \
} while (0)

#define FLASH_WRITE(address, data, length) do { \
	int r = send_flash_write(handle, (address), (data), (length)); \
	if (r < (length)) \
		return LIBUSB_ERROR_OTHER; \
} while (0)

Fabio Utzig's avatar
Fabio Utzig committed
473
474
475
476
477
/*
 *  This flow is of commands is based on an USB capture of
 *  traffic between LM Flash Programmer and the Stellaris Launchpad
 *  when doing a firmware write
 */
478
static int write_firmware(libusb_device_handle *handle, FILE *f)
479
480
{
	uint32_t val = 0;
Fabio Utzig's avatar
Fabio Utzig committed
481
	uint32_t addr;
482
	size_t rdbytes;
483
	int retval = 0;
484
	uint32_t size;
485

Fabio Utzig's avatar
Fabio Utzig committed
486
487
	print_icdi_version(handle);

488
489
490
	SEND_COMMAND("debug clock \0");
	SEND_STRING("qSupported");
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
491
492
493
	MEM_WRITE(FP_CTRL, 0x3000000);
	MEM_READ(DID0, &val);
	MEM_READ(DID1, &val);
494
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
495
	MEM_READ(DHCSR, &val);
496
	SEND_COMMAND("debug sreset");
Fabio Utzig's avatar
Fabio Utzig committed
497
	MEM_READ(DHCSR, &val);
498
499
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
500
501
502
503
504
505
506
	MEM_READ(DHCSR, &val);
	MEM_READ(RCC, &val);
	MEM_READ(DID0, &val);
	MEM_READ(DID1, &val);
	MEM_READ(DC0, &val);
	MEM_READ(DID0, &val);
	MEM_READ(NVMSTAT, &val);
Fabio Utzig's avatar
Fabio Utzig committed
507

508
	MEM_WRITE(FMA, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
509
	MEM_READ(DHCSR, &val);
510
511
512
513

	if (erase_used) {
		fseek(f, 0, SEEK_END);
		size = ftell(f);
514
		for (addr = start_addr; addr < (start_addr + size); addr += FLASH_ERASE_SIZE)
515
516
517
518
519
520
			FLASH_ERASE(addr, FLASH_ERASE_SIZE);
		fseek(f, 0, SEEK_SET);
	} else {
		FLASH_ERASE(0, 0);
	}

521
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
522
	MEM_READ(DHCSR, &val);
Fabio Utzig's avatar
Fabio Utzig committed
523

Fabio Utzig's avatar
Fabio Utzig committed
524
	MEM_WRITE(DHCSR, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
525

526
527
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
528
	MEM_READ(DHCSR, &val);
529

530
	for (addr = start_addr; !feof(f); addr += sizeof(flash_block)) {
531
532
533
534
535
536
537
		rdbytes = fread(flash_block, 1, sizeof(flash_block), f);

		if (rdbytes < sizeof(flash_block) && !feof(f)) {
			perror("fread");
			return LIBUSB_ERROR_OTHER;
		}

538
539
540
541
542
543
		/*
		 * Avoid writing a buffer with zero-sized content which can
		 * happen when the input file has a size multiple of flash_block
		 */
		if (rdbytes)
			FLASH_WRITE(addr, flash_block, rdbytes);
544
545
	}

Fabio Utzig's avatar
Fabio Utzig committed
546
547
548
	if (do_verify) {
		fseek(f, 0, SEEK_SET);

549
		for (addr = start_addr; !feof(f); addr += sizeof(flash_block)) {
Fabio Utzig's avatar
Fabio Utzig committed
550
551
552
553
554
555
556
557
			rdbytes = fread(flash_block, 1, sizeof(flash_block), f);

			if (rdbytes < sizeof(flash_block) && !feof(f)) {
				perror("fread");
				return LIBUSB_ERROR_OTHER;
			}

			/* On error don't return immediately... finish resetting the board */
558
559
560
			retval = send_flash_verify(handle, addr, flash_block, rdbytes);
			if (retval) {
				printf("Error verifying flash\n");
Fabio Utzig's avatar
Fabio Utzig committed
561
				break;
562
			}
Fabio Utzig's avatar
Fabio Utzig committed
563
564
565
		}
	}

566
567
568
569
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	/* reset board */
Fabio Utzig's avatar
Fabio Utzig committed
570
	MEM_WRITE(FP_CTRL, 0x3000000);
571
572
573
574
	SEND_COMMAND("debug hreset");
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

575
	return retval;
576
}
Fabio Utzig's avatar
Fabio Utzig committed
577

578
579
580
581
582
583
584
585

enum flasher_error {
	FLASHER_SUCCESS,
	FLASHER_ERR_LIBUSB_FAILURE,
	FLASHER_ERR_NO_DEVICES,
	FLASHER_ERR_MULTIPLE_DEVICES,
};

586

Fabio Utzig's avatar
Fabio Utzig committed
587
static enum flasher_error
588
589
590
591
592
593
594
595
596
flasher_find_matching_device(
	libusb_context *ctx,
	libusb_device **matching_device_out,
	enum libusb_error *libusb_error_out,
	int vendor_id,
	int product_id,
	const char *serial)
{
	struct libusb_device_descriptor device_descriptor;
Fabio Utzig's avatar
Fabio Utzig committed
597
#ifndef __APPLE__
598
	char descriptor_buffer[256];
Fabio Utzig's avatar
Fabio Utzig committed
599
#endif
600
601
	libusb_device **device_list = NULL;
	libusb_device *matching_device = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
602
#ifndef __APPLE__
603
	libusb_device_handle *handle;
Fabio Utzig's avatar
Fabio Utzig committed
604
#endif
605
606
607
608
609
610
611
	enum flasher_error flasher_error;
	enum libusb_error libusb_error;

	int retval;
	int device_count;
	int device_index;

Fabio Utzig's avatar
Fabio Utzig committed
612
	/* Enumerate all USB devices */
613
614
615
616
617
	retval = libusb_get_device_list(ctx, &device_list);
	if (retval < 0) {
		libusb_error = retval;
		flasher_error = FLASHER_ERR_LIBUSB_FAILURE;
		fprintf(stderr, "Unable to get enumerate USB devices: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
618
		        libusb_error_name(libusb_error));
619
620
621
622
623
624
625
626
627
628
629
630
631
		goto out;
	} else {
		device_count = retval;
		flasher_error = FLASHER_SUCCESS;
		libusb_error = LIBUSB_SUCCESS;
	}

	/* Assume no devices were found */
	flasher_error = FLASHER_ERR_NO_DEVICES;

	/* Walk the list of devices and try to match some */
	for (device_index = 0; device_index < device_count; ++device_index) {
		retval = libusb_get_device_descriptor(
Fabio Utzig's avatar
Fabio Utzig committed
632
		           device_list[device_index], &device_descriptor);
633
634
		if (retval < 0) {
			fprintf(stderr, "Unable to get device descritor: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
635
			        libusb_error_name(retval));
636
637
638
639
640
641
			libusb_error = retval;
			flasher_error = FLASHER_ERR_LIBUSB_FAILURE;
			goto out;
		}
		/* Skip devices that have incorrect vendor and product IDs */
		if (device_descriptor.idVendor != vendor_id ||
Fabio Utzig's avatar
Fabio Utzig committed
642
		    device_descriptor.idProduct != product_id) {
643
644
			continue;
		}
Fabio Utzig's avatar
Fabio Utzig committed
645
646

#ifndef __APPLE__
647
648
649
650
		/* Open each device so that we can read the serial number */
		retval = libusb_open(device_list[device_index], &handle);
		if (retval < 0) {
			fprintf(stderr, "Unable to open USB device: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
651
			        libusb_error_name(retval));
652
653
654
655
656
657
658
659
660
661
			continue;
		}
		/* Read the serial number */
		retval = libusb_get_string_descriptor_ascii(
			handle, device_descriptor.iSerialNumber,
			(unsigned char *)descriptor_buffer, sizeof descriptor_buffer);
		/* Close the handle as we won't need it below */
		libusb_close(handle);
		if (retval < 0) {
			fprintf(stderr, "Unable to get device serial number: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
662
			        libusb_error_name(retval));
663
664
665
666
667
668
			continue;
		}
		printf("Found ICDI device with serial: %s\n", descriptor_buffer);
		/* Skip devices with serial that does not match */
		if (serial != NULL && strcmp(serial, descriptor_buffer) != 0)
			continue;
Fabio Utzig's avatar
Fabio Utzig committed
669
#endif
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
		if (matching_device == NULL) {
			flasher_error = FLASHER_SUCCESS;
			matching_device = device_list[device_index];
		} else {
			/* If there's a device found already then abort */
			flasher_error = FLASHER_ERR_MULTIPLE_DEVICES;
			/* Don't try returning arbitrary "first" device */
			matching_device = NULL;
			goto out;
		}
	}

out:
	/* Ref the matching device as we'll be returning it */
	if (matching_device != NULL && matching_device_out != NULL) {
		libusb_ref_device(matching_device);
		*matching_device_out = matching_device;
	}
	/* Release the device list */
	if (device_list != NULL)
		libusb_free_device_list(device_list, 1);
	/* Store libusb error if requested */
	if (libusb_error_out != NULL)
		*libusb_error_out = libusb_error;
	/* Return the flasher error code */
	return flasher_error;
}

698

Fabio Utzig's avatar
Fabio Utzig committed
699
static void flasher_usage()
700
{
Fabio Utzig's avatar
Fabio Utzig committed
701
702
703
704
705
706
707
	printf("Usage: lm4flash [options] <binary-file>\n");
	printf("\t-V\n");
	printf("\t\tPrint version information\n");
	printf("\t-h\n");
	printf("\t\tPrint usage information\n");
	printf("\t-v\n");
	printf("\t\tEnables verification after write\n");
708
709
	printf("\t-E\n");
	printf("\t\tOnly erase blocks where binary file will be written\n");
710
711
	printf("\t-S address\n");
	printf("\t\tWrite binary at the given address (in hexadecimal)\n");
Fabio Utzig's avatar
Fabio Utzig committed
712
713
	printf("\t-s SERIAL\n");
	printf("\t\tFlash device with the following serial\n");
714
715
}

716

Fabio Utzig's avatar
Fabio Utzig committed
717
static int flasher_flash(const char *serial, const char *rom_name)
Fabio Utzig's avatar
Fabio Utzig committed
718
{
719
	libusb_context *ctx = NULL;
720
	libusb_device *device = NULL;
721
	libusb_device_handle *handle = NULL;
722
	int retval;
723
	FILE *f = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
724

725
726
727
728
	retval = libusb_init(&ctx);

	if (retval != 0) {
		fprintf(stderr, "Error initializing libusb: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
729
		        libusb_error_name(retval));
730
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
731
	}
Fabio Utzig's avatar
Fabio Utzig committed
732

733
	switch (retval = flasher_find_matching_device(
Fabio Utzig's avatar
Fabio Utzig committed
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
	        ctx, &device, &retval, ICDI_VID, ICDI_PID, serial)) {
	case FLASHER_SUCCESS:
		break;
	case FLASHER_ERR_LIBUSB_FAILURE:
		fprintf(stderr, "Error while matching ICDI devices: %s\n",
		        libusb_error_name(retval));
		goto done;
	case FLASHER_ERR_NO_DEVICES:
		fprintf(stderr, "Unable to find any ICDI devices\n");
		goto done;
	case FLASHER_ERR_MULTIPLE_DEVICES:
		if (serial == NULL)
			fprintf(stderr, "Found multiple ICDI devices\n");
		else
			fprintf(stderr, "Found ICDI serial number collision!\n");
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
750
751
	}

752
753
754
	retval = libusb_open(device, &handle);
	if (retval != 0) {
		fprintf(stderr, "Error opening selected device: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
755
		        libusb_error_name(retval));
756
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
757
758
	}

759
	retval = libusb_claim_interface(handle, INTERFACE_NR);
Fabio Utzig's avatar
Fabio Utzig committed
760
	if (retval != 0) {
761
		fprintf(stderr, "Error claiming interface: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
762
		        libusb_error_name(retval));
763
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
764
765
	}

766
	f = fopen(rom_name, "rb");
767
768
	if (!f) {
		perror("fopen");
769
770
		retval = 1;
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
771
772
	}

773
	retval = write_firmware(handle, f);
Fabio Utzig's avatar
Fabio Utzig committed
774

775
done:
776
777
	if (f)
		fclose(f);
778
779
	if (handle)
		libusb_close(handle);
780
781
	if (device)
		libusb_unref_device(device);
782
783
784
785
	if (ctx)
		libusb_exit(ctx);

	return retval;
Fabio Utzig's avatar
Fabio Utzig committed
786
}
787

Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
788
789
790
791
792
793
794

int main(int argc, char *argv[])
{
	const char *serial = NULL;
	const char *rom_name = NULL;
	int opt;

795
	while ((opt = getopt(argc, argv, "VES:hvs:")) != -1) {
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
796
		switch (opt) {
Fabio Utzig's avatar
Fabio Utzig committed
797
798
799
800
801
802
		case 'V':
			show_version();
			return 0;
		case 'h':
			flasher_usage();
			return 0;
803
804
805
		case 'E':
			erase_used = 1;
			break;
806
807
808
809
810
		case 'S':
			start_addr = strtol(optarg, NULL, 16);
			/* force erasing only the used blocks */
			erase_used = 1;
			break;
Fabio Utzig's avatar
Fabio Utzig committed
811
812
813
814
815
816
817
818
819
		case 'v':
			do_verify = 1;
			break;
		case 's':
			serial = optarg;
			break;
		default:
			flasher_usage();
			return EXIT_FAILURE;
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
820
821
		}
	}
Fabio Utzig's avatar
Fabio Utzig committed
822

Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
823
824
825
826
827
	if (optind >= argc) {
		flasher_usage();
		return EXIT_FAILURE;
	} else
		rom_name = argv[optind];
Fabio Utzig's avatar
Fabio Utzig committed
828

829
830
831
832
833
	if (start_addr && (start_addr % FLASH_ERASE_SIZE)) {
		printf("Address given to -S must be 0x%x aligned\n", FLASH_ERASE_SIZE);
		return EXIT_FAILURE;
	}

Fabio Utzig's avatar
Fabio Utzig committed
834
	return flasher_flash(serial, rom_name);
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
835
}