lm4flash.c 18.9 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

81
82
83
84
85
86
87
88
89
90
/* 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
91
92
93
94
95
96
97
98
99
100
101
102
void show_version(void)
{
	printf("%s",
	       "LM4Flash version 0.1 - Flasher for Stellaris Launchpad ICDI boards\n"
	       "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"
	);
}

103
static uint32_t le32_to_cpu(const uint32_t x)
104
{
105
106
107
108
109
110
111
112
113
	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;
114
115
}

Fabio Utzig's avatar
Fabio Utzig committed
116
117
static int do_verify = 0;

118
119
#define cpu_to_le32 le32_to_cpu

Fabio Utzig's avatar
Fabio Utzig committed
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
#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

153
154
155
156
157
158
static int send_command(libusb_device_handle *handle, int size)
{
	int transferred = 0;
	int retval;

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

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

	return retval;
}

171
static int wait_response(libusb_device_handle *handle, int *has_ack, int *size)
172
173
{
	int retval;
174
	int transferred = 0;
175

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
	*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] != '#'));
197
198

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

	return retval;
}

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

213
214
	if (idx + SNPRINTF_OFFSET + END_LEN > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
215

216
217
	for (i = 1; i < idx; i++)
		sum += buf.u8[i];
218

219
	idx += sprintf(buf.c + idx, END "%02x", sum);
220
221

	retval = send_command(handle, idx);
222
223
	if (retval)
		return retval;
224

Fabio Utzig's avatar
Fabio Utzig committed
225
	retval = wait_response(handle, &has_ack, &transferred);
226
227
	if (retval)
		return retval;
228

229
	if (!has_ack)
230
		return LIBUSB_ERROR_OTHER;
231

Fabio Utzig's avatar
Fabio Utzig committed
232
	if (xfer)
Fabio Utzig's avatar
Fabio Utzig committed
233
		*xfer = transferred;
234

Fabio Utzig's avatar
Fabio Utzig committed
235
	/* FIXME: validate transferred here? */
236
237
238
239

	return retval;
}

Fabio Utzig's avatar
Fabio Utzig committed
240

