libusb 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. data/.gitignore +8 -0
  2. data/.travis.yml +10 -0
  3. data/.yardopts +6 -1
  4. data/Gemfile +16 -0
  5. data/{History.txt → History.md} +28 -16
  6. data/README.md +144 -0
  7. data/Rakefile +28 -24
  8. data/ext/extconf.rb +33 -0
  9. data/ext/libusbx-1.0.14/AUTHORS +50 -0
  10. data/ext/libusbx-1.0.14/COPYING +504 -0
  11. data/ext/libusbx-1.0.14/ChangeLog +139 -0
  12. data/ext/libusbx-1.0.14/INSTALL +234 -0
  13. data/ext/libusbx-1.0.14/Makefile.am +23 -0
  14. data/ext/libusbx-1.0.14/Makefile.in +803 -0
  15. data/ext/libusbx-1.0.14/NEWS +2 -0
  16. data/ext/libusbx-1.0.14/PORTING +94 -0
  17. data/ext/libusbx-1.0.14/README +28 -0
  18. data/ext/libusbx-1.0.14/THANKS +7 -0
  19. data/ext/libusbx-1.0.14/TODO +2 -0
  20. data/ext/libusbx-1.0.14/aclocal.m4 +9480 -0
  21. data/ext/libusbx-1.0.14/compile +143 -0
  22. data/ext/libusbx-1.0.14/config.guess +1501 -0
  23. data/ext/libusbx-1.0.14/config.h.in +116 -0
  24. data/ext/libusbx-1.0.14/config.sub +1705 -0
  25. data/ext/libusbx-1.0.14/configure +14818 -0
  26. data/ext/libusbx-1.0.14/configure.ac +230 -0
  27. data/ext/libusbx-1.0.14/depcomp +630 -0
  28. data/ext/libusbx-1.0.14/doc/Makefile.am +9 -0
  29. data/ext/libusbx-1.0.14/doc/Makefile.in +380 -0
  30. data/ext/libusbx-1.0.14/doc/doxygen.cfg.in +1288 -0
  31. data/ext/libusbx-1.0.14/examples/Makefile.am +18 -0
  32. data/ext/libusbx-1.0.14/examples/Makefile.in +596 -0
  33. data/ext/libusbx-1.0.14/examples/dpfp.c +506 -0
  34. data/ext/libusbx-1.0.14/examples/dpfp_threaded.c +544 -0
  35. data/ext/libusbx-1.0.14/examples/ezusb.c +616 -0
  36. data/ext/libusbx-1.0.14/examples/ezusb.h +107 -0
  37. data/ext/libusbx-1.0.14/examples/fxload.c +261 -0
  38. data/ext/libusbx-1.0.14/examples/getopt/getopt.c +1060 -0
  39. data/ext/libusbx-1.0.14/examples/getopt/getopt.h +180 -0
  40. data/ext/libusbx-1.0.14/examples/getopt/getopt1.c +188 -0
  41. data/ext/libusbx-1.0.14/examples/listdevs.c +63 -0
  42. data/ext/libusbx-1.0.14/examples/xusb.c +1036 -0
  43. data/ext/libusbx-1.0.14/install-sh +520 -0
  44. data/ext/libusbx-1.0.14/libusb-1.0.pc.in +11 -0
  45. data/ext/libusbx-1.0.14/libusb/Makefile.am +56 -0
  46. data/ext/libusbx-1.0.14/libusb/Makefile.in +721 -0
  47. data/ext/libusbx-1.0.14/libusb/core.c +1951 -0
  48. data/ext/libusbx-1.0.14/libusb/descriptor.c +731 -0
  49. data/ext/libusbx-1.0.14/libusb/io.c +2450 -0
  50. data/ext/libusbx-1.0.14/libusb/libusb-1.0.def +126 -0
  51. data/ext/libusbx-1.0.14/libusb/libusb-1.0.rc +59 -0
  52. data/ext/libusbx-1.0.14/libusb/libusb.h +1506 -0
  53. data/ext/libusbx-1.0.14/libusb/libusbi.h +910 -0
  54. data/ext/libusbx-1.0.14/libusb/os/darwin_usb.c +1807 -0
  55. data/ext/libusbx-1.0.14/libusb/os/darwin_usb.h +169 -0
  56. data/ext/libusbx-1.0.14/libusb/os/linux_usbfs.c +2569 -0
  57. data/ext/libusbx-1.0.14/libusb/os/linux_usbfs.h +149 -0
  58. data/ext/libusbx-1.0.14/libusb/os/openbsd_usb.c +727 -0
  59. data/ext/libusbx-1.0.14/libusb/os/poll_posix.h +10 -0
  60. data/ext/libusbx-1.0.14/libusb/os/poll_windows.c +747 -0
  61. data/ext/libusbx-1.0.14/libusb/os/poll_windows.h +114 -0
  62. data/ext/libusbx-1.0.14/libusb/os/threads_posix.c +80 -0
  63. data/ext/libusbx-1.0.14/libusb/os/threads_posix.h +50 -0
  64. data/ext/libusbx-1.0.14/libusb/os/threads_windows.c +211 -0
  65. data/ext/libusbx-1.0.14/libusb/os/threads_windows.h +87 -0
  66. data/ext/libusbx-1.0.14/libusb/os/windows_usb.c +4369 -0
  67. data/ext/libusbx-1.0.14/libusb/os/windows_usb.h +979 -0
  68. data/ext/libusbx-1.0.14/libusb/sync.c +321 -0
  69. data/ext/libusbx-1.0.14/libusb/version.h +18 -0
  70. data/ext/libusbx-1.0.14/libusb/version_nano.h +1 -0
  71. data/ext/libusbx-1.0.14/ltmain.sh +9636 -0
  72. data/ext/libusbx-1.0.14/missing +376 -0
  73. data/lib/libusb.rb +2 -3
  74. data/lib/libusb/call.rb +49 -7
  75. data/lib/libusb/compat.rb +15 -9
  76. data/lib/libusb/configuration.rb +15 -3
  77. data/lib/libusb/constants.rb +19 -6
  78. data/lib/libusb/context.rb +181 -3
  79. data/lib/libusb/dev_handle.rb +91 -40
  80. data/lib/libusb/endpoint.rb +41 -14
  81. data/lib/libusb/eventmachine.rb +183 -0
  82. data/lib/libusb/transfer.rb +21 -8
  83. data/lib/libusb/version_gem.rb +19 -0
  84. data/lib/libusb/{version.rb → version_struct.rb} +0 -0
  85. data/libusb.gemspec +31 -0
  86. data/test/test_libusb_compat.rb +1 -1
  87. data/test/test_libusb_compat_mass_storage.rb +2 -2
  88. data/test/test_libusb_descriptors.rb +1 -1
  89. data/test/test_libusb_event_machine.rb +118 -0
  90. data/test/test_libusb_iso_transfer.rb +6 -1
  91. data/test/test_libusb_mass_storage.rb +9 -3
  92. data/test/test_libusb_mass_storage2.rb +1 -1
  93. data/test/test_libusb_structs.rb +45 -0
  94. data/test/test_libusb_threads.rb +89 -0
  95. data/test/test_libusb_version.rb +4 -0
  96. metadata +109 -44
  97. data/.autotest +0 -23
  98. data/.gemtest +0 -0
  99. data/Manifest.txt +0 -3
  100. data/README.rdoc +0 -115
  101. data/test/test_libusb_keyboard.rb +0 -50
