lm4flash.c 18.2 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
210
	size_t i;
	uint8_t sum = 0;
	int retval, transfered;
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

225
	retval = wait_response(handle, &has_ack, &transfered);
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
233
	if (xfer)
		*xfer = transfered;
234

235
	/* FIXME: validate transfered 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
static int send_flash_verify(libusb_device_handle *handle, const uint32_t addr, const uint8_t *bytes, size_t len)
{
	size_t i, j;
	char by, rawbuf[1024], *bp = rawbuf;
	int retval, transfered;

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

	retval = checksum_and_send(handle, idx, &transfered);
357
358
	if (retval)
		return retval;
Fabio Utzig's avatar
Fabio Utzig committed
359
360
361

	for (i = 0; i < transfered; i++) {
		switch (by = buf.u8[i]) {
362
363
364
365
366
367
368
369
			case '}':
				by = buf.u8[++i] ^ 0x20;
				/* fall through */
			default:
				if (bp >= rawbuf + sizeof(rawbuf))
					return LIBUSB_ERROR_NO_MEM;
				*bp++ = by;
				break;
Fabio Utzig's avatar
Fabio Utzig committed
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
		}
	}

	if (strncmp(rawbuf, "$OK:", 4) != 0)
		return LIBUSB_ERROR_OTHER;

	for (i = 0, j = strlen("$OK:"); i < len; i++, j++) {
		if (bytes[i] != (uint8_t)rawbuf[j]) {
			printf("Error verifying flash\n");
			return LIBUSB_ERROR_OTHER;
		}
	}

	return 0;
}

386
387
388
389
390
391
392
393
394
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
#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
422
423
424
425
426
/*
 *  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
 */
427
static int write_firmware(libusb_device_handle *handle, FILE *f)
428
429
{
	uint32_t val = 0;
Fabio Utzig's avatar
Fabio Utzig committed
430
	uint32_t addr;
431
432
433
434
435
	size_t rdbytes;

	SEND_COMMAND("debug clock \0");
	SEND_STRING("qSupported");
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
436
437
438
	MEM_WRITE(FP_CTRL, 0x3000000);
	MEM_READ(DID0, &val);
	MEM_READ(DID1, &val);
439
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
440
	MEM_READ(DHCSR, &val);
441
	SEND_COMMAND("debug sreset");
Fabio Utzig's avatar
Fabio Utzig committed
442
	MEM_READ(DHCSR, &val);
443
444
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
445
446
447
448
449
450
451
	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
452
453

	/* XXX: Repeated below, why? */
454
	MEM_WRITE(FMA, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
455
	MEM_READ(DHCSR, &val);
456
457
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
458
	MEM_READ(DHCSR, &val);
Fabio Utzig's avatar
Fabio Utzig committed
459

Fabio Utzig's avatar
Fabio Utzig committed
460
	MEM_WRITE(DHCSR, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
461
462

	/* XXX: this is the same sequence of the above commands? */
463
	MEM_WRITE(FMA, 0x200);
Fabio Utzig's avatar
Fabio Utzig committed
464
	MEM_READ(DHCSR, &val);
465
466
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
467
	MEM_READ(DHCSR, &val);
468
469
470

	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
471
	MEM_READ(DHCSR, &val);
472
473
474
475
476
477
478
479
480
481
482
483

	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
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
	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 */
			if (send_flash_verify(handle, addr, flash_block, rdbytes) != 0)
				break;
		}
	}

501
502
503
504
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	/* reset board */
Fabio Utzig's avatar
Fabio Utzig committed
505
	MEM_WRITE(FP_CTRL, 0x3000000);
506
507
508
509
510
	SEND_COMMAND("debug hreset");
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	return 0;
511
}
Fabio Utzig's avatar
Fabio Utzig committed
512

513
514
515
516
517
518
519
520

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

521

Fabio Utzig's avatar
Fabio Utzig committed
522
static enum flasher_error
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
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
543
	/* Enumerate all USB devices */
544
545
546
547
548
	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
549
		        libusb_error_name(libusb_error));
550
551
552
553
554
555
556
557
558
559
560
561
562
		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
563
		           device_list[device_index], &device_descriptor);
564
565
		if (retval < 0) {
			fprintf(stderr, "Unable to get device descritor: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
566
			        libusb_error_name(retval));
567
568
569
570
571
572
			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
573
		    device_descriptor.idProduct != product_id) {
574
575
576
577
578
579
			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
580
			        libusb_error_name(retval));
581
582
583
584
585
586
587
588
589
590
			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
591
			        libusb_error_name(retval));
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
			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;
}

626

Fabio Utzig's avatar
Fabio Utzig committed
627
static void flasher_usage()
628
{
Fabio Utzig's avatar
Fabio Utzig committed
629
630
631
632
633
634
635
636
637
	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");
638
639
}

640

Fabio Utzig's avatar
Fabio Utzig committed
641
static int flasher_flash(const char *serial, const char *rom_name)
Fabio Utzig's avatar
Fabio Utzig committed
642
{
643
	libusb_context *ctx = NULL;
644
	libusb_device *device = NULL;
645
	libusb_device_handle *handle = NULL;
646
	int retval;
647
	FILE *f = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
648

649
650
651
652
	retval = libusb_init(&ctx);

	if (retval != 0) {
		fprintf(stderr, "Error initializing libusb: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
653
		        libusb_error_name(retval));
654
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
655
	}
Fabio Utzig's avatar
Fabio Utzig committed
656

657
	switch (flasher_find_matching_device(
Fabio Utzig's avatar
Fabio Utzig committed
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
	        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
674
675
	}

676
677
678
	retval = libusb_open(device, &handle);
	if (retval != 0) {
		fprintf(stderr, "Error opening selected device: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
679
		        libusb_error_name(retval));
680
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
681
682
	}

683
	retval = libusb_claim_interface(handle, INTERFACE_NR);
Fabio Utzig's avatar
Fabio Utzig committed
684
	if (retval != 0) {
685
		fprintf(stderr, "Error claiming interface: %s\n",
Fabio Utzig's avatar
Fabio Utzig committed
686
		        libusb_error_name(retval));
687
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
688
689
	}

690
	f = fopen(rom_name, "rb");
691
692
	if (!f) {
		perror("fopen");
693
694
		retval = 1;
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
695
696
	}

697
	retval = write_firmware(handle, f);
Fabio Utzig's avatar
Fabio Utzig committed
698

699
done:
700
701
	if (f)
		fclose(f);
702
703
	if (handle)
		libusb_close(handle);
704
705
	if (device)
		libusb_unref_device(device);
706
707
708
709
	if (ctx)
		libusb_exit(ctx);

	return retval;
Fabio Utzig's avatar
Fabio Utzig committed
710
}
711

Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
712
713
714
715
716
717
718

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

Fabio Utzig's avatar
Fabio Utzig committed
719
	while ((opt = getopt(argc, argv, "Vhvs:")) != -1) {
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
720
		switch (opt) {
Fabio Utzig's avatar
Fabio Utzig committed
721
722
723
724
725
726
		case 'V':
			show_version();
			return 0;
		case 'h':
			flasher_usage();
			return 0;
Fabio Utzig's avatar
Fabio Utzig committed
727
728
729
730
731
732
733
734
735
		case 'v':
			do_verify = 1;
			break;
		case 's':
			serial = optarg;
			break;
		default:
			flasher_usage();
			return EXIT_FAILURE;
Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
736
737
		}
	}
Fabio Utzig's avatar
Fabio Utzig committed
738

Zygmunt Krynicki's avatar
Zygmunt Krynicki committed
739
740
741
742
743
	if (optind >= argc) {
		flasher_usage();
		return EXIT_FAILURE;
	} else
		rom_name = argv[optind];
Fabio Utzig's avatar
Fabio Utzig committed
744
745

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