lm4flash.c 20 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",
95
	       "LM4Flash version 0.1.2 - 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;
Fabio Utzig's avatar
Fabio Utzig committed
310

311
312
	if (val)
		*val = le32_to_cpu(buf.u32[0]);
Fabio Utzig's avatar
Fabio Utzig committed
313

314
315
	return 0;
}
Fabio Utzig's avatar
Fabio Utzig committed
316

317
318
319
320
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
321

322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
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
344
345
		}

346
	i = buf - rawbuf;
Fabio Utzig's avatar
Fabio Utzig committed
347

348
	return send_u8_binary(handle, prefix, rawbuf, i) ? -1 : i;
Fabio Utzig's avatar
Fabio Utzig committed
349
350
}

Fabio Utzig's avatar
Fabio Utzig committed
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
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
372
373
374
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
375
	char rawbuf[1024];
Fabio Utzig's avatar
Fabio Utzig committed
376
	int retval, transferred;
Fabio Utzig's avatar
Fabio Utzig committed
377
378
379

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

Fabio Utzig's avatar
Fabio Utzig committed
380
	retval = checksum_and_send(handle, idx, &transferred);
381
382
	if (retval)
		return retval;
Fabio Utzig's avatar
Fabio Utzig committed
383

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

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

389
	for (i = 0, j = strlen("+$OK:"); i < len; i++, j++) {
Fabio Utzig's avatar
Fabio Utzig committed
390
391
392
393
394
395
396
397
		if (bytes[i] != (uint8_t)rawbuf[j]) {
			return LIBUSB_ERROR_OTHER;
		}
	}

	return 0;
}

Fabio Utzig's avatar
Fabio Utzig committed
398
399
400
401
402
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
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;
}

432
433
434
435
436
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
#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
468
469
470
471
472
/*
 *  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
 */
473
static int write_firmware(libusb_device_handle *handle, FILE *f)
474
475
{
	uint32_t val = 0;
Fabio Utzig's avatar
Fabio Utzig committed
476
	uint32_t addr;
477
	size_t rdbytes;
478
	int retval = 0;
479
	uint32_t size;
480

Fabio Utzig's avatar
Fabio Utzig committed
481
482
	print_icdi_version(handle);

483
484
485
	SEND_COMMAND("debug clock \0");
	SEND_STRING("qSupported");
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
486
487
488
	MEM_WRITE(FP_CTRL, 0x3000000);
	MEM_READ(DID0, &val);
	MEM_READ(DID1, &val);
489
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
490
	MEM_READ(DHCSR, &val);
491
	SEND_COMMAND("debug sreset");
Fabio Utzig's avatar
Fabio Utzig committed
492
	MEM_READ(DHCSR, &val);
493
494
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
495
496
497
498
499
500
501
	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
502

503
	MEM_WRITE(FMA, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
504
	MEM_READ(DHCSR, &val);
505
506
507
508

	if (erase_used) {
		fseek(f, 0, SEEK_END);
		size = ftell(f);
509
		for (addr = start_addr; addr < (start_addr + size); addr += FLASH_ERASE_SIZE)
510
511
512
513
514
515
			FLASH_ERASE(addr, FLASH_ERASE_SIZE);
		fseek(f, 0, SEEK_SET);
	} else {
		FLASH_ERASE(0, 0);
	}

516
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
517
	MEM_READ(DHCSR, &val);
Fabio Utzig's avatar
Fabio Utzig committed
518

Fabio Utzig's avatar
Fabio Utzig committed
519
	MEM_WRITE(DHCSR, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
520

521
522
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
523
	MEM_READ(DHCSR, &val);
524

525
	for (addr = start_addr; !feof(f); addr += sizeof(flash_block)) {
526
527
528
529
530
531
532
		rdbytes = fread(flash_block, 1, sizeof(flash_block), f);

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

533
534
535
536
537
538
		/*
		 * 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);
539
540
	}

Fabio Utzig's avatar
Fabio Utzig committed
541
542
543
	if (do_verify) {
		fseek(f, 0, SEEK_SET);

544
		for (addr = start_addr; !feof(f); addr += sizeof(flash_block)) {
Fabio Utzig's avatar
Fabio Utzig committed
545
546
547
548
549
550
551
552
			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 */
553
554
555
			retval = send_flash_verify(handle, addr, flash_block, rdbytes);
			if (retval) {
				printf("Error verifying flash\n");
Fabio Utzig's avatar
Fabio Utzig committed
556
				break;
557
			}
Fabio Utzig's avatar
Fabio Utzig committed
558
559
560
		}
	}

561
562
563
564
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	/* reset board */
Fabio Utzig's avatar
Fabio Utzig committed
565
	MEM_WRITE(FP_CTRL, 0x3000000);
566
567
568
569
	SEND_COMMAND("debug hreset");
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

570
	return retval;
571
}
Fabio Utzig's avatar
Fabio Utzig committed
572

573
574
575
576
577
578
579
580

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

581

Fabio Utzig's avatar
Fabio Utzig committed
582
static enum flasher_error
583
584
585
586
587
588
589
590
591
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
592
#ifndef __APPLE__
593
	char descriptor_buffer[256];
Fabio Utzig's avatar
Fabio Utzig committed
594
#endif
595
596
	libusb_device **device_list = NULL;
	libusb_device *matching_device = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
597
#ifndef __APPLE__
598
	libusb_device_handle *handle;
Fabio Utzig's avatar
Fabio Utzig committed
599
#endif
600
601
602
603
604
605
606
	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
607
	/* Enumerate all USB devices */
608
609
610
611
612
	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
613
		        libusb_error_name(libusb_error));
