summaryrefslogtreecommitdiff
path: root/utils/mks5lboot/ipoddfu.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mks5lboot/ipoddfu.c')
-rw-r--r--utils/mks5lboot/ipoddfu.c1061
1 files changed, 1061 insertions, 0 deletions
diff --git a/utils/mks5lboot/ipoddfu.c b/utils/mks5lboot/ipoddfu.c
new file mode 100644
index 0000000000..5e2914af4b
--- /dev/null
+++ b/utils/mks5lboot/ipoddfu.c
@@ -0,0 +1,1061 @@
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
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <stdbool.h>
30#include <string.h>
31#ifdef WIN32
32#include <windows.h>
33#include <setupapi.h>
34#endif
35#ifdef USE_LIBUSBAPI
36#include <libusb-1.0/libusb.h>
37#endif
38#ifdef __APPLE__
39#include <CoreFoundation/CoreFoundation.h>
40#include <IOKit/IOCFPlugIn.h>
41#include <IOKit/usb/IOUSBLib.h>
42#endif
43
44#include "mks5lboot.h"
45
46
47#ifdef WIN32
48#define sleep_ms(ms) Sleep(ms)
49#else
50#include <time.h>
51static void sleep_ms(unsigned int ms)
52{
53 struct timespec req;
54 req.tv_sec = ms / 1000;
55 req.tv_nsec = (ms % 1000) * 1000000;
56 nanosleep(&req, NULL);
57}
58#endif
59
60static void put_uint32le(unsigned char* p, uint32_t x)
61{
62 p[0] = x & 0xff;
63 p[1] = (x >> 8) & 0xff;
64 p[2] = (x >> 16) & 0xff;
65 p[3] = (x >> 24) & 0xff;
66}
67
68/*
69 * CRC32 functions
70 * Based on public domain implementation by Finn Yannick Jacobs.
71 *
72 * Written and copyright 1999 by Finn Yannick Jacobs
73 * No rights were reserved to this, so feel free to
74 * manipulate or do with it, what you want or desire :)
75 */
76
77/* crc32table[] built by crc32_init() */
78static uint32_t crc32table[256];
79
80/* Calculate crc32 */
81static uint32_t crc32(void *data, unsigned int len, uint32_t previousCrc32)
82{
83 uint32_t crc = ~previousCrc32;
84 unsigned char *d = (unsigned char*) data;
85 while (len--)
86 crc = (crc >> 8) ^ crc32table[(crc & 0xFF) ^ *d++];
87 return ~crc;
88}
89
90/* Calculate crc32table */
91static void crc32_init()
92{
93 uint32_t poly = 0xEDB88320L;
94 uint32_t crc;
95 int i, j;
96 for (i = 0; i < 256; ++i)
97 {
98 crc = i;
99 for (j = 0; j < 8; ++j)
100 crc = (crc >> 1) ^ ((crc & 1) ? poly : 0);
101 crc32table[i] = crc;
102 }
103}
104
105/* USB */
106#define APPLE_VID 0x05AC
107
108struct pid_info {
109 int pid;
110 int mode; /* 0->DFU, 1->WTF */
111 char *desc;
112};
113
114struct pid_info known_pids[] =
115{
116 /* DFU */
117 { 0x1220, 0, "Nano 2G" },
118 { 0x1223, 0, "Nano 3G / Classic" },
119 { 0x1224, 0, "Shuffle 3G" },
120 { 0x1225, 0, "Nano 4G" },
121 { 0x1231, 0, "Nano 5G" },
122 { 0x1232, 0, "Nano 6G" },
123 { 0x1233, 0, "Shuffle 4G" },
124 { 0x1234, 0, "Nano 7G" },
125 /* WTF */
126 { 0x1240, 1, "Nano 2G" },
127 { 0x1241, 1, "Classic 1G" },
128 { 0x1242, 1, "Nano 3G" },
129 { 0x1243, 1, "Nano 4G" },
130 { 0x1245, 1, "Classic 2G" },
131 { 0x1246, 1, "Nano 5G" },
132 { 0x1247, 1, "Classic 3G" },
133 { 0x1248, 1, "Nano 6G" },
134 { 0x1249, 1, "Nano 7G" },
135 { 0x124a, 1, "Nano 7G" },
136 { 0x1250, 1, "Classic 4G" },
137};
138#define N_KNOWN_PIDS (sizeof(known_pids)/sizeof(struct pid_info))
139
140struct usbControlSetup {
141 uint8_t bmRequestType;
142 uint8_t bRequest;
143 uint16_t wValue;
144 uint16_t wIndex;
145 uint16_t wLength;
146} __attribute__ ((packed));
147#define USB_CS_SZ (sizeof(struct usbControlSetup))
148
149struct usbStatusData {
150 uint8_t bStatus;
151 uint8_t bwPollTimeout0;
152 uint8_t bwPollTimeout1;
153 uint8_t bwPollTimeout2;
154 uint8_t bState;
155 uint8_t iString;
156} __attribute__ ((packed));
157
158
159/*
160 * DFU API
161 */
162#define DFU_PKT_SZ 2048 /* must be pow2 <= wTransferSize (2048) */
163
164/* DFU 1.1 specs */
165typedef enum {
166 appIDLE = 0,
167 appDETACH = 1,
168 dfuIDLE = 2,
169 dfuDNLOAD_SYNC = 3,
170 dfuDNBUSY = 4,
171 dfuDNLOAD_IDLE = 5,
172 dfuMANIFEST_SYNC = 6,
173 dfuMANIFEST = 7,
174 dfuMANIFEST_WAIT_RESET = 8,
175 dfuUPLOAD_IDLE = 9,
176 dfuERROR = 10
177} DFUState;
178
179typedef enum {
180 errNONE = 0,
181 errTARGET = 1,
182 errFILE = 2,
183 errWRITE = 3,
184 errERASE = 4,
185 errCHECK_ERASED = 5,
186 errPROG = 6,
187 errVERIFY = 7,
188 errADDRESS = 8,
189 errNOTDONE = 9,
190 errFIRMWARE = 10,
191 errVENDOR = 11,
192 errUSBR = 12,
193 errPOR = 13,
194 errUNKNOWN = 14,
195 errSTALLEDPKT = 15
196} DFUStatus;
197
198typedef enum {
199 DFU_DETACH = 0,
200 DFU_DNLOAD = 1,
201 DFU_UPLOAD = 2,
202 DFU_GETSTATUS = 3,
203 DFU_CLRSTATUS = 4,
204 DFU_GETSTATE = 5,
205 DFU_ABORT = 6
206} DFURequest;
207
208typedef enum {
209 DFUAPIFail = 0,
210 DFUAPISuccess,
211} dfuAPIResult;
212
213struct dfuDev {
214 struct dfuAPI *api;
215 int found_pid;
216 int detached;
217 char descr[256];
218 dfuAPIResult res;
219 char err[256];
220 /* API private */
221#ifdef WIN32
222 HANDLE fh;
223 HANDLE ph;
224 DWORD ec; /* winapi error code */
225#endif
226#ifdef USE_LIBUSBAPI
227 libusb_context* ctx;
228 libusb_device_handle* devh;
229 int rc; /* libusb return code */
230#endif
231#ifdef __APPLE__
232 IOUSBDeviceInterface** dev;
233 kern_return_t kr;
234#endif
235};
236
237struct dfuAPI {
238 char *name;
239 dfuAPIResult (*open_fn)(struct dfuDev*, int*);
240 dfuAPIResult (*dfureq_fn)(struct dfuDev*, struct usbControlSetup*, void*);
241 dfuAPIResult (*reset_fn)(struct dfuDev*);
242 void (*close_fn)(struct dfuDev*);
243};
244
245
246/*
247 * DFU API low-level (specific) functions
248 */
249static bool dfu_check_id(int vid, int pid, int *pid_list)
250{
251 int *p;
252 if (vid != APPLE_VID)
253 return 0;
254 for (p = pid_list; *p; p++)
255 if (*p == pid)
256 return 1;
257 return 0;
258}
259
260/* adds extra DFU request error info */
261static void dfu_add_reqerrstr(struct dfuDev *dfuh, struct usbControlSetup *cs)
262{
263 snprintf(dfuh->err + strlen(dfuh->err),
264 sizeof(dfuh->err) - strlen(dfuh->err), " (cs=%02x/%d/%d/%d/%d)",
265 cs->bmRequestType, cs->bRequest, cs->wValue, cs->wIndex, cs->wLength);
266}
267
268#ifdef WIN32
269static bool dfu_winapi_chkrc(struct dfuDev *dfuh, char *str, bool success)
270{
271 dfuh->res = (success) ? DFUAPISuccess : DFUAPIFail;
272 if (!success) {
273 dfuh->ec = GetLastError();
274 snprintf(dfuh->err, sizeof(dfuh->err), "%s error %ld", str, dfuh->ec);
275 }
276 return success;
277}
278
279static dfuAPIResult dfu_winapi_request(struct dfuDev *dfuh,
280 struct usbControlSetup* cs, void* data)
281{
282 unsigned char buf[USB_CS_SZ + DFU_PKT_SZ];
283 DWORD rdwr;
284 bool rc;
285
286 memcpy(buf, cs, USB_CS_SZ);
287
288 if (cs->bmRequestType & 0x80)
289 {
290 rc = ReadFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL);
291 memcpy(data, buf+USB_CS_SZ, cs->wLength);
292 dfu_winapi_chkrc(dfuh, "DFU request failed: ReadFile()", rc);
293 }
294 else
295 {
296 memcpy(buf+USB_CS_SZ, data, cs->wLength);
297 rc = WriteFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL);
298 dfu_winapi_chkrc(dfuh, "DFU request failed: WriteFile()", rc);
299 }
300 if (!rc)
301 dfu_add_reqerrstr(dfuh, cs);
302
303 return dfuh->res;
304}
305
306static dfuAPIResult dfu_winapi_reset(struct dfuDev *dfuh)
307{
308 DWORD bytesReturned;
309 bool rc = DeviceIoControl(dfuh->fh,
310 0x22000c, NULL, 0, NULL, 0, &bytesReturned, NULL);
311 dfu_winapi_chkrc(dfuh,
312 "Could not reset USB device: DeviceIoControl()", rc);
313 return dfuh->res;
314}
315
316static void dfu_winapi_close(struct dfuDev *dfuh)
317{
318 if (dfuh->fh != INVALID_HANDLE_VALUE) {
319 CloseHandle(dfuh->fh);
320 dfuh->fh = INVALID_HANDLE_VALUE;
321 }
322 if (dfuh->ph != INVALID_HANDLE_VALUE) {
323 CloseHandle(dfuh->ph);
324 dfuh->ph = INVALID_HANDLE_VALUE;
325 }
326}
327
328static const GUID GUID_AAPLDFU =
329 { 0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
330
331static dfuAPIResult dfu_winapi_open(struct dfuDev *dfuh, int *pid_list)
332{
333 const GUID *guid = &GUID_AAPLDFU;
334 HDEVINFO devinfo = NULL;
335 SP_DEVICE_INTERFACE_DETAIL_DATA_A* details = NULL;
336 SP_DEVICE_INTERFACE_DATA iface;
337 char *path = NULL;
338 DWORD i, size;
339 bool rc;
340
341 dfuh->fh =
342 dfuh->ph = INVALID_HANDLE_VALUE;
343 dfuh->found_pid = 0;
344 dfuh->res = DFUAPISuccess;
345 dfuh->ec = 0;
346
347 /* Get DFU path */
348 devinfo = SetupDiGetClassDevsA(guid, NULL, NULL,
349 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
350 if (!dfu_winapi_chkrc(dfuh, "SetupDiGetClassDevsA()",
351 (devinfo != INVALID_HANDLE_VALUE)))
352 goto error;
353
354 iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
355
356 for (i = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, guid, i, &iface); i++)
357 {
358 int vid, pid;
359
360 SetupDiGetDeviceInterfaceDetailA(devinfo, &iface, NULL, 0, &size, NULL);
361
362 if (details) free(details);
363 details = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(size);
364 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
365 rc = SetupDiGetDeviceInterfaceDetailA(devinfo, &iface, details, size, NULL, NULL);
366 if (!dfu_winapi_chkrc(dfuh, "SetupDiGetDeviceInterfaceDetailA()", rc))
367 goto error;
368
369 CharUpperA(details->DevicePath);
370 if (sscanf(details->DevicePath, "%*4cUSB#VID_%04x&PID_%04x%*s", &vid, &pid) != 2)
371 continue;
372 if (!dfu_check_id(vid, pid, pid_list))
373 continue;
374
375 if (path) free(path);
376 path = malloc(size - sizeof(DWORD) + 16);
377 memcpy(path, details->DevicePath, size - sizeof(DWORD));
378
379 dfuh->fh = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
380 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
381 if (!dfu_winapi_chkrc(dfuh, "CreateFileA(fh)", (dfuh->fh != INVALID_HANDLE_VALUE)))
382 goto error;
383
384 strcat(path, "\\PIPE0");
385 dfuh->ph = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
386 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
387 if (!dfu_winapi_chkrc(dfuh, "CreateFileA(ph)", (dfuh->ph != INVALID_HANDLE_VALUE)))
388 goto error;
389
390 /* ok */
391 snprintf(dfuh->descr, sizeof(dfuh->descr), "%s", details->DevicePath);
392 dfuh->found_pid = pid;
393 goto bye;
394 }
395
396 if (!dfu_winapi_chkrc(dfuh, "SetupDiEnumDeviceInterfaces()",
397 (GetLastError() == ERROR_NO_MORE_ITEMS)))
398 goto error;
399
400 /* no devices found */
401
402bye:
403 if (path) free(path);
404 if (details) free(details);
405 if (devinfo) SetupDiDestroyDeviceInfoList(devinfo);
406 return dfuh->res;
407
408error:
409 dfu_winapi_close(dfuh);
410 goto bye;
411}
412#endif /* WIN32 */
413
414#ifdef USE_LIBUSBAPI
415static bool dfu_libusb_chkrc(struct dfuDev *dfuh, char *str)
416{
417 dfuh->res = (dfuh->rc < LIBUSB_SUCCESS) ? DFUAPIFail : DFUAPISuccess;
418 if (dfuh->res == DFUAPIFail)
419 snprintf(dfuh->err, sizeof(dfuh->err),
420 "%s: %s", str, libusb_error_name(dfuh->rc));
421 return (dfuh->res == DFUAPISuccess);
422}
423
424static dfuAPIResult dfu_libusb_request(struct dfuDev *dfuh,
425 struct usbControlSetup *cs, void *data)
426{
427 dfuh->rc = libusb_control_transfer(dfuh->devh, cs->bmRequestType,
428 cs->bRequest, cs->wValue, cs->wIndex, data, cs->wLength, 500);
429 if (!dfu_libusb_chkrc(dfuh, "DFU request failed"))
430 dfu_add_reqerrstr(dfuh, cs);
431 return dfuh->res;
432}
433
434static dfuAPIResult dfu_libusb_reset(struct dfuDev *dfuh)
435{
436 dfuh->rc = libusb_reset_device(dfuh->devh);
437 dfu_libusb_chkrc(dfuh, "Could not reset USB device");
438 return dfuh->res;
439}
440
441static void dfu_libusb_close(struct dfuDev *dfuh)
442{
443 if (dfuh->devh) {
444 libusb_release_interface(dfuh->devh, 0);
445 if (dfuh->detached)
446 libusb_attach_kernel_driver(dfuh->devh, 0);
447 libusb_close(dfuh->devh);
448 dfuh->devh = NULL;
449 }
450 if (dfuh->ctx) {
451 libusb_exit(dfuh->ctx);
452 dfuh->ctx = NULL;
453 }
454}
455
456static dfuAPIResult dfu_libusb_open(struct dfuDev *dfuh, int *pid_list)
457{
458 struct libusb_device_descriptor desc;
459 libusb_device **devs = NULL, *dev;
460 int n_devs, i;
461
462 dfuh->devh = NULL;
463 dfuh->found_pid = 0;
464 dfuh->detached = 0;
465 dfuh->res = DFUAPISuccess;
466
467 dfuh->rc = libusb_init(&(dfuh->ctx));
468 if (!dfu_libusb_chkrc(dfuh, "Could not init USB library")) {
469 dfuh->ctx = NULL; /* invalidate ctx (if any) */
470 goto error;
471 }
472
473 n_devs =
474 dfuh->rc = libusb_get_device_list(dfuh->ctx, &devs);
475 if (!dfu_libusb_chkrc(dfuh, "Could not get USB device list"))
476 goto error;
477
478 for (i = 0; i < n_devs; ++i)
479 {
480 dev = devs[i];
481
482 /* Note: since libusb-1.0.16 (LIBUSB_API_VERSION >= 0x01000102)
483 this function always succeeds. */
484 if (libusb_get_device_descriptor(dev, &desc) != LIBUSB_SUCCESS)
485 continue; /* Unable to get device descriptor */
486
487 if (!dfu_check_id(desc.idVendor, desc.idProduct, pid_list))
488 continue;
489
490 dfuh->rc = libusb_open(dev, &(dfuh->devh));
491 if (!dfu_libusb_chkrc(dfuh, "Could not open USB device"))
492 goto error;
493
494 dfuh->rc = libusb_set_configuration(dfuh->devh, 1);
495 if (!dfu_libusb_chkrc(dfuh, "Could not set USB configuration"))
496 goto error;
497
498 dfuh->rc = libusb_kernel_driver_active(dfuh->devh, 0);
499 if (dfuh->rc != LIBUSB_ERROR_NOT_SUPPORTED) {
500 if (!dfu_libusb_chkrc(dfuh, "Could not get USB driver status"))
501 goto error;
502 if (dfuh->rc == 1) {
503 dfuh->rc = libusb_detach_kernel_driver(dfuh->devh, 0);
504 if (!dfu_libusb_chkrc(dfuh, "Could not detach USB driver"))
505 goto error;
506 dfuh->detached = 1;
507 }
508 }
509
510 dfuh->rc = libusb_claim_interface(dfuh->devh, 0);
511 if (!dfu_libusb_chkrc(dfuh, "Could not claim USB interface"))
512 goto error;
513
514 /* ok */
515 snprintf(dfuh->descr, sizeof(dfuh->descr),
516 "[%04x:%04x] at bus %d, device %d, USB ver. %04x",
517 desc.idVendor, desc.idProduct, libusb_get_bus_number(dev),
518 libusb_get_device_address(dev), desc.bcdUSB);
519 dfuh->found_pid = desc.idProduct;
520 break;
521 }
522
523bye:
524 if (devs)
525 libusb_free_device_list(devs, 1);
526 if (!dfuh->found_pid)
527 dfu_libusb_close(dfuh);
528 return dfuh->res;
529
530error:
531 goto bye;
532}
533#endif /* USE_LIBUSBAPI */
534
535#ifdef __APPLE__
536static bool dfu_iokit_chkrc(struct dfuDev *dfuh, char *str)
537{
538 dfuh->res = (dfuh->kr == kIOReturnSuccess) ? DFUAPISuccess : DFUAPIFail;
539 if (dfuh->res == DFUAPIFail)
540 snprintf(dfuh->err, sizeof(dfuh->err),
541 "%s: error %08x", str, dfuh->kr);
542 return (dfuh->res == DFUAPISuccess);
543}
544
545static dfuAPIResult dfu_iokit_request(struct dfuDev *dfuh,
546 struct usbControlSetup *cs, void *data)
547{
548 IOUSBDevRequest req;
549 req.bmRequestType = cs->bmRequestType;
550 req.bRequest = cs->bRequest;
551 req.wValue = cs->wValue;
552 req.wIndex = cs->wIndex;
553 req.wLength = cs->wLength;
554 req.pData = data;
555
556 dfuh->kr = (*(dfuh->dev))->DeviceRequest(dfuh->dev, &req);
557 if (!dfu_iokit_chkrc(dfuh, "DFU request failed"))
558 dfu_add_reqerrstr(dfuh, cs);
559
560 return dfuh->res;
561}
562
563static dfuAPIResult dfu_iokit_reset(struct dfuDev *dfuh)
564{
565 dfuh->kr = (*(dfuh->dev))->ResetDevice(dfuh->dev);
566#if 0
567 /* On 10.11+ ResetDevice() returns no error but does not perform
568 * any reset, just a kernel log message.
569 * USBDeviceReEnumerate() could be used as a workaround.
570 */
571 dfuh->kr = (*(dfuh->dev))->USBDeviceReEnumerate(dfuh->dev, 0);
572#endif
573 dfu_iokit_chkrc(dfuh, "Could not reset USB device");
574 return dfuh->res;
575}
576
577static void dfu_iokit_close(struct dfuDev *dfuh)
578{
579 if (dfuh->dev) {
580 (*(dfuh->dev))->USBDeviceClose(dfuh->dev);
581 (*(dfuh->dev))->Release(dfuh->dev);
582 dfuh->dev = NULL;
583 }
584}
585
586static dfuAPIResult dfu_iokit_open(struct dfuDev *dfuh, int *pid_list)
587{
588 kern_return_t kr;
589 CFMutableDictionaryRef usb_matching_dict = 0;
590 io_object_t usbDevice;
591 io_iterator_t usb_iterator = IO_OBJECT_NULL;
592 IOCFPlugInInterface **plugInInterface = NULL;
593 IOUSBDeviceInterface **dev = NULL;
594 HRESULT result;
595 SInt32 score;
596 UInt16 vendor;
597 UInt16 product;
598 UInt16 release;
599
600 dfuh->dev = NULL;
601 dfuh->found_pid = 0;
602 dfuh->res = DFUAPISuccess;
603
604 usb_matching_dict = IOServiceMatching(kIOUSBDeviceClassName);
605 dfuh->kr = IOServiceGetMatchingServices(
606 kIOMasterPortDefault, usb_matching_dict, &usb_iterator);
607 if (!dfu_iokit_chkrc(dfuh, "Could not get matching services"))
608 goto error;
609
610 while ((usbDevice = IOIteratorNext(usb_iterator)))
611 {
612 /* Create an intermediate plug-in */
613 kr = IOCreatePlugInInterfaceForService(usbDevice,
614 kIOUSBDeviceUserClientTypeID,
615 kIOCFPlugInInterfaceID,
616 &plugInInterface,
617 &score);
618 IOObjectRelease(usbDevice);
619
620 if ((kIOReturnSuccess != kr) || !plugInInterface)
621 continue; /* Unable to create a plugin */
622
623 /* Now create the device interface */
624 result = (*plugInInterface)->QueryInterface(plugInInterface,
625 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
626 (LPVOID*)&dev);
627 (*plugInInterface)->Release(plugInInterface);
628
629 if (result || !dev)
630 continue; /* Couldn't create a device interface */
631
632 kr = (*dev)->GetDeviceVendor(dev, &vendor);
633 kr = (*dev)->GetDeviceProduct(dev, &product);
634 kr = (*dev)->GetDeviceReleaseNumber(dev, &release);
635
636 if (!dfu_check_id(vendor, product, pid_list)) {
637 (*dev)->Release(dev);
638 continue;
639 }
640
641 /* Device found, open it */
642 dfuh->kr = (*dev)->USBDeviceOpen(dev);
643 if (!dfu_iokit_chkrc(dfuh, "Could not open USB device")) {
644 (*dev)->Release(dev);
645 goto error;
646 }
647
648 /* ok */
649 dfuh->found_pid = product;
650 dfuh->dev = dev;
651 snprintf(dfuh->descr, sizeof(dfuh->descr),
652 "[%04x:%04x] release: %d", vendor, product, release);
653 break;
654 }
655
656bye:
657 if (usb_iterator != IO_OBJECT_NULL)
658 IOObjectRelease(usb_iterator);
659 return dfuh->res;
660
661error:
662 goto bye;
663}
664#endif /* __APPLE__ */
665
666/* list of suported APIs */
667static struct dfuAPI api_list[] =
668{
669#ifdef WIN32
670 { "winapi",
671 dfu_winapi_open,
672 dfu_winapi_request,
673 dfu_winapi_reset,
674 dfu_winapi_close },
675#endif
676#ifdef USE_LIBUSBAPI
677 { "libusb",
678 dfu_libusb_open,
679 dfu_libusb_request,
680 dfu_libusb_reset,
681 dfu_libusb_close },
682#endif
683#ifdef __APPLE__
684 { "IOKit",
685 dfu_iokit_open,
686 dfu_iokit_request,
687 dfu_iokit_reset,
688 dfu_iokit_close },
689#endif
690};
691#define N_DFU_APIS (sizeof(api_list)/sizeof(struct dfuAPI))
692
693
694/*
695 * DFU API common functions
696 */
697static int DEBUG_DFUREQ = 0;
698
699static dfuAPIResult dfuapi_request(struct dfuDev *dfuh,
700 struct usbControlSetup *cs, void *data)
701{
702 if (!DEBUG_DFUREQ)
703 return dfuh->api->dfureq_fn(dfuh, cs, data);
704
705 /* DEBUG */
706
707 /* previous state */
708 unsigned char ste = 0;
709 struct usbControlSetup css = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(ste) };
710 if (dfuh->api->dfureq_fn(dfuh, &css, &ste) != DFUAPISuccess) {
711 snprintf(dfuh->err + strlen(dfuh->err), sizeof(dfuh->err) -
712 strlen(dfuh->err), " [DEBUG_DFUREQ ERROR: state=%d]", ste);
713 goto error;
714 }
715
716 dfuh->api->dfureq_fn(dfuh, cs, data);
717 fprintf(stderr, "[DEBUG]: REQ: ste=%d, cs=%2x/%d/%d/%d/%d -> %s",
718 ste, cs->bmRequestType, cs->bRequest, cs->wValue,
719 cs->wIndex, cs->wLength,
720 (dfuh->res == DFUAPISuccess) ? "ok" : "ERROR");
721 if (cs->bRequest == DFU_GETSTATE)
722 fprintf(stderr, " (state=%d)", *((unsigned char*)(data)));
723 if (cs->bRequest == DFU_GETSTATUS) {
724 struct usbStatusData *sd = (struct usbStatusData*)data;
725 fprintf(stderr, " (status=%d, polltmo=%d, state=%d)", sd->bStatus,
726 (sd->bwPollTimeout2 << 16) | (sd->bwPollTimeout1 << 8) |
727 (sd->bwPollTimeout0), sd->bState);
728 }
729 fputc('\n', stderr);
730 fflush(stderr);
731
732bye:
733 return dfuh->res;
734error:
735 goto bye;
736}
737
738static dfuAPIResult dfuapi_req_getstatus(struct dfuDev *dfuh,
739 DFUStatus *status, int *poll_tmo /*ms*/,
740 DFUState *state)
741{
742 struct usbStatusData sd = { 0, 0, 0, 0, 0, 0 };
743 struct usbControlSetup cs = { 0xA1, DFU_GETSTATUS, 0, 0, sizeof(sd) };
744 dfuapi_request(dfuh, &cs, &sd);
745 if (status) *status = sd.bStatus;
746 if (state) *state = sd.bState;
747 if (poll_tmo) *poll_tmo = (sd.bwPollTimeout2 << 16) |
748 (sd.bwPollTimeout1 << 8) | (sd.bwPollTimeout0);
749 return dfuh->res;
750}
751
752static dfuAPIResult dfuapi_req_getstate(struct dfuDev *dfuh, DFUState *state)
753{
754 unsigned char sts = 0;
755 struct usbControlSetup cs = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(sts) };
756 dfuapi_request(dfuh, &cs, &sts);
757 if (state) *state = sts;
758 return dfuh->res;
759}
760
761static dfuAPIResult dfuapi_req_dnload(struct dfuDev* dfuh, uint16_t blknum,
762 uint16_t len, unsigned char *data)
763{
764 struct usbControlSetup cs = { 0x21, DFU_DNLOAD, blknum, 0, len };
765 return dfuapi_request(dfuh, &cs, data);
766}
767
768/* not used */
769#if 0
770static dfuAPIResult dfuapi_req_upload(struct dfuDev* dfuh,
771 uint16_t blknum, uint16_t len, unsigned char *data)
772{
773 struct usbControlSetup cs = { 0xA1, DFU_UPLOAD, blknum, 0, len };
774 return dfuapi_request(dfuh, &cs, data);
775}
776
777static dfuAPIResult dfuapi_req_clrstatus(struct dfuDev* dfuh)
778{
779 struct usbControlSetup cs = { 0x21, DFU_CLRSTATUS, 0, 0, 0 };
780 return dfuapi_request(dfuh, &cs, NULL);
781}
782
783static dfuAPIResult dfuapi_req_abort(struct dfuDev* dfuh)
784{
785 struct usbControlSetup cs = { 0x21, DFU_ABORT, 0, 0, 0 };
786 return dfuapi_request(dfuh, &cs, NULL);
787}
788
789/* not implemented on DFU8702 */
790static dfuAPIResult dfuapi_req_detach(struct dfuDev* dfuh, int tmo)
791{
792 struct usbControlSetup cs = { 0x21, DFU_DETACH, tmo, 0, 0 };
793 return dfuapi_request(dfuh, &cs, NULL);
794}
795#endif
796
797static dfuAPIResult dfuapi_reset(struct dfuDev *dfuh)
798{
799 return dfuh->api->reset_fn(dfuh);
800}
801
802static dfuAPIResult dfuapi_send_packet(struct dfuDev* dfuh, uint16_t blknum,
803 uint16_t len, unsigned char *data, DFUStatus *status,
804 int *poll_tmo, DFUState *state, DFUState *pre_state)
805{
806 if (dfuapi_req_dnload(dfuh, blknum, len, data) != DFUAPISuccess)
807 goto error;
808
809 /* device is in dfuDLSYNC state, waiting for a GETSTATUS request
810 * to enter the next state, if she respond with dfuDLBUSY then
811 * we must wait to resend the GETSTATUS request */
812
813 if (dfuapi_req_getstatus(dfuh, status, poll_tmo, state) != DFUAPISuccess)
814 goto error;
815
816 if (*state == dfuDNBUSY) {
817 if (*poll_tmo)
818 sleep_ms(*poll_tmo);
819 if (pre_state)
820 if (dfuapi_req_getstate(dfuh, pre_state) != DFUAPISuccess)
821 goto error;
822 if (dfuapi_req_getstatus(dfuh, status, poll_tmo, state) != DFUAPISuccess)
823 goto error;
824 }
825
826bye:
827 return dfuh->res;
828error:
829 goto bye;
830}
831
832static void dfuapi_set_err(struct dfuDev *dfuh, char *str)
833{
834 dfuh->res = DFUAPIFail;
835 strncpy(dfuh->err, str, sizeof(dfuh->err));
836}
837
838static dfuAPIResult dfuapi_open(struct dfuDev *dfuh, int pid)
839{
840 int pid_l[N_KNOWN_PIDS+1] = { 0 };
841 struct dfuAPI *api;
842 unsigned i, p;
843
844 /* fill pid list */
845 if (pid)
846 pid_l[0] = pid;
847 else
848 for (p = 0; p < N_KNOWN_PIDS; p++)
849 pid_l[p] = known_pids[p].pid;
850
851 for (i = 0; i < N_DFU_APIS; i++)
852 {
853 api = &api_list[i];
854 if (api->open_fn(dfuh, pid_l) != DFUAPISuccess)
855 goto error;
856 if (dfuh->found_pid) {
857 /* ok */
858 dfuh->api = api;
859 printf("[INFO] %s: found %s\n", api->name, dfuh->descr);
860 for (p = 0; p < N_KNOWN_PIDS; p++) {
861 if (known_pids[p].pid == dfuh->found_pid) {
862 printf("[INFO] iPod %s, mode: %s\n", known_pids[p].desc,
863 known_pids[p].mode ? "WTF" : "DFU");
864 break;
865 }
866 }
867 fflush(stdout);
868 goto bye;
869 }
870 printf("[INFO] %s: no DFU devices found\n", api->name);
871 fflush(stdout);
872 }
873
874 /* error */
875 dfuapi_set_err(dfuh, "DFU device not found");
876
877bye:
878 return dfuh->res;
879error:
880 goto bye;
881}
882
883static void dfuapi_destroy(struct dfuDev *dfuh)
884{
885 if (dfuh) {
886 if (dfuh->api)
887 dfuh->api->close_fn(dfuh);
888 free(dfuh);
889 }
890}
891
892static struct dfuDev *dfuapi_create(void)
893{
894 return calloc(sizeof(struct dfuDev), 1);
895}
896
897
898/*
899 * app level functions
900 */
901static int ipoddfu_download_file(struct dfuDev* dfuh,
902 unsigned char *data, unsigned long size)
903{
904 unsigned int blknum, len, remaining;
905 int poll_tmo;
906 DFUStatus status;
907 DFUState state;
908
909 if (dfuapi_req_getstate(dfuh, &state) != DFUAPISuccess)
910 goto error;
911
912 if (state != dfuIDLE) {
913 dfuapi_set_err(dfuh, "Could not start DFU download: not idle");
914 goto error;
915 }
916
917 blknum = 0;
918 remaining = size;
919 while (remaining)
920 {
921 len = (remaining < DFU_PKT_SZ) ? remaining : DFU_PKT_SZ;
922
923 if (dfuapi_send_packet(dfuh, blknum, len, data + blknum*DFU_PKT_SZ,
924 &status, &poll_tmo, &state, NULL) != DFUAPISuccess)
925 goto error;
926
927 if (state != dfuDNLOAD_IDLE) {
928 dfuapi_set_err(dfuh, "DFU download aborted: unexpected state");
929 goto error;
930 }
931
932 remaining -= len;
933 blknum++;
934 }
935
936 /* send ZLP */
937 DFUState pre_state = appIDLE; /* dummy state */
938 if (dfuapi_send_packet(dfuh, blknum, 0, NULL,
939 &status, &poll_tmo, &state, &pre_state) != DFUAPISuccess) {
940 if (pre_state == dfuMANIFEST_SYNC)
941 goto ok; /* pwnaged .dfu file */
942 goto error;
943 }
944
945 if (state != dfuMANIFEST) {
946 if (status == errFIRMWARE)
947 dfuapi_set_err(dfuh, "DFU download failed: corrupt firmware");
948 else
949 dfuapi_set_err(dfuh, "DFU download failed: unexpected state");
950 goto error;
951 }
952
953 /* wait for manifest stage */
954 if (poll_tmo)
955 sleep_ms(poll_tmo);
956
957 if (dfuapi_req_getstatus(dfuh, &status, NULL, &state) != DFUAPISuccess)
958 goto ok; /* 1223 .dfu file */
959
960 /* XXX: next code never tested */
961
962 if (state != dfuMANIFEST_WAIT_RESET) {
963 if (status == errVERIFY)
964 dfuapi_set_err(dfuh, "DFU manifest failed: wrong FW verification");
965 else
966 dfuapi_set_err(dfuh, "DFU manifest failed: unexpected state");
967 goto error;
968 }
969
970 if (dfuapi_reset(dfuh) != DFUAPISuccess)
971 goto error;
972
973ok:
974 return 1;
975error:
976 return 0;
977}
978
979/* exported functions */
980int ipoddfu_send(int pid, unsigned char *data, int size,
981 char* errstr, int errstrsize)
982{
983 struct dfuDev *dfuh;
984 unsigned char *buf;
985 uint32_t checksum;
986 int ret = 1; /* ok */
987
988 dfuh = dfuapi_create();
989
990 buf = malloc(size+4);
991 if (!buf) {
992 dfuapi_set_err(dfuh, "Could not allocate memory for DFU buffer");
993 goto error;
994 }
995
996 if (memcmp(data, IM3_IDENT, 4)) {
997 dfuapi_set_err(dfuh, "Bad DFU image data");
998 goto error;
999 }
1000
1001 crc32_init();
1002 checksum = crc32(data, size, 0);
1003 memcpy(buf, data, size);
1004 put_uint32le(buf+size, ~checksum);
1005
1006 if (dfuapi_open(dfuh, pid) != DFUAPISuccess)
1007 goto error;
1008
1009 if (!ipoddfu_download_file(dfuh, buf, size+4))
1010 goto error;
1011
1012bye:
1013 if (buf) free(buf);
1014 dfuapi_destroy(dfuh);
1015 return ret;
1016
1017error:
1018 ret = 0;
1019 if (errstr)
1020 snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err);
1021 goto bye;
1022}
1023
1024/* search for the DFU device and gets its DFUState */
1025int ipoddfu_scan(int pid, int *state, int reset,
1026 char* errstr, int errstrsize)
1027{
1028 struct dfuDev *dfuh;
1029 int ret = 1; /* ok */
1030
1031 dfuh = dfuapi_create();
1032
1033 if (dfuapi_open(dfuh, pid) != DFUAPISuccess)
1034 goto error;
1035
1036 if (reset)
1037 if (dfuapi_reset(dfuh) != DFUAPISuccess)
1038 goto error;
1039
1040 if (state) {
1041 DFUState sts;
1042 if (dfuapi_req_getstate(dfuh, &sts) != DFUAPISuccess)
1043 goto error;
1044 *state = (int)sts;
1045 }
1046
1047bye:
1048 dfuapi_destroy(dfuh);
1049 return ret;
1050
1051error:
1052 ret = 0;
1053 if (errstr)
1054 snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err);
1055 goto bye;
1056}
1057
1058void ipoddfu_debug(int debug)
1059{
1060 DEBUG_DFUREQ = debug;
1061}