diff options
Diffstat (limited to 'utils/mks5lboot/ipoddfu.c')
-rw-r--r-- | utils/mks5lboot/ipoddfu.c | 1061 |
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> | ||
51 | static 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 | |||
60 | static 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() */ | ||
78 | static uint32_t crc32table[256]; | ||
79 | |||
80 | /* Calculate crc32 */ | ||
81 | static 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 */ | ||
91 | static 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 | |||
108 | struct pid_info { | ||
109 | int pid; | ||
110 | int mode; /* 0->DFU, 1->WTF */ | ||
111 | char *desc; | ||
112 | }; | ||
113 | |||
114 | struct 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 | |||
140 | struct 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 | |||
149 | struct 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 */ | ||
165 | typedef 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 | |||
179 | typedef 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 | |||
198 | typedef 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 | |||
208 | typedef enum { | ||
209 | DFUAPIFail = 0, | ||
210 | DFUAPISuccess, | ||
211 | } dfuAPIResult; | ||
212 | |||
213 | struct 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 | |||
237 | struct 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 | */ | ||
249 | static 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 */ | ||
261 | static 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 | ||
269 | static 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 | |||
279 | static 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 | |||
306 | static 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 | |||
316 | static 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 | |||
328 | static const GUID GUID_AAPLDFU = | ||
329 | { 0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; | ||
330 | |||
331 | static 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 | |||
402 | bye: | ||
403 | if (path) free(path); | ||
404 | if (details) free(details); | ||
405 | if (devinfo) SetupDiDestroyDeviceInfoList(devinfo); | ||
406 | return dfuh->res; | ||
407 | |||
408 | error: | ||
409 | dfu_winapi_close(dfuh); | ||
410 | goto bye; | ||
411 | } | ||
412 | #endif /* WIN32 */ | ||
413 | |||
414 | #ifdef USE_LIBUSBAPI | ||
415 | static 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 | |||
424 | static 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 | |||
434 | static 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 | |||
441 | static 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 | |||
456 | static 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 | |||
523 | bye: | ||
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 | |||
530 | error: | ||
531 | goto bye; | ||
532 | } | ||
533 | #endif /* USE_LIBUSBAPI */ | ||
534 | |||
535 | #ifdef __APPLE__ | ||
536 | static 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 | |||
545 | static 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 | |||
563 | static 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 | |||
577 | static 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 | |||
586 | static 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 | |||
656 | bye: | ||
657 | if (usb_iterator != IO_OBJECT_NULL) | ||
658 | IOObjectRelease(usb_iterator); | ||
659 | return dfuh->res; | ||
660 | |||
661 | error: | ||
662 | goto bye; | ||
663 | } | ||
664 | #endif /* __APPLE__ */ | ||
665 | |||
666 | /* list of suported APIs */ | ||
667 | static 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 | */ | ||
697 | static int DEBUG_DFUREQ = 0; | ||
698 | |||
699 | static 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 | |||
732 | bye: | ||
733 | return dfuh->res; | ||
734 | error: | ||
735 | goto bye; | ||
736 | } | ||
737 | |||
738 | static 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 | |||
752 | static 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 | |||
761 | static 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 | ||
770 | static 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 | |||
777 | static 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 | |||
783 | static 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 */ | ||
790 | static 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 | |||
797 | static dfuAPIResult dfuapi_reset(struct dfuDev *dfuh) | ||
798 | { | ||
799 | return dfuh->api->reset_fn(dfuh); | ||
800 | } | ||
801 | |||
802 | static 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 | |||
826 | bye: | ||
827 | return dfuh->res; | ||
828 | error: | ||
829 | goto bye; | ||
830 | } | ||
831 | |||
832 | static void dfuapi_set_err(struct dfuDev *dfuh, char *str) | ||
833 | { | ||
834 | dfuh->res = DFUAPIFail; | ||
835 | strncpy(dfuh->err, str, sizeof(dfuh->err)); | ||
836 | } | ||
837 | |||
838 | static 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 | |||
877 | bye: | ||
878 | return dfuh->res; | ||
879 | error: | ||
880 | goto bye; | ||
881 | } | ||
882 | |||
883 | static 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 | |||
892 | static struct dfuDev *dfuapi_create(void) | ||
893 | { | ||
894 | return calloc(sizeof(struct dfuDev), 1); | ||
895 | } | ||
896 | |||
897 | |||
898 | /* | ||
899 | * app level functions | ||
900 | */ | ||
901 | static 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 | |||
973 | ok: | ||
974 | return 1; | ||
975 | error: | ||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | /* exported functions */ | ||
980 | int 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 | |||
1012 | bye: | ||
1013 | if (buf) free(buf); | ||
1014 | dfuapi_destroy(dfuh); | ||
1015 | return ret; | ||
1016 | |||
1017 | error: | ||
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 */ | ||
1025 | int 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 | |||
1047 | bye: | ||
1048 | dfuapi_destroy(dfuh); | ||
1049 | return ret; | ||
1050 | |||
1051 | error: | ||
1052 | ret = 0; | ||
1053 | if (errstr) | ||
1054 | snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err); | ||
1055 | goto bye; | ||
1056 | } | ||
1057 | |||
1058 | void ipoddfu_debug(int debug) | ||
1059 | { | ||
1060 | DEBUG_DFUREQ = debug; | ||
1061 | } | ||