614
615
616
617
618
619
620
621
622
623
624
625
626
		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
627
		           device_list[device_index], &device_descriptor);
628
629
		if (retval < 0) {
			fprintf(stderr, "Unable to get device descritor: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
630
			        libusb_error_name(retval));
631
632
633
634
635
636
			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
637
		    device_descriptor.idProduct != product_id) {
638
639
			continue;
		}
Fabio Utzig's avatar
Fabio Utzig committed
640
641

#ifndef __APPLE__
642
643
644
645
		/* 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
646
			        libusb_error_name(retval));
647
648
649
650
651
652
653
654
655
656
			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
657
			        libusb_error_name(retval));
658
659
660
661
662
663
			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
664
#endif
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
		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;
}

693

Fabio Utzig's avatar
Fabio Utzig committed
694
static void flasher_usage()
695
{
Fabio Utzig's avatar
Fabio Utzig committed
696
697
698
699
700
701
702
	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");
703
704
	printf("\t-E\n");
	printf("\t\tOnly erase blocks where binary file will be written\n");
705
706
	printf("\t-S address\n");
	printf("\t\tWrite binary at the given address (in hexadecimal)\n");
Fabio Utzig's avatar
Fabio Utzig committed
707
708
	printf("\t-s SERIAL\n");
	printf("\t\tFlash device with the following serial\n");
709
710
}

711

Fabio Utzig's avatar
Fabio Utzig committed
712
static int flasher_flash(const char *serial, const char *rom_name)
Fabio Utzig's avatar
Fabio Utzig committed
713
{
714
	libusb_context *ctx = NULL;
715
	libusb_device *device = NULL;
716
	libusb_device_handle *handle = NULL;
717
	int retval;
718
	FILE *f = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
719

720
721
722
723
	retval = libusb_init(&ctx);

	if (retval != 0) {
		fprintf(stderr, "Error initializing libusb: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
724
		        libusb_error_name(retval));
725
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
726
	}
Fabio Utzig's avatar
Fabio Utzig committed
727

728
	switch (flasher_find_matching_device(
Fabio Utzig's avatar
Fabio Utzig committed
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
	        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
745
746
	}

747
748
749
	retval = libusb_open(device, &handle);
	if (retval != 0) {
		fprintf(stderr, "Error opening selected device: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
750
		        libusb_error_name(retval));
751
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
752
753
	}

754
	retval = libusb_claim_interface(handle, INTERFACE_NR);
Fabio Utzig's avatar
Fabio Utzig committed
755
	if (retval != 0) {
756
		fprintf(stderr, "Error claiming interface: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
757
		        libusb_error_name(retval));
758
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
759
760
	}

761
	f = fopen(rom_name, "rb");
762
763
	if (!f) {
		perror("fopen");
764
765
		retval = 1;
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
766
767
	}

768
	retval = write_firmware(handle, f);
Fabio Utzig's avatar
Fabio Utzig committed
769

770
done:
771
772
	if (f)
		fclose(f);
773
774
	if (handle)
		libusb_close(handle);
775
776
	if (device)
		libusb_unref_device(device);
777
778
779
780
	if (ctx)
		libusb_exit(ctx);

	return retval;
Fabio Utzig's avatar
Fabio Utzig committed
781
}
782

Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
783
784
785
786
787
788
789

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

790
	while ((opt = getopt(argc, argv, "VES:hvs:")) != -1) {
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
791
		switch (opt) {
Fabio Utzig's avatar
Fabio Utzig committed
792
793
794
795
796
797
		case 'V':
			show_version();
			return 0;
		case 'h':
			flasher_usage();
			return 0;
798
799
800
		case 'E':
			erase_used = 1;
			break;
801
802
803
804
805
		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
806
807
808
809
810
811
812
813
814
		case 'v':
			do_verify = 1;
			break;
		case 's':
			serial = optarg;
			break;
		default:
			flasher_usage();
			return EXIT_FAILURE;
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
815
816
		}
	}
Fabio Utzig's avatar
Fabio Utzig committed
817

Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
818
819
820
821
822
	if (optind >= argc) {
		flasher_usage();
		return EXIT_FAILURE;
	} else
		rom_name = argv[optind];
Fabio Utzig's avatar
Fabio Utzig committed
823

824
825
826
827
828
	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
829
	return flasher_flash(serial, rom_name);
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
830
}