diff options
Diffstat (limited to 'rbutil/mks5lboot/ipoddfu.c')
-rw-r--r-- | rbutil/mks5lboot/ipoddfu.c | 875 |
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 | |||
47 | static 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() */ | ||
68 | static 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 | */ | ||
74 | static 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 */ | ||
87 | static 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 | |||
116 | static 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 | |||
142 | struct 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 | |||
151 | struct 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 */ | ||
161 | typedef 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 | |||
175 | typedef 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 | |||
194 | typedef 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 | |||
204 | struct 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 | |||
224 | struct 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 | */ | ||
236 | static 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 */ | ||
248 | static 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 | ||
256 | static 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 | |||
266 | static 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 | |||
293 | static 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 | |||
302 | static 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 | |||
314 | static const GUID GUID_AAPLDFU = | ||
315 | { 0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; | ||
316 | |||
317 | static 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 | |||
388 | bye: | ||
389 | if (path) free(path); | ||
390 | if (details) free(details); | ||
391 | if (devinfo) SetupDiDestroyDeviceInfoList(devinfo); | ||
392 | return dfuh->res; | ||
393 | |||
394 | error: | ||
395 | dfu_winapi_close(dfuh); | ||
396 | goto bye; | ||
397 | } | ||
398 | #endif /* WIN32 */ | ||
399 | |||
400 | #ifdef USE_LIBUSBAPI | ||
401 | static 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 | |||
410 | static 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 | |||
420 | static 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 | |||
426 | static 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 | |||
441 | static 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 | |||
508 | bye: | ||
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 | |||
515 | error: | ||
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 | */ | ||
524 | static 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 | */ | ||
549 | static void dfu_set_errstr(struct dfuDev *dfuh, char *str) | ||
550 | { | ||
551 | strncpy(dfuh->err, str, sizeof(dfuh->err)); | ||
552 | } | ||
553 | |||
554 | static int DEBUG_DFUREQ = 0; | ||
555 | |||
556 | static 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 | |||
590 | static 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 | |||
603 | static 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 | |||
614 | static 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 | ||
623 | static 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 | |||
630 | static 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 | |||
636 | static 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 */ | ||
643 | static 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 | |||
650 | static 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 | |||
676 | static 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 | |||
747 | ok: | ||
748 | return 1; | ||
749 | error: | ||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | static 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 | |||
780 | static 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 | |||
789 | static struct dfuDev *dfu_create() | ||
790 | { | ||
791 | return calloc(sizeof(struct dfuDev), 1); | ||
792 | } | ||
793 | |||
794 | /* | ||
795 | * exported functions | ||
796 | */ | ||
797 | int 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 | |||
830 | bye: | ||
831 | if (buf) free(buf); | ||
832 | dfu_destroy(dfuh); | ||
833 | return ret; | ||
834 | |||
835 | error: | ||
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 */ | ||
843 | int 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 | |||
861 | bye: | ||
862 | dfu_destroy(dfuh); | ||
863 | return ret; | ||
864 | |||
865 | error: | ||
866 | ret = 0; | ||
867 | if (errstr) | ||
868 | snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err); | ||
869 | goto bye; | ||
870 | } | ||
871 | |||
872 | void ipoddfu_debug(int debug) | ||
873 | { | ||
874 | DEBUG_DFUREQ = debug; | ||
875 | } | ||