summaryrefslogtreecommitdiff
path: root/rbutil/mks5lboot/ipoddfu.c
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2016-02-04 23:05:17 +0100
committerCástor Muñoz <cmvidal@gmail.com>2017-04-14 00:03:42 +0200
commit346423c040fe4ac31dae7c1afcb1d853cc80635c (patch)
treebd8dd4c55f083a1162f7019188977213a20dc41c /rbutil/mks5lboot/ipoddfu.c
parenteefc7c73e2495decdc6f242515696fe0e3f85609 (diff)
downloadrockbox-346423c040fe4ac31dae7c1afcb1d853cc80635c.tar.gz
rockbox-346423c040fe4ac31dae7c1afcb1d853cc80635c.zip
mks5lboot v1.0 - dualboot installer for s5l8702 targetsmks5lboot_1.0
A tool to install/uninstall a bootloader into a s5l8702 based device: - iPod Classic 6G - iPod Nano 3G (TODO) See mks5lboot/README for detailed info. Change-Id: I451d2aaff34509ebd356e4660647e5222c5d3409
Diffstat (limited to 'rbutil/mks5lboot/ipoddfu.c')
-rw-r--r--rbutil/mks5lboot/ipoddfu.c875
1 files changed, 875 insertions, 0 deletions
diff --git a/rbutil/mks5lboot/ipoddfu.c b/rbutil/mks5lboot/ipoddfu.c
new file mode 100644
index 0000000000..6d303d6603
--- /dev/null
+++ b/rbutil/mks5lboot/ipoddfu.c
@@ -0,0 +1,875 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2015 by Cástor Muñoz
11 *
12 * based on:
13 * ipoddfu_c by user890104
14 * xpwn/pwnmetheus2
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
20 *
21 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
22 * KIND, either express or implied.
23 *
24 ****************************************************************************/
25#ifndef NO_LIBUSBAPI
26#define USE_LIBUSBAPI
27#endif
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <string.h>
33#include <time.h>
34#ifdef WIN32
35#include <windows.h>
36#include <setupapi.h>
37#include <stdbool.h>
38#endif
39
40#ifdef USE_LIBUSBAPI
41#include <libusb-1.0/libusb.h>
42#endif
43
44#include "mks5lboot.h"
45
46
47static void sleep_ms(unsigned int ms)
48{
49 struct timespec req;
50 req.tv_sec = ms / 1000;
51 req.tv_nsec = (ms % 1000) * 1000000;
52 nanosleep(&req, NULL);
53}
54
55/*
56 * CRC32 functions
57 * Based on public domain implementation by Finn Yannick Jacobs.
58 */
59
60/* Written and copyright 1999 by Finn Yannick Jacobs
61 * No rights were reserved to this, so feel free to
62 * manipulate or do with it, what you want or desire :)
63 */
64
65#define CRC32_DEFAULT_SEED 0xffffffff
66
67/* crc32table[] built by crc32_init() */
68static unsigned long crc32table[256];
69
70/* Calculate crc32. Little endian.
71 * Standard seed is 0xffffffff or 0.
72 * Some implementations xor result with 0xffffffff after calculation.
73 */
74static uint32_t crc32(void *data, unsigned int len, uint32_t seed)
75{
76 uint8_t *d = data;
77
78 while (len--)
79 {
80 seed = ((seed >> 8) & 0x00FFFFFF) ^ crc32table [(seed ^ *d++) & 0xFF];
81 }
82
83 return seed;
84}
85
86/* Calculate crc32table */
87static void crc32_init()
88{
89 uint32_t poly = 0xEDB88320L;
90 uint32_t crc;
91 int i, j;
92
93 for (i = 0; i < 256; ++i)
94 {
95 crc = i;
96
97 for (j = 8; j > 0; --j)
98 {
99 crc = (crc >> 1) ^ ((crc & 1) ? poly : 0);
100 }
101
102 crc32table[i] = crc;
103 }
104}
105
106
107/*
108 * DFU
109 */
110
111/* must be pow2 <= wTransferSize (2048) */
112#define DFU_PKT_SZ 2048
113
114#define APPLE_VID 0x05AC
115
116static int KNOWN_PIDS[] =
117{
118 /* DFU */
119 0x1220, /* Nano 2G */
120 0x1223, /* Nano 3G and Classic 1G/2G/3G/4G */
121 0x1224, /* Shuffle 3G */
122 0x1225, /* Nano 4G */
123 0x1231, /* Nano 5G */
124 0x1232, /* Nano 6G */
125 0x1233, /* Shuffle 4G */
126 0x1234, /* Nano 7G */
127 /* WTF */
128 0x1240, /* Nano 2G */
129 0x1241, /* Classic 1G */
130 0x1242, /* Nano 3G */
131 0x1243, /* Nano 4G */
132 0x1245, /* Classic 2G */
133 0x1246, /* Nano 5G */
134 0x1247, /* Classic 3G */
135 0x1248, /* Nano 6G */
136 0x1249, /* Nano 7G */
137 0x124a, /* Nano 7G */
138 0x1250, /* Classic 4G */
139 0
140};
141
142struct usbControlSetup {
143 uint8_t bmRequestType;
144 uint8_t bRequest;
145 uint16_t wValue;
146 uint16_t wIndex;
147 uint16_t wLength;
148} __attribute__ ((packed));
149#define USB_CS_SZ (sizeof(struct usbControlSetup))
150
151struct usbStatusData {
152 uint8_t bStatus;
153 uint8_t bwPollTimeout0;
154 uint8_t bwPollTimeout1;
155 uint8_t bwPollTimeout2;
156 uint8_t bState;
157 uint8_t iString;
158} __attribute__ ((packed));
159
160/* DFU 1.1 specs */
161typedef enum DFUState {
162 appIDLE = 0,
163 appDETACH = 1,
164 dfuIDLE = 2,
165 dfuDNLOAD_SYNC = 3,
166 dfuDNBUSY = 4,
167 dfuDNLOAD_IDLE = 5,
168 dfuMANIFEST_SYNC = 6,
169 dfuMANIFEST = 7,
170 dfuMANIFEST_WAIT_RESET = 8,
171 dfuUPLOAD_IDLE = 9,
172 dfuERROR = 10
173} DFUState;
174
175typedef enum DFUStatus {
176 errNONE = 0,
177 errTARGET = 1,
178 errFILE = 2,
179 errWRITE = 3,
180 errERASE = 4,
181 errCHECK_ERASED = 5,
182 errPROG = 6,
183 errVERIFY = 7,
184 errADDRESS = 8,
185 errNOTDONE = 9,
186 errFIRMWARE = 10,
187 errVENDOR = 11,
188 errUSBR = 12,
189 errPOR = 13,
190 errUNKNOWN = 14,
191 errSTALLEDPKT = 15
192} DFUStatus;
193
194typedef enum DFURequest {
195 DFU_DETACH = 0,
196 DFU_DNLOAD = 1,
197 DFU_UPLOAD = 2,
198 DFU_GETSTATUS = 3,
199 DFU_CLRSTATUS = 4,
200 DFU_GETSTATE = 5,
201 DFU_ABORT = 6
202} DFURequest;
203
204struct dfuDev {
205 struct dfuAPI *api;
206 int found_pid;
207 int detached;
208 char descr[256];
209 int res; /* API result: 1->ok, 0->failure */
210 char err[256];
211 /* API private */
212#ifdef WIN32
213 HANDLE fh;
214 HANDLE ph;
215 DWORD ec; /* winapi error code */
216#endif
217#ifdef USE_LIBUSBAPI
218 libusb_context* ctx;
219 libusb_device_handle* devh;
220 int rc; /* libusb return code */
221#endif
222};
223
224struct dfuAPI {
225 char *name;
226 int (*open_fn)(struct dfuDev*, int*);
227 int (*dfureq_fn)(struct dfuDev*, struct usbControlSetup*, void*);
228 int (*reset_fn)(struct dfuDev*);
229 void (*close_fn)(struct dfuDev*);
230};
231
232
233/*
234 * low-level (API specific) functions
235 */
236static int dfu_check_id(int vid, int pid, int *pid_list)
237{
238 int *p;
239 if (vid != APPLE_VID)
240 return 0;
241 for (p = pid_list; *p; p++)
242 if (*p == pid)
243 return 1;
244 return 0;
245}
246
247/* adds extra DFU request error info */
248static void dfu_add_reqerrstr(struct dfuDev *dfuh, struct usbControlSetup *cs)
249{
250 snprintf(dfuh->err + strlen(dfuh->err),
251 sizeof(dfuh->err) - strlen(dfuh->err), " (cs=%02x/%d/%d/%d/%d)",
252 cs->bmRequestType, cs->bRequest, cs->wValue, cs->wIndex, cs->wLength);
253}
254
255#ifdef WIN32
256static int dfu_winapi_chkrc(struct dfuDev *dfuh, char *str, bool success)
257{
258 dfuh->res = (int)success;
259 if (!success) {
260 dfuh->ec = GetLastError();
261 snprintf(dfuh->err, sizeof(dfuh->err), "%s error %ld", str, dfuh->ec);
262 }
263 return dfuh->res;
264}
265
266static int dfu_winapi_request(struct dfuDev *dfuh,
267 struct usbControlSetup* cs, void* data)
268{
269 unsigned char buf[USB_CS_SZ + DFU_PKT_SZ];
270 DWORD rdwr;
271 bool rc;
272
273 memcpy(buf, cs, USB_CS_SZ);
274
275 if (cs->bmRequestType & 0x80)
276 {
277 rc = ReadFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL);
278 memcpy(data, buf+USB_CS_SZ, cs->wLength);
279 dfu_winapi_chkrc(dfuh, "DFU request failed: ReadFile()", rc);
280 }
281 else
282 {
283 memcpy(buf+USB_CS_SZ, data, cs->wLength);
284 rc = WriteFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL);
285 dfu_winapi_chkrc(dfuh, "DFU request failed: WriteFile()", rc);
286 }
287
288 if (!dfuh->res)
289 dfu_add_reqerrstr(dfuh, cs);
290 return dfuh->res;
291}
292
293static int dfu_winapi_reset(struct dfuDev *dfuh)
294{
295 DWORD bytesReturned;
296 bool rc = DeviceIoControl(dfuh->fh, 0x22000c,
297 NULL, 0, NULL, 0, &bytesReturned, NULL);
298 return dfu_winapi_chkrc(dfuh,
299 "Could not reset USB device: DeviceIoControl()", rc);
300}
301
302static void dfu_winapi_close(struct dfuDev *dfuh)
303{
304 if (dfuh->fh != INVALID_HANDLE_VALUE) {
305 CloseHandle(dfuh->fh);
306 dfuh->fh = INVALID_HANDLE_VALUE;
307 }
308 if (dfuh->ph != INVALID_HANDLE_VALUE) {
309 CloseHandle(dfuh->ph);
310 dfuh->ph = INVALID_HANDLE_VALUE;
311 }
312}
313
314static const GUID GUID_AAPLDFU =
315 { 0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
316
317static int dfu_winapi_open(struct dfuDev *dfuh, int *pid_list)
318{
319 const GUID *guid = &GUID_AAPLDFU;
320 HDEVINFO devinfo = NULL;
321 SP_DEVICE_INTERFACE_DETAIL_DATA_A* details = NULL;
322 SP_DEVICE_INTERFACE_DATA iface;
323 char *path = NULL;
324 DWORD i, size;
325 bool rc;
326
327 dfuh->fh =
328 dfuh->ph = INVALID_HANDLE_VALUE;
329 dfuh->found_pid = 0;
330 dfuh->res = 1; /* ok */
331 dfuh->ec = 0;
332
333 /* Get DFU path */
334 devinfo = SetupDiGetClassDevsA(guid, NULL, NULL,
335 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
336 if (!dfu_winapi_chkrc(dfuh, "SetupDiGetClassDevsA()",
337 (devinfo != INVALID_HANDLE_VALUE)))
338 goto error;
339
340 iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
341
342 for (i = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, guid, i, &iface); i++)
343 {
344 int vid, pid;
345
346 SetupDiGetDeviceInterfaceDetailA(devinfo, &iface, NULL, 0, &size, NULL);
347
348 if (details) free(details);
349 details = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(size);
350 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
351 rc = SetupDiGetDeviceInterfaceDetailA(devinfo, &iface, details, size, NULL, NULL);
352 if (!dfu_winapi_chkrc(dfuh, "SetupDiGetDeviceInterfaceDetailA()", rc))
353 goto error;
354
355 CharUpperA(details->DevicePath);
356 if (sscanf(details->DevicePath, "%*4cUSB#VID_%04x&PID_%04x%*s", &vid, &pid) != 2)
357 continue;
358 if (!dfu_check_id(vid, pid, pid_list))
359 continue;
360
361 if (path) free(path);
362 path = malloc(size - sizeof(DWORD) + 16);
363 memcpy(path, details->DevicePath, size - sizeof(DWORD));
364
365 dfuh->fh = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
366 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
367 if (!dfu_winapi_chkrc(dfuh, "CreateFileA(fh)", (dfuh->fh != INVALID_HANDLE_VALUE)))
368 goto error;
369
370 strcat(path, "\\PIPE0");
371 dfuh->ph = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
372 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
373 if (!dfu_winapi_chkrc(dfuh, "CreateFileA(ph)", (dfuh->ph != INVALID_HANDLE_VALUE)))
374 goto error;
375
376 /* ok */
377 snprintf(dfuh->descr, sizeof(dfuh->descr), "%s", details->DevicePath);
378 dfuh->found_pid = pid;
379 goto bye;
380 }
381
382 if (!dfu_winapi_chkrc(dfuh, "SetupDiEnumDeviceInterfaces()",
383 (GetLastError() == ERROR_NO_MORE_ITEMS)))
384 goto error;
385
386 /* no devices found */
387
388bye:
389 if (path) free(path);
390 if (details) free(details);
391 if (devinfo) SetupDiDestroyDeviceInfoList(devinfo);
392 return dfuh->res;
393
394error:
395 dfu_winapi_close(dfuh);
396 goto bye;
397}
398#endif /* WIN32 */
399
400#ifdef USE_LIBUSBAPI
401static int dfu_libusb_chkrc(struct dfuDev *dfuh, char *str)
402{
403 dfuh->res = (dfuh->rc < LIBUSB_SUCCESS) ? 0 : 1;
404 if (dfuh->res == 0)
405 snprintf(dfuh->err, sizeof(dfuh->err),
406 "%s: %s", str, libusb_error_name(dfuh->rc));
407 return dfuh->res;
408}
409
410static int dfu_libusb_request(struct dfuDev *dfuh,
411 struct usbControlSetup *cs, void *data)
412{
413 dfuh->rc = libusb_control_transfer(dfuh->devh, cs->bmRequestType,
414 cs->bRequest, cs->wValue, cs->wIndex, data, cs->wLength, 500);
415 if (!dfu_libusb_chkrc(dfuh, "DFU request failed"))
416 dfu_add_reqerrstr(dfuh, cs);
417 return dfuh->res;
418}
419
420static int dfu_libusb_reset(struct dfuDev *dfuh)
421{
422 dfuh->rc = libusb_reset_device(dfuh->devh);
423 return dfu_libusb_chkrc(dfuh, "Could not reset USB device");
424}
425
426static void dfu_libusb_close(struct dfuDev *dfuh)
427{
428 if (dfuh->devh) {
429 libusb_release_interface(dfuh->devh, 0);
430 if (dfuh->detached)
431 libusb_attach_kernel_driver(dfuh->devh, 0);
432 libusb_close(dfuh->devh);
433 dfuh->devh = NULL;
434 }
435 if (dfuh->ctx) {
436 libusb_exit(dfuh->ctx);
437 dfuh->ctx = NULL;
438 }
439}
440
441static int dfu_libusb_open(struct dfuDev *dfuh, int *pid_list)
442{
443 struct libusb_device_descriptor desc;
444 libusb_device **devs = NULL, *dev;
445 int n_devs, i;
446
447 dfuh->devh = NULL;
448 dfuh->found_pid = 0;
449 dfuh->detached = 0;
450 dfuh->res = 1; /* ok */
451
452 dfuh->rc = libusb_init(&(dfuh->ctx));
453 if (!dfu_libusb_chkrc(dfuh, "Could not init USB library")) {
454 dfuh->ctx = NULL; /* invalidate ctx (if any) */
455 goto error;
456 }
457
458 n_devs =
459 dfuh->rc = libusb_get_device_list(dfuh->ctx, &devs);
460 if (!dfu_libusb_chkrc(dfuh, "Could not get USB device list"))
461 goto error;
462
463 for (i = 0; i < n_devs; ++i)
464 {
465 dev = devs[i];
466
467 /* Note: since libusb-1.0.16 (LIBUSB_API_VERSION >= 0x01000102)
468 this function always succeeds. */
469 if (libusb_get_device_descriptor(dev, &desc) != LIBUSB_SUCCESS)
470 continue; /* Unable to get device descriptor */
471
472 if (!dfu_check_id(desc.idVendor, desc.idProduct, pid_list))
473 continue;
474
475 dfuh->rc = libusb_open(dev, &(dfuh->devh));
476 if (!dfu_libusb_chkrc(dfuh, "Could not open USB device"))
477 goto error;
478
479 dfuh->rc = libusb_set_configuration(dfuh->devh, 1);
480 if (!dfu_libusb_chkrc(dfuh, "Could not set USB configuration"))
481 goto error;
482
483 dfuh->rc = libusb_kernel_driver_active(dfuh->devh, 0);
484 if (dfuh->rc != LIBUSB_ERROR_NOT_SUPPORTED) {
485 if (!dfu_libusb_chkrc(dfuh, "Could not get USB driver status"))
486 goto error;
487 if (dfuh->rc == 1) {
488 dfuh->rc = libusb_detach_kernel_driver(dfuh->devh, 0);
489 if (!dfu_libusb_chkrc(dfuh, "Could not detach USB driver"))
490 goto error;
491 dfuh->detached = 1;
492 }
493 }
494
495 dfuh->rc = libusb_claim_interface(dfuh->devh, 0);
496 if (!dfu_libusb_chkrc(dfuh, "Could not claim USB interface"))
497 goto error;
498
499 /* ok */
500 snprintf(dfuh->descr, sizeof(dfuh->descr),
501 "[%04x:%04x] at bus %d, device %d, USB ver. %04x",
502 desc.idVendor, desc.idProduct, libusb_get_bus_number(dev),
503 libusb_get_device_address(dev), desc.bcdUSB);
504 dfuh->found_pid = desc.idProduct;
505 break;
506 }
507
508bye:
509 if (devs)
510 libusb_free_device_list(devs, 1);
511 if (!dfuh->found_pid)
512 dfu_libusb_close(dfuh);
513 return dfuh->res;
514
515error:
516 goto bye;
517}
518#endif /* USE_LIBUSBAPI */
519
520/* list of suported APIs:
521 * Windows: winapi and libusb (optional)
522 * Linux and OSX: libusb
523 */
524static struct dfuAPI api_list[] =
525{
526#ifdef WIN32
527 { "winapi",
528 dfu_winapi_open,
529 dfu_winapi_request,
530 dfu_winapi_reset,
531 dfu_winapi_close },
532#endif
533#ifdef USE_LIBUSBAPI
534 { "libusb",
535 dfu_libusb_open,
536 dfu_libusb_request,
537 dfu_libusb_reset,
538 dfu_libusb_close },
539#endif
540#ifdef __APPLE__
541 /* TODO: implement API for OS X < 10.6 ??? */
542#endif
543};
544#define DFU_N_APIS (sizeof(api_list)/sizeof(struct dfuAPI))
545
546/*
547 * mid-layer (common) functions
548 */
549static void dfu_set_errstr(struct dfuDev *dfuh, char *str)
550{
551 strncpy(dfuh->err, str, sizeof(dfuh->err));
552}
553
554static int DEBUG_DFUREQ = 0;
555
556static int dfu_request(struct dfuDev *dfuh,
557 struct usbControlSetup *cs, void *data)
558{
559 if (!DEBUG_DFUREQ)
560 return dfuh->api->dfureq_fn(dfuh, cs, data);
561
562 /* DEBUG */
563
564 /* previous state */
565 unsigned char ste = 0;
566 struct usbControlSetup css = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(ste) };
567 if (!dfuh->api->dfureq_fn(dfuh, &css, &ste)) {
568 snprintf(dfuh->err + strlen(dfuh->err), sizeof(dfuh->err) -
569 strlen(dfuh->err), " [DEBUG_DFUREQ ERROR: state=%d]", ste);
570 return 0;
571 }
572
573 int ret = dfuh->api->dfureq_fn(dfuh, cs, data);
574 fprintf(stderr, "[DEBUG]: REQ: ste=%d, cs=%2x/%d/%d/%d/%d -> %s",
575 ste, cs->bmRequestType, cs->bRequest, cs->wValue,
576 cs->wIndex, cs->wLength, ret ? "ok" : "ERROR");
577 if (cs->bRequest == DFU_GETSTATE)
578 fprintf(stderr, " (state=%d)", *((unsigned char*)(data)));
579 if (cs->bRequest == DFU_GETSTATUS) {
580 struct usbStatusData *sd = (struct usbStatusData*)data;
581 fprintf(stderr, " (status=%d, polltmo=%d, state=%d)", sd->bStatus,
582 (sd->bwPollTimeout2 << 16) | (sd->bwPollTimeout1 << 8) |
583 (sd->bwPollTimeout0), sd->bState);
584 }
585 fputc('\n', stderr);
586 fflush(stderr);
587 return ret;
588}
589
590static int dfureq_getstatus(struct dfuDev *dfuh, int *status,
591 int *poll_tmo /*ms*/, int *state)
592{
593 struct usbStatusData sd = { 0, 0, 0, 0, 0, 0 };
594 struct usbControlSetup cs = { 0xA1, DFU_GETSTATUS, 0, 0, sizeof(sd) };
595 int ret = dfu_request(dfuh, &cs, &sd);
596 if (status) *status = sd.bStatus;
597 if (state) *state = sd.bState;
598 if (poll_tmo) *poll_tmo = (sd.bwPollTimeout2 << 16) |
599 (sd.bwPollTimeout1 << 8) | (sd.bwPollTimeout0);
600 return ret;
601}
602
603static int dfureq_getstate(struct dfuDev *dfuh, int *state)
604{
605 if (!state)
606 return 1; /* nothing to do */
607 unsigned char sts = 0;
608 struct usbControlSetup cs = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(sts) };
609 int ret = dfu_request(dfuh, &cs, &sts);
610 *state = sts;
611 return ret;
612}
613
614static int dfureq_dnload(struct dfuDev* dfuh, uint16_t blknum,
615 uint16_t len, unsigned char *data)
616{
617 struct usbControlSetup cs = { 0x21, DFU_DNLOAD, blknum, 0, len };
618 return dfu_request(dfuh, &cs, data);
619}
620
621/* not used */
622#if 0
623static int dfureq_upload(struct dfuDev* dfuh,
624 uint16_t blknum, uint16_t len, unsigned char *data)
625{
626 struct usbControlSetup cs = { 0xA1, DFU_UPLOAD, blknum, 0, len };
627 return dfu_request(dfuh, &cs, data);
628}
629
630static int dfureq_clrstatus(struct dfuDev* dfuh)
631{
632 struct usbControlSetup cs = { 0x21, DFU_CLRSTATUS, 0, 0, 0 };
633 return dfu_request(dfuh, &cs, NULL);
634}
635
636static int dfureq_abort(struct dfuDev* dfuh)
637{
638 struct usbControlSetup cs = { 0x21, DFU_ABORT, 0, 0, 0 };
639 return dfu_request(dfuh, &cs, NULL);
640}
641
642/* not implemented on DFU8702 */
643static int dfureq_detach(struct dfuDev* dfuh, int tmo)
644{
645 struct usbControlSetup cs = { 0x21, DFU_DETACH, tmo, 0, 0 };
646 return dfu_request(dfuh, &cs, NULL);
647}
648#endif
649
650static int dfu_send_packet(struct dfuDev* dfuh, uint16_t blknum,
651 uint16_t len, unsigned char *data, int *status,
652 int *poll_tmo, int *state, int *pre_state)
653{
654 if (!dfureq_dnload(dfuh, blknum, len, data))
655 return 0;
656
657 /* device is in dfuDLSYNC state, waiting for a GETSTATUS request
658 to enter the next state, if she respond with dfuDLBUSY then
659 we must wait to resend the GETSTATUS request */
660
661 if (!dfureq_getstatus(dfuh, status, poll_tmo, state))
662 return 0;
663
664 if (*state == dfuDNBUSY) {
665 if (*poll_tmo)
666 sleep_ms(*poll_tmo);
667 if (!dfureq_getstate(dfuh, pre_state))
668 return 0;
669 if (!dfureq_getstatus(dfuh, status, poll_tmo, state))
670 return 0;
671 }
672
673 return 1;
674}
675
676static int dfu_download_file(struct dfuDev* dfuh,
677 unsigned char *data, unsigned long size)
678{
679 unsigned int blknum, len, remaining;
680 int status, poll_tmo, state;
681
682 if (!dfureq_getstate(dfuh, &state))
683 goto error;
684
685 if (state != dfuIDLE) {
686 dfu_set_errstr(dfuh, "Could not start DFU download: not idle");
687 goto error;
688 }
689
690 blknum = 0;
691 remaining = size;
692 while (remaining)
693 {
694 len = (remaining < DFU_PKT_SZ) ? remaining : DFU_PKT_SZ;
695
696 if (!dfu_send_packet(dfuh, blknum, len, data +
697 blknum*DFU_PKT_SZ, &status, &poll_tmo, &state, NULL))
698 goto error;
699
700 if (state != dfuDNLOAD_IDLE) {
701 dfu_set_errstr(dfuh, "DFU download aborted: unexpected state");
702 goto error;
703 }
704
705 remaining -= len;
706 blknum++;
707 }
708
709 /* send ZLP */
710 int pre_state = 0;
711 if (!dfu_send_packet(dfuh, blknum, 0, NULL,
712 &status, &poll_tmo, &state, &pre_state)) {
713 if (pre_state == dfuMANIFEST_SYNC)
714 goto ok; /* pwnaged .dfu file */
715 goto error;
716 }
717
718 if (state != dfuMANIFEST) {
719 if (status == errFIRMWARE)
720 dfu_set_errstr(dfuh, "DFU download failed: corrupt firmware");
721 else
722 dfu_set_errstr(dfuh, "DFU download failed: unexpected state");
723 goto error;
724 }
725
726 /* wait for manifest stage */
727 if (poll_tmo)
728 sleep_ms(poll_tmo);
729
730 if (!dfureq_getstatus(dfuh, &status, NULL, &state))
731 goto ok; /* 1223 .dfu file */
732
733
734 /* TODO: next code never tested */
735
736 if (state != dfuMANIFEST_WAIT_RESET) {
737 if (status == errVERIFY)
738 dfu_set_errstr(dfuh, "DFU manifest failed: wrong FW verification");
739 else
740 dfu_set_errstr(dfuh, "DFU manifest failed: unexpected state");
741 goto error;
742 }
743
744 if (!dfuh->api->reset_fn(dfuh))
745 goto error;
746
747ok:
748 return 1;
749error:
750 return 0;
751}
752
753static int dfu_open(struct dfuDev *dfuh, int pid)
754{
755 int pid_l[2] = {0};
756 struct dfuAPI *api;
757 unsigned i;
758
759 pid_l[0] = pid;
760
761 for (i = 0; i < DFU_N_APIS; i++)
762 {
763 api = &api_list[i];
764 if (!(api->open_fn(dfuh, pid ? pid_l : KNOWN_PIDS)))
765 return 0; /* error */
766 if (dfuh->found_pid) {
767 dfuh->api = api;
768 printf("[INFO] %s: found %s\n", api->name, dfuh->descr);
769 fflush(stdout);
770 return 1; /* ok */
771 }
772 printf("[INFO] %s: no DFU devices found\n", api->name);
773 fflush(stdout);
774 }
775
776 dfu_set_errstr(dfuh, "DFU device not found");
777 return 0;
778}
779
780static void dfu_destroy(struct dfuDev *dfuh)
781{
782 if (dfuh) {
783 if (dfuh->api)
784 dfuh->api->close_fn(dfuh);
785 free(dfuh);
786 }
787}
788
789static struct dfuDev *dfu_create()
790{
791 return calloc(sizeof(struct dfuDev), 1);
792}
793
794/*
795 * exported functions
796 */
797int ipoddfu_send(int pid, unsigned char *data, int size,
798 char* errstr, int errstrsize)
799{
800 struct dfuDev *dfuh;
801 unsigned char *buf;
802 unsigned int checksum;
803 int ret = 1; /* ok */
804
805 dfuh = dfu_create();
806
807 buf = malloc(size+4);
808 if (!buf) {
809 dfu_set_errstr(dfuh, "Could not allocate memory for DFU buffer");
810 goto error;
811 }
812
813 if (memcmp(data, IM3_IDENT, 4)) {
814 dfu_set_errstr(dfuh, "Bad DFU image data");
815 goto error;
816 }
817
818 /* FIXME: big endian */
819 crc32_init();
820 checksum = crc32(data, size, CRC32_DEFAULT_SEED);
821 memcpy(buf, data, size);
822 memcpy(buf+size, &checksum, 4);
823
824 if (!dfu_open(dfuh, pid))
825 goto error;
826
827 if (!dfu_download_file(dfuh, buf, size+4))
828 goto error;
829
830bye:
831 if (buf) free(buf);
832 dfu_destroy(dfuh);
833 return ret;
834
835error:
836 ret = 0;
837 if (errstr)
838 snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err);
839 goto bye;
840}
841
842/* search for the DFU device and gets its DFUState */
843int ipoddfu_scan(int pid, int *state, int reset,
844 char* errstr, int errstrsize)
845{
846 struct dfuDev *dfuh;
847 int ret = 1; /* ok */
848
849 dfuh = dfu_create();
850
851 if (!dfu_open(dfuh, pid))
852 goto error;
853
854 if (reset)
855 if (!dfuh->api->reset_fn(dfuh))
856 goto error;
857
858 if (!dfureq_getstate(dfuh, state))
859 goto error;
860
861bye:
862 dfu_destroy(dfuh);
863 return ret;
864
865error:
866 ret = 0;
867 if (errstr)
868 snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err);
869 goto bye;
870}
871
872void ipoddfu_debug(int debug)
873{
874 DEBUG_DFUREQ = debug;
875}