241
242
243
static int send_u8_hex(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
{
	size_t i, idx;
244

245
246
247
248
249
	/* 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;
250

251
	idx = sprintf(buf.c, START "%s", prefix);
252

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

Fabio Utzig's avatar
Fabio Utzig committed
256
	return checksum_and_send(handle, idx, NULL);
257
258
}

259
static int send_u8_binary(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
260
261
262
{
	size_t idx;

263
264
265
266
267
	/* 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;
268

269
	idx = sprintf(buf.c, START "%s", prefix);
270

271
272
	memcpy(buf.c + idx, bytes, num_bytes);
	idx += num_bytes;
273

Fabio Utzig's avatar
Fabio Utzig committed
274
	return checksum_and_send(handle, idx, NULL);
275
276
}

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

Fabio Utzig's avatar
Fabio Utzig committed
283
	return checksum_and_send(handle, idx, NULL);
284
285
}

286
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)
287
{
288
289
290
291
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s%08x%s",
			prefix ? prefix : "", val1,
			infix ? infix : "", val2,
			suffix ? suffix : "");
292

Fabio Utzig's avatar
Fabio Utzig committed
293
	return checksum_and_send(handle, idx, NULL);
294
}
295
296


297
298
299
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);
300
301
}

302
static int send_mem_read(libusb_device_handle *handle, const uint32_t addr, uint32_t *val)
Fabio Utzig's avatar
Fabio Utzig committed
303
{
304
305
306
	int retval = send_u32(handle, "x", addr, ",4");
	if (retval)
		return retval;
Fabio Utzig's avatar
Fabio Utzig committed
307

308
309
	if (val)
		*val = le32_to_cpu(buf.u32[0]);
Fabio Utzig's avatar
Fabio Utzig committed
310

311
312
	return 0;
}
Fabio Utzig's avatar
Fabio Utzig committed
313

314
315
316
317
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
318

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

343
	i = buf - rawbuf;
Fabio Utzig's avatar
Fabio Utzig committed
344

345
	return send_u8_binary(handle, prefix, rawbuf, i) ? -1 : i;
Fabio Utzig's avatar
Fabio Utzig committed
346
347
}

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

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

Fabio Utzig's avatar
Fabio Utzig committed
377
	retval = checksum_and_send(handle, idx, &transferred);
378
379
	if (retval)
		return retval;
Fabio Utzig's avatar
Fabio Utzig committed
380

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

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

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

	return 0;
}

Fabio Utzig's avatar
Fabio Utzig committed
395
396
397
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
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;
}

429
430
431
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
#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
465
466
467
468
469
/*
 *  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
 */
470
static int write_firmware(libusb_device_handle *handle, FILE *f)
471
472
{
	uint32_t val = 0;
Fabio Utzig's avatar
Fabio Utzig committed
473
	uint32_t addr;
474
	size_t rdbytes;
475
	int retval = 0;
476

Fabio Utzig's avatar
Fabio Utzig committed
477
478
	print_icdi_version(handle);

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

499
	MEM_WRITE(FMA, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
500
	MEM_READ(DHCSR, &val);
501
502
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
503
	MEM_READ(DHCSR, &val);
Fabio Utzig's avatar
Fabio Utzig committed
504

Fabio Utzig's avatar
Fabio Utzig committed
505
	MEM_WRITE(DHCSR, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
506

507
508
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
509
	MEM_READ(DHCSR, &val);
510
511
512
513
514
515
516
517
518
519
520
521

	for (addr = 0; !feof(f); addr += sizeof(flash_block)) {
		rdbytes = fread(flash_block, 1, sizeof(flash_block), f);

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

		FLASH_WRITE(addr, flash_block, rdbytes);
	}

Fabio Utzig's avatar
Fabio Utzig committed
522
523
524
525
526
527
528
529
530
531
532
533
	if (do_verify) {
		fseek(f, 0, SEEK_SET);

		for (addr = 0; !feof(f); addr += sizeof(flash_block)) {
			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 */
534
535
536
			retval = send_flash_verify(handle, addr, flash_block, rdbytes);
			if (retval) {
				printf("Error verifying flash\n");
Fabio Utzig's avatar
Fabio Utzig committed
537
				break;
538
			}
Fabio Utzig's avatar
Fabio Utzig committed
539
540
541
		}
	}

542
543
544
545
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	/* reset board */
Fabio Utzig's avatar
Fabio Utzig committed
546
	MEM_WRITE(FP_CTRL, 0x3000000);
547
548
549
550
	SEND_COMMAND("debug hreset");
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

551
	return retval;
552
}
Fabio Utzig's avatar
Fabio Utzig committed
553

554
555
556
557
558
559
560
561

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

562

Fabio Utzig's avatar
Fabio Utzig committed
563
static enum flasher_error
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
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;
	char descriptor_buffer[256];
	libusb_device **device_list = NULL;
	libusb_device *matching_device = NULL;
	libusb_device_handle *handle;
	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
584
	/* Enumerate all USB devices */
585
586
587
588
589
	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
590
		        libusb_error_name(libusb_error));
591
592
593
594
595
596
597
598
599
600
601
602
603
		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
604
		           device_list[device_index], &device_descriptor);
605
606
		if (retval < 0) {
			fprintf(stderr, "Unable to get device descritor: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
607
			        libusb_error_name(retval));
608
609
610
611
612
613
			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
614
		    device_descriptor.idProduct != product_id) {
615
616
617
618
619
620
			continue;
		}
		/* 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
621
			        libusb_error_name(retval));
622
623
624
625
626
627
628
629
630
631
			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
632
			        libusb_error_name(retval));
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
			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;
		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;
}

667

Fabio Utzig's avatar
Fabio Utzig committed
668
static void flasher_usage()
669
{
Fabio Utzig's avatar
Fabio Utzig committed
670
671
672
673
674
675
676
677
678
	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");
	printf("\t-s SERIAL\n");
	printf("\t\tFlash device with the following serial\n");
679
680
}

681

Fabio Utzig's avatar
Fabio Utzig committed
682
static int flasher_flash(const char *serial, const char *rom_name)
Fabio Utzig's avatar
Fabio Utzig committed
683
{
684
	libusb_context *ctx = NULL;
685
	libusb_device *device = NULL;
686
	libusb_device_handle *handle = NULL;
687
	int retval;
688
	FILE *f = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
689

690
691
692
693
	retval = libusb_init(&ctx);

	if (retval != 0) {
		fprintf(stderr, "Error initializing libusb: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
694
		        libusb_error_name(retval));
695
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
696
	}
Fabio Utzig's avatar
Fabio Utzig committed
697

698
	switch (flasher_find_matching_device(
Fabio Utzig's avatar
Fabio Utzig committed
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
	        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
715
716
	}

717
718
719
	retval = libusb_open(device, &handle);
	if (retval != 0) {
		fprintf(stderr, "Error opening selected device: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
720
		        libusb_error_name(retval));
721
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
722
723
	}

724
	retval = libusb_claim_interface(handle, INTERFACE_NR);
Fabio Utzig's avatar
Fabio Utzig committed
725
	if (retval != 0) {
726
		fprintf(stderr, "Error claiming interface: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
727
		        libusb_error_name(retval));
728
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
729
730
	}

731
	f = fopen(rom_name, "rb");
732
733
	if (!f) {
		perror("fopen");
734
735
		retval = 1;
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
736
737
	}

738
	retval = write_firmware(handle, f);
Fabio Utzig's avatar
Fabio Utzig committed
739

740
done:
741
742
	if (f)
		fclose(f);
743
744
	if (handle)
		libusb_close(handle);
745
746
	if (device)
		libusb_unref_device(device);
747
748
749
750
	if (ctx)
		libusb_exit(ctx);

	return retval;
Fabio Utzig's avatar
Fabio Utzig committed
751
}
752

Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
753
754
755
756
757
758
759

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

Fabio Utzig's avatar
Fabio Utzig committed
760
	while ((opt = getopt(argc, argv, "Vhvs:")) != -1) {
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
761
		switch (opt) {
Fabio Utzig's avatar
Fabio Utzig committed
762
763
764
765
766
767
		case 'V':
			show_version();
			return 0;
		case 'h':
			flasher_usage();
			return 0;
Fabio Utzig's avatar
Fabio Utzig committed
768
769
770
771
772
773
774
775
776
		case 'v':
			do_verify = 1;
			break;
		case 's':
			serial = optarg;
			break;
		default:
			flasher_usage();
			return EXIT_FAILURE;
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
777
778
		}
	}
Fabio Utzig's avatar
Fabio Utzig committed
779

Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
780
781
782
783
784
	if (optind >= argc) {
		flasher_usage();
		return EXIT_FAILURE;
	} else
		rom_name = argv[optind];
Fabio Utzig's avatar
Fabio Utzig committed
785
786

	return flasher_flash(serial, rom_name);
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
787
}