@@ -0,0 +1,616 @@
1
+ /*
2
+ * Copyright © 2001 Stephen Williams (steve@icarus.com)
3
+ * Copyright © 2001-2002 David Brownell (dbrownell@users.sourceforge.net)
4
+ * Copyright © 2008 Roger Williams (rawqux@users.sourceforge.net)
5
+ * Copyright © 2012 Pete Batard (pete@akeo.ie)
6
+ *
7
+ * This source code is free software; you can redistribute it
8
+ * and/or modify it in source code form under the terms of the GNU
9
+ * General Public License as published by the Free Software
10
+ * Foundation; either version 2 of the License, or (at your option)
11
+ * any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21
+ */
22
+ #include <stdio.h>
23
+ #include <errno.h>
24
+ #include <stdlib.h>
25
+ #include <string.h>
26
+ #include <stdint.h>
27
+
28
+ #include <libusb.h>
29
+ #include "ezusb.h"
30
+
31
+ extern void logerror(const char *format, ...)
32
+ __attribute__ ((format(printf, 1, 2)));
33
+
34
+ /*
35
+ * This file contains functions for uploading firmware into Cypress
36
+ * EZ-USB microcontrollers. These chips use control endpoint 0 and vendor
37
+ * specific commands to support writing into the on-chip SRAM. They also
38
+ * support writing into the CPUCS register, which is how we reset the
39
+ * processor after loading firmware (including the reset vector).
40
+ *
41
+ * These Cypress devices are 8-bit 8051 based microcontrollers with
42
+ * special support for USB I/O. They come in several packages, and
43
+ * some can be set up with external memory when device costs allow.
44
+ * Note that the design was originally by AnchorChips, so you may find
45
+ * references to that vendor (which was later merged into Cypress).
46
+ * The Cypress FX parts are largely compatible with the Anchorhip ones.
47
+ */
48
+
49
+ int verbose;
50
+
51
+ /*
52
+ * return true if [addr,addr+len] includes external RAM
53
+ * for Anchorchips EZ-USB or Cypress EZ-USB FX
54
+ */
55
+ static bool fx_is_external(uint32_t addr, size_t len)
56
+ {
57
+ /* with 8KB RAM, 0x0000-0x1b3f can be written
58
+ * we can't tell if it's a 4KB device here
59
+ */
60
+ if (addr <= 0x1b3f)
61
+ return ((addr + len) > 0x1b40);
62
+
63
+ /* there may be more RAM; unclear if we can write it.
64
+ * some bulk buffers may be unused, 0x1b3f-0x1f3f
65
+ * firmware can set ISODISAB for 2KB at 0x2000-0x27ff
66
+ */
67
+ return true;
68
+ }
69
+
70
+ /*
71
+ * return true if [addr,addr+len] includes external RAM
72
+ * for Cypress EZ-USB FX2
73
+ */
74
+ static bool fx2_is_external(uint32_t addr, size_t len)
75
+ {
76
+ /* 1st 8KB for data/code, 0x0000-0x1fff */
77
+ if (addr <= 0x1fff)
78
+ return ((addr + len) > 0x2000);
79
+
80
+ /* and 512 for data, 0xe000-0xe1ff */
81
+ else if (addr >= 0xe000 && addr <= 0xe1ff)
82
+ return ((addr + len) > 0xe200);
83
+
84
+ /* otherwise, it's certainly external */
85
+ else
86
+ return true;
87
+ }
88
+
89
+ /*
90
+ * return true if [addr,addr+len] includes external RAM
91
+ * for Cypress EZ-USB FX2LP
92
+ */
93
+ static bool fx2lp_is_external(uint32_t addr, size_t len)
94
+ {
95
+ /* 1st 16KB for data/code, 0x0000-0x3fff */
96
+ if (addr <= 0x3fff)
97
+ return ((addr + len) > 0x4000);
98
+
99
+ /* and 512 for data, 0xe000-0xe1ff */
100
+ else if (addr >= 0xe000 && addr <= 0xe1ff)
101
+ return ((addr + len) > 0xe200);
102
+
103
+ /* otherwise, it's certainly external */
104
+ else
105
+ return true;
106
+ }
107
+
108
+
109
+ /*****************************************************************************/
110
+
111
+ /*
112
+ * These are the requests (bRequest) that the bootstrap loader is expected
113
+ * to recognize. The codes are reserved by Cypress, and these values match
114
+ * what EZ-USB hardware, or "Vend_Ax" firmware (2nd stage loader) uses.
115
+ * Cypress' "a3load" is nice because it supports both FX and FX2, although
116
+ * it doesn't have the EEPROM support (subset of "Vend_Ax").
117
+ */
118
+ #define RW_INTERNAL 0xA0 /* hardware implements this one */
119
+ #define RW_MEMORY 0xA3
120
+
121
+ /*
122
+ * Issues the specified vendor-specific write request.
123
+ */
124
+ static int ezusb_write(libusb_device_handle *device, const char *label,
125
+ uint8_t opcode, uint32_t addr, const unsigned char *data, size_t len)
126
+ {
127
+ int status;
128
+
129
+ if (verbose)
130
+ logerror("%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, (unsigned)len, (unsigned)len);
131
+ status = libusb_control_transfer(device,
132
+ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
133
+ opcode, addr & 0xFFFF, addr >> 16,
134
+ (unsigned char*)data, (uint16_t)len, 1000);
135
+ if (status != len) {
136
+ if (status < 0)
137
+ logerror("%s: %s\n", label, libusb_error_name(status));
138
+ else
139
+ logerror("%s ==> %d\n", label, status);
140
+ }
141
+ return (status < 0) ? -EIO : 0;
142
+ }
143
+
144
+ /*
145
+ * Modifies the CPUCS register to stop or reset the CPU.
146
+ * Returns false on error.
147
+ */
148
+ static bool ezusb_cpucs(libusb_device_handle *device, uint32_t addr, bool doRun)
149
+ {
150
+ int status;
151
+ uint8_t data = doRun ? 0x00 : 0x01;
152
+
153
+ if (verbose)
154
+ logerror("%s\n", data ? "stop CPU" : "reset CPU");
155
+ status = libusb_control_transfer(device,
156
+ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
157
+ RW_INTERNAL, addr & 0xFFFF, addr >> 16,
158
+ &data, 1, 1000);
159
+ if ((status != 1) &&
160
+ /* We may get an I/O error from libusbx as the device disappears */
161
+ ((!doRun) || (status != LIBUSB_ERROR_IO)))
162
+ {
163
+ const char *mesg = "can't modify CPUCS";
164
+ if (status < 0)
165
+ logerror("%s: %s\n", mesg, libusb_error_name(status));
166
+ else
167
+ logerror("%s\n", mesg);
168
+ return false;
169
+ } else
170
+ return true;
171
+ }
172
+
173
+ /*****************************************************************************/
174
+
175
+ /*
176
+ * Parse an Intel HEX image file and invoke the poke() function on the
177
+ * various segments to implement policies such as writing to RAM (with
178
+ * a one or two stage loader setup, depending on the firmware) or to
179
+ * EEPROM (two stages required).
180
+ *
181
+ * image - the hex image file
182
+ * context - for use by poke()
183
+ * is_external - if non-null, used to check which segments go into
184
+ * external memory (writable only by software loader)
185
+ * poke - called with each memory segment; errors indicated
186
+ * by returning negative values.
187
+ *
188
+ * Caller is responsible for halting CPU as needed, such as when
189
+ * overwriting a second stage loader.
190
+ */
191
+ static int parse_ihex(FILE *image, void *context,
192
+ bool (*is_external)(uint32_t addr, size_t len),
193
+ int (*poke) (void *context, uint32_t addr, bool external,
194
+ const unsigned char *data, size_t len))
195
+ {
196
+ unsigned char data[1023];
197
+ uint32_t data_addr = 0;
198
+ size_t data_len = 0;
199
+ int rc;
200
+ int first_line = 1;
201
+ bool external = false;
202
+
203
+ /* Read the input file as an IHEX file, and report the memory segments
204
+ * as we go. Each line holds a max of 16 bytes, but uploading is
205
+ * faster (and EEPROM space smaller) if we merge those lines into larger
206
+ * chunks. Most hex files keep memory segments together, which makes
207
+ * such merging all but free. (But it may still be worth sorting the
208
+ * hex files to make up for undesirable behavior from tools.)
209
+ *
210
+ * Note that EEPROM segments max out at 1023 bytes; the upload protocol
211
+ * allows segments of up to 64 KBytes (more than a loader could handle).
212
+ */
213
+ for (;;) {
214
+ char buf[512], *cp;
215
+ char tmp, type;
216
+ size_t len;
217
+ unsigned idx, off;
218
+
219
+ cp = fgets(buf, sizeof(buf), image);
220
+ if (cp == NULL) {
221
+ logerror("EOF without EOF record!\n");
222
+ break;
223
+ }
224
+
225
+ /* EXTENSION: "# comment-till-end-of-line", for copyrights etc */
226
+ if (buf[0] == '#')
227
+ continue;
228
+
229
+ if (buf[0] != ':') {
230
+ logerror("not an ihex record: %s", buf);
231
+ return -2;
232
+ }
233
+
234
+ /* ignore any newline */
235
+ cp = strchr(buf, '\n');
236
+ if (cp)
237
+ *cp = 0;
238
+
239
+ if (verbose >= 3)
240
+ logerror("** LINE: %s\n", buf);
241
+
242
+ /* Read the length field (up to 16 bytes) */
243
+ tmp = buf[3];
244
+ buf[3] = 0;
245
+ len = strtoul(buf+1, NULL, 16);
246
+ buf[3] = tmp;
247
+
248
+ /* Read the target offset (address up to 64KB) */
249
+ tmp = buf[7];
250
+ buf[7] = 0;
251
+ off = strtoul(buf+3, NULL, 16);
252
+ buf[7] = tmp;
253
+
254
+ /* Initialize data_addr */
255
+ if (first_line) {
256
+ data_addr = off;
257
+ first_line = 0;
258
+ }
259
+
260
+ /* Read the record type */
261
+ tmp = buf[9];
262
+ buf[9] = 0;
263
+ type = (char)strtoul(buf+7, NULL, 16);
264
+ buf[9] = tmp;
265
+
266
+ /* If this is an EOF record, then make it so. */
267
+ if (type == 1) {
268
+ if (verbose >= 2)
269
+ logerror("EOF on hexfile\n");
270
+ break;
271
+ }
272
+
273
+ if (type != 0) {
274
+ logerror("unsupported record type: %u\n", type);
275
+ return -3;
276
+ }
277
+
278
+ if ((len * 2) + 11 > strlen(buf)) {
279
+ logerror("record too short?\n");
280
+ return -4;
281
+ }
282
+
283
+ /* FIXME check for _physically_ contiguous not just virtually
284
+ * e.g. on FX2 0x1f00-0x2100 includes both on-chip and external
285
+ * memory so it's not really contiguous */
286
+
287
+ /* flush the saved data if it's not contiguous,
288
+ * or when we've buffered as much as we can.
289
+ */
290
+ if (data_len != 0
291
+ && (off != (data_addr + data_len)
292
+ /* || !merge */
293
+ || (data_len + len) > sizeof(data))) {
294
+ if (is_external)
295
+ external = is_external(data_addr, data_len);
296
+ rc = poke(context, data_addr, external, data, data_len);
297
+ if (rc < 0)
298
+ return -1;
299
+ data_addr = off;
300
+ data_len = 0;
301
+ }
302
+
303
+ /* append to saved data, flush later */
304
+ for (idx = 0, cp = buf+9 ; idx < len ; idx += 1, cp += 2) {
305
+ tmp = cp[2];
306
+ cp[2] = 0;
307
+ data[data_len + idx] = (uint8_t)strtoul(cp, NULL, 16);
308
+ cp[2] = tmp;
309
+ }
310
+ data_len += len;
311
+ }
312
+
313
+
314
+ /* flush any data remaining */
315
+ if (data_len != 0) {
316
+ if (is_external)
317
+ external = is_external(data_addr, data_len);
318
+ rc = poke(context, data_addr, external, data, data_len);
319
+ if (rc < 0)
320
+ return -1;
321
+ }
322
+ return 0;
323
+ }
324
+
325
+ /*
326
+ * Parse a binary image file and write it as is to the target.
327
+ * Applies to Cypress BIX images for RAM or Cypress IIC images
328
+ * for EEPROM.
329
+ *
330
+ * image - the BIX image file
331
+ * context - for use by poke()
332
+ * is_external - if non-null, used to check which segments go into
333
+ * external memory (writable only by software loader)
334
+ * poke - called with each memory segment; errors indicated
335
+ * by returning negative values.
336
+ *
337
+ * Caller is responsible for halting CPU as needed, such as when
338
+ * overwriting a second stage loader.
339
+ */
340
+ static int parse_bin(FILE *image, void *context,
341
+ bool (*is_external)(uint32_t addr, size_t len), int (*poke)(void *context,
342
+ uint32_t addr, bool external, const unsigned char *data, size_t len))
343
+ {
344
+ unsigned char data[4096];
345
+ uint32_t data_addr = 0;
346
+ size_t data_len = 0;
347
+ int rc;
348
+ bool external = false;
349
+
350
+ for (;;) {
351
+ data_len = fread(data, 1, 4096, image);
352
+ if (data_len == 0)
353
+ break;
354
+ if (is_external)
355
+ external = is_external(data_addr, data_len);
356
+ rc = poke(context, data_addr, external, data, data_len);
357
+ if (rc < 0)
358
+ return -1;
359
+ data_addr += (uint32_t)data_len;
360
+ }
361
+ return feof(image)?0:-1;
362
+ }
363
+
364
+ /*
365
+ * Parse a Cypress IIC image file and invoke the poke() function on the
366
+ * various segments for writing to RAM
367
+ *
368
+ * image - the IIC image file
369
+ * context - for use by poke()
370
+ * is_external - if non-null, used to check which segments go into
371
+ * external memory (writable only by software loader)
372
+ * poke - called with each memory segment; errors indicated
373
+ * by returning negative values.
374
+ *
375
+ * Caller is responsible for halting CPU as needed, such as when
376
+ * overwriting a second stage loader.
377
+ */
378
+ static int parse_iic(FILE *image, void *context,
379
+ bool (*is_external)(uint32_t addr, size_t len),
380
+ int (*poke)(void *context, uint32_t addr, bool external, const unsigned char *data, size_t len))
381
+ {
382
+ unsigned char data[4096];
383
+ uint32_t data_addr = 0;
384
+ size_t data_len = 0, read_len;
385
+ uint8_t block_header[4];
386
+ int rc;
387
+ bool external = false;
388
+ long file_size, initial_pos = ftell(image);
389
+
390
+ fseek(image, 0L, SEEK_END);
391
+ file_size = ftell(image);
392
+ fseek(image, initial_pos, SEEK_SET);
393
+ for (;;) {
394
+ /* Ignore the trailing reset IIC data (5 bytes) */
395
+ if (ftell(image) >= (file_size - 5))
396
+ break;
397
+ if (fread(&block_header, 1, sizeof(block_header), image) != 4) {
398
+ logerror("unable to read IIC block header\n");
399
+ return -1;
400
+ }
401
+ data_len = (block_header[0] << 8) + block_header[1];
402
+ data_addr = (block_header[2] << 8) + block_header[3];
403
+ if (data_len > sizeof(data)) {
404
+ /* If this is ever reported as an error, switch to using malloc/realloc */
405
+ logerror("IIC data block too small - please report this error to libusbx.org\n");
406
+ return -1;
407
+ }
408
+ read_len = fread(data, 1, data_len, image);
409
+ if (read_len != data_len) {
410
+ logerror("read error\n");
411
+ return -1;
412
+ }
413
+ if (is_external)
414
+ external = is_external(data_addr, data_len);
415
+ rc = poke(context, data_addr, external, data, data_len);
416
+ if (rc < 0)
417
+ return -1;
418
+ }
419
+ return 0;
420
+ }
421
+
422
+ /* the parse call will be selected according to the image type */
423
+ int (*parse[IMG_TYPE_MAX])(FILE *image, void *context, bool (*is_external)(uint32_t addr, size_t len),
424
+ int (*poke)(void *context, uint32_t addr, bool external, const unsigned char *data, size_t len))
425
+ = { parse_ihex, parse_iic, parse_bin };
426
+
427
+ /*****************************************************************************/
428
+
429
+ /*
430
+ * For writing to RAM using a first (hardware) or second (software)
431
+ * stage loader and 0xA0 or 0xA3 vendor requests
432
+ */
433
+ typedef enum {
434
+ _undef = 0,
435
+ internal_only, /* hardware first-stage loader */
436
+ skip_internal, /* first phase, second-stage loader */
437
+ skip_external /* second phase, second-stage loader */
438
+ } ram_mode;
439
+
440
+ struct ram_poke_context {
441
+ libusb_device_handle *device;
442
+ ram_mode mode;
443
+ size_t total, count;
444
+ };
445
+
446
+ #define RETRY_LIMIT 5
447
+
448
+ static int ram_poke(void *context, uint32_t addr, bool external,
449
+ const unsigned char *data, size_t len)
450
+ {
451
+ struct ram_poke_context *ctx = (struct ram_poke_context*)context;
452
+ int rc;
453
+ unsigned retry = 0;
454
+
455
+ switch (ctx->mode) {
456
+ case internal_only: /* CPU should be stopped */
457
+ if (external) {
458
+ logerror("can't write %u bytes external memory at 0x%08x\n",
459
+ (unsigned)len, addr);
460
+ return -EINVAL;
461
+ }
462
+ break;
463
+ case skip_internal: /* CPU must be running */
464
+ if (!external) {
465
+ if (verbose >= 2) {
466
+ logerror("SKIP on-chip RAM, %u bytes at 0x%08x\n",
467
+ (unsigned)len, addr);
468
+ }
469
+ return 0;
470
+ }
471
+ break;
472
+ case skip_external: /* CPU should be stopped */
473
+ if (external) {
474
+ if (verbose >= 2) {
475
+ logerror("SKIP external RAM, %u bytes at 0x%08x\n",
476
+ (unsigned)len, addr);
477
+ }
478
+ return 0;
479
+ }
480
+ break;
481
+ case _undef:
482
+ default:
483
+ logerror("bug\n");
484
+ return -EDOM;
485
+ }
486
+
487
+ ctx->total += len;
488
+ ctx->count++;
489
+
490
+ /* Retry this till we get a real error. Control messages are not
491
+ * NAKed (just dropped) so time out means is a real problem.
492
+ */
493
+ while ((rc = ezusb_write(ctx->device,
494
+ external ? "write external" : "write on-chip",
495
+ external ? RW_MEMORY : RW_INTERNAL,
496
+ addr, data, len)) < 0
497
+ && retry < RETRY_LIMIT) {
498
+ if (rc != LIBUSB_ERROR_TIMEOUT)
499
+ break;
500
+ retry += 1;
501
+ }
502
+ return rc;
503
+ }
504
+
505
+ /*
506
+ * Load a firmware file into target RAM. device is the open libusbx
507
+ * device, and the path is the name of the source file. Open the file,
508
+ * parse the bytes, and write them in one or two phases.
509
+ *
510
+ * If stage == 0, this uses the first stage loader, built into EZ-USB
511
+ * hardware but limited to writing on-chip memory or CPUCS. Everything
512
+ * is written during one stage, unless there's an error such as the image
513
+ * holding data that needs to be written to external memory.
514
+ *
515
+ * Otherwise, things are written in two stages. First the external
516
+ * memory is written, expecting a second stage loader to have already
517
+ * been loaded. Then file is re-parsed and on-chip memory is written.
518
+ */
519
+ int ezusb_load_ram(libusb_device_handle *device, const char *path, int fx_type, int img_type, int stage)
520
+ {
521
+ FILE *image;
522
+ uint32_t cpucs_addr;
523
+ bool (*is_external)(uint32_t off, size_t len);
524
+ struct ram_poke_context ctx;
525
+ int status;
526
+ uint8_t iic_header[8] = { 0 };
527
+
528
+ image = fopen(path, "rb");
529
+ if (image == NULL) {
530
+ logerror("%s: unable to open for input.\n", path);
531
+ return -2;
532
+ } else if (verbose)
533
+ logerror("open firmware image %s for RAM upload\n", path);
534
+
535
+ if (img_type == IMG_TYPE_IIC) {
536
+ if ( (fread(iic_header, 1, sizeof(iic_header), image) != sizeof(iic_header))
537
+ || (((fx_type == FX_TYPE_FX2LP) || (fx_type == FX_TYPE_FX2)) && (iic_header[0] != 0xC2))
538
+ || ((fx_type == FX_TYPE_AN21) && (iic_header[0] != 0xB2))
539
+ || ((fx_type == FX_TYPE_FX1) && (iic_header[0] != 0xB6)) ) {
540
+ logerror("IIC image does not contain executable code - cannot load to RAM.\n");
541
+ return -1;
542
+ }
543
+ }
544
+
545
+ /* EZ-USB original/FX and FX2 devices differ, apart from the 8051 core */
546
+ switch(fx_type) {
547
+ case FX_TYPE_FX2LP:
548
+ cpucs_addr = 0xe600;
549
+ is_external = fx2lp_is_external;
550
+ break;
551
+ case FX_TYPE_FX2:
552
+ cpucs_addr = 0xe600;
553
+ is_external = fx2_is_external;
554
+ break;
555
+ default:
556
+ cpucs_addr = 0x7f92;
557
+ is_external = fx_is_external;
558
+ break;
559
+ }
560
+
561
+ /* use only first stage loader? */
562
+ if (stage == 0) {
563
+ ctx.mode = internal_only;
564
+
565
+ /* if required, halt the CPU while we overwrite its code/data */
566
+ if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false))
567
+ return -1;
568
+
569
+ /* 2nd stage, first part? loader was already uploaded */
570
+ } else {
571
+ ctx.mode = skip_internal;
572
+
573
+ /* let CPU run; overwrite the 2nd stage loader later */
574
+ if (verbose)
575
+ logerror("2nd stage: write external memory\n");
576
+ }
577
+
578
+ /* scan the image, first (maybe only) time */
579
+ ctx.device = device;
580
+ ctx.total = ctx.count = 0;
581
+ status = parse[img_type](image, &ctx, is_external, ram_poke);
582
+ if (status < 0) {
583
+ logerror("unable to upload %s\n", path);
584
+ return status;
585
+ }
586
+
587
+ /* second part of 2nd stage: rescan */
588
+ // TODO: what should we do for non HEX images there?
589
+ if (stage) {
590
+ ctx.mode = skip_external;
591
+
592
+ /* if needed, halt the CPU while we overwrite the 1st stage loader */
593
+ if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false))
594
+ return -1;
595
+
596
+ /* at least write the interrupt vectors (at 0x0000) for reset! */
597
+ rewind(image);
598
+ if (verbose)
599
+ logerror("2nd stage: write on-chip memory\n");
600
+ status = parse_ihex(image, &ctx, is_external, ram_poke);
601
+ if (status < 0) {
602
+ logerror("unable to completely upload %s\n", path);
603
+ return status;
604
+ }
605
+ }
606
+
607
+ if (verbose)
608
+ logerror("... WROTE: %d bytes, %d segments, avg %d\n",
609
+ (int)ctx.total, (int)ctx.count, (int)(ctx.total/ctx.count));
610
+
611
+ /* if required, reset the CPU so it runs what we just uploaded */
612
+ if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, true))
613
+ return -1;
614
+
615
+ return 0;
616
+ }