Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | ****************************************************************************/ |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 25 | |
| 26 | #include <stdio.h> |
| 27 | #include <stdlib.h> |
| 28 | #include <unistd.h> |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 29 | #include <stdbool.h> |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 30 | #include <string.h> |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 31 | #ifdef WIN32 |
| 32 | #include <windows.h> |
| 33 | #include <setupapi.h> |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 34 | #endif |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 35 | #ifdef USE_LIBUSBAPI |
| 36 | #include <libusb-1.0/libusb.h> |
| 37 | #endif |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 38 | #ifdef __APPLE__ |
| 39 | #include <CoreFoundation/CoreFoundation.h> |
| 40 | #include <IOKit/IOCFPlugIn.h> |
| 41 | #include <IOKit/usb/IOUSBLib.h> |
| 42 | #endif |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 43 | |
| 44 | #include "mks5lboot.h" |
| 45 | |
| 46 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 47 | #ifdef WIN32 |
| 48 | #define sleep_ms(ms) Sleep(ms) |
| 49 | #else |
| 50 | #include <time.h> |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | } |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 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 | } |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 67 | |
| 68 | /* |
| 69 | * CRC32 functions |
| 70 | * Based on public domain implementation by Finn Yannick Jacobs. |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 71 | * |
| 72 | * Written and copyright 1999 by Finn Yannick Jacobs |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 73 | * No rights were reserved to this, so feel free to |
| 74 | * manipulate or do with it, what you want or desire :) |
| 75 | */ |
| 76 | |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 77 | /* crc32table[] built by crc32_init() */ |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 78 | static uint32_t crc32table[256]; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 79 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 80 | /* Calculate crc32 */ |
| 81 | static uint32_t crc32(void *data, unsigned int len, uint32_t previousCrc32) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 82 | { |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 83 | uint32_t crc = ~previousCrc32; |
| 84 | unsigned char *d = (unsigned char*) data; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 85 | while (len--) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 86 | crc = (crc >> 8) ^ crc32table[(crc & 0xFF) ^ *d++]; |
| 87 | return ~crc; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | /* Calculate crc32table */ |
| 91 | static void crc32_init() |
| 92 | { |
| 93 | uint32_t poly = 0xEDB88320L; |
| 94 | uint32_t crc; |
| 95 | int i, j; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 96 | for (i = 0; i < 256; ++i) |
| 97 | { |
| 98 | crc = i; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 99 | for (j = 0; j < 8; ++j) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 100 | crc = (crc >> 1) ^ ((crc & 1) ? poly : 0); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 101 | crc32table[i] = crc; |
| 102 | } |
| 103 | } |
| 104 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 105 | /* USB */ |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 106 | #define APPLE_VID 0x05AC |
| 107 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 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[] = |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 115 | { |
| 116 | /* DFU */ |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 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" }, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 125 | /* WTF */ |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 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" }, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 137 | }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 138 | #define N_KNOWN_PIDS (sizeof(known_pids)/sizeof(struct pid_info)) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 158 | |
| 159 | /* |
| 160 | * DFU API |
| 161 | */ |
| 162 | #define DFU_PKT_SZ 2048 /* must be pow2 <= wTransferSize (2048) */ |
| 163 | |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 164 | /* DFU 1.1 specs */ |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 165 | typedef enum { |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 179 | typedef enum { |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 198 | typedef enum { |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 208 | typedef enum { |
| 209 | DFUAPIFail = 0, |
| 210 | DFUAPISuccess, |
| 211 | } dfuAPIResult; |
| 212 | |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 213 | struct dfuDev { |
| 214 | struct dfuAPI *api; |
| 215 | int found_pid; |
| 216 | int detached; |
| 217 | char descr[256]; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 218 | dfuAPIResult res; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 231 | #ifdef __APPLE__ |
| 232 | IOUSBDeviceInterface** dev; |
| 233 | kern_return_t kr; |
| 234 | #endif |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 235 | }; |
| 236 | |
| 237 | struct dfuAPI { |
| 238 | char *name; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 239 | dfuAPIResult (*open_fn)(struct dfuDev*, int*); |
| 240 | dfuAPIResult (*dfureq_fn)(struct dfuDev*, struct usbControlSetup*, void*); |
| 241 | dfuAPIResult (*reset_fn)(struct dfuDev*); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 242 | void (*close_fn)(struct dfuDev*); |
| 243 | }; |
| 244 | |
| 245 | |
| 246 | /* |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 247 | * DFU API low-level (specific) functions |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 248 | */ |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 249 | static bool dfu_check_id(int vid, int pid, int *pid_list) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 269 | static bool dfu_winapi_chkrc(struct dfuDev *dfuh, char *str, bool success) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 270 | { |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 271 | dfuh->res = (success) ? DFUAPISuccess : DFUAPIFail; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 272 | if (!success) { |
| 273 | dfuh->ec = GetLastError(); |
| 274 | snprintf(dfuh->err, sizeof(dfuh->err), "%s error %ld", str, dfuh->ec); |
| 275 | } |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 276 | return success; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 277 | } |
| 278 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 279 | static dfuAPIResult dfu_winapi_request(struct dfuDev *dfuh, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | } |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 300 | if (!rc) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 301 | dfu_add_reqerrstr(dfuh, cs); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 302 | |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 303 | return dfuh->res; |
| 304 | } |
| 305 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 306 | static dfuAPIResult dfu_winapi_reset(struct dfuDev *dfuh) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 307 | { |
| 308 | DWORD bytesReturned; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 309 | bool rc = DeviceIoControl(dfuh->fh, |
| 310 | 0x22000c, NULL, 0, NULL, 0, &bytesReturned, NULL); |
| 311 | dfu_winapi_chkrc(dfuh, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 312 | "Could not reset USB device: DeviceIoControl()", rc); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 313 | return dfuh->res; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 331 | static dfuAPIResult dfu_winapi_open(struct dfuDev *dfuh, int *pid_list) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 344 | dfuh->res = DFUAPISuccess; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 415 | static bool dfu_libusb_chkrc(struct dfuDev *dfuh, char *str) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 416 | { |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 417 | dfuh->res = (dfuh->rc < LIBUSB_SUCCESS) ? DFUAPIFail : DFUAPISuccess; |
| 418 | if (dfuh->res == DFUAPIFail) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 419 | snprintf(dfuh->err, sizeof(dfuh->err), |
| 420 | "%s: %s", str, libusb_error_name(dfuh->rc)); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 421 | return (dfuh->res == DFUAPISuccess); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 422 | } |
| 423 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 424 | static dfuAPIResult dfu_libusb_request(struct dfuDev *dfuh, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 434 | static dfuAPIResult dfu_libusb_reset(struct dfuDev *dfuh) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 435 | { |
| 436 | dfuh->rc = libusb_reset_device(dfuh->devh); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 437 | dfu_libusb_chkrc(dfuh, "Could not reset USB device"); |
| 438 | return dfuh->res; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 456 | static dfuAPIResult dfu_libusb_open(struct dfuDev *dfuh, int *pid_list) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 465 | dfuh->res = DFUAPISuccess; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 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 */ |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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__ |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 684 | { "IOKit", |
| 685 | dfu_iokit_open, |
| 686 | dfu_iokit_request, |
| 687 | dfu_iokit_reset, |
| 688 | dfu_iokit_close }, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 689 | #endif |
| 690 | }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 691 | #define N_DFU_APIS (sizeof(api_list)/sizeof(struct dfuAPI)) |
| 692 | |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 693 | |
| 694 | /* |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 695 | * DFU API common functions |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 696 | */ |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 697 | static int DEBUG_DFUREQ = 0; |
| 698 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 699 | static dfuAPIResult dfuapi_request(struct dfuDev *dfuh, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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) }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 710 | if (dfuh->api->dfureq_fn(dfuh, &css, &ste) != DFUAPISuccess) { |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 711 | snprintf(dfuh->err + strlen(dfuh->err), sizeof(dfuh->err) - |
| 712 | strlen(dfuh->err), " [DEBUG_DFUREQ ERROR: state=%d]", ste); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 713 | goto error; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 714 | } |
| 715 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 716 | dfuh->api->dfureq_fn(dfuh, cs, data); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 717 | fprintf(stderr, "[DEBUG]: REQ: ste=%d, cs=%2x/%d/%d/%d/%d -> %s", |
| 718 | ste, cs->bmRequestType, cs->bRequest, cs->wValue, |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 719 | cs->wIndex, cs->wLength, |
| 720 | (dfuh->res == DFUAPISuccess) ? "ok" : "ERROR"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 731 | |
| 732 | bye: |
| 733 | return dfuh->res; |
| 734 | error: |
| 735 | goto bye; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 736 | } |
| 737 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 738 | static dfuAPIResult dfuapi_req_getstatus(struct dfuDev *dfuh, |
| 739 | DFUStatus *status, int *poll_tmo /*ms*/, |
| 740 | DFUState *state) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 741 | { |
| 742 | struct usbStatusData sd = { 0, 0, 0, 0, 0, 0 }; |
| 743 | struct usbControlSetup cs = { 0xA1, DFU_GETSTATUS, 0, 0, sizeof(sd) }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 744 | dfuapi_request(dfuh, &cs, &sd); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 749 | return dfuh->res; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 750 | } |
| 751 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 752 | static dfuAPIResult dfuapi_req_getstate(struct dfuDev *dfuh, DFUState *state) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 753 | { |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 754 | unsigned char sts = 0; |
| 755 | struct usbControlSetup cs = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(sts) }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 756 | dfuapi_request(dfuh, &cs, &sts); |
| 757 | if (state) *state = sts; |
| 758 | return dfuh->res; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 759 | } |
| 760 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 761 | static dfuAPIResult dfuapi_req_dnload(struct dfuDev* dfuh, uint16_t blknum, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 762 | uint16_t len, unsigned char *data) |
| 763 | { |
| 764 | struct usbControlSetup cs = { 0x21, DFU_DNLOAD, blknum, 0, len }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 765 | return dfuapi_request(dfuh, &cs, data); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 766 | } |
| 767 | |
| 768 | /* not used */ |
| 769 | #if 0 |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 770 | static dfuAPIResult dfuapi_req_upload(struct dfuDev* dfuh, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 771 | uint16_t blknum, uint16_t len, unsigned char *data) |
| 772 | { |
| 773 | struct usbControlSetup cs = { 0xA1, DFU_UPLOAD, blknum, 0, len }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 774 | return dfuapi_request(dfuh, &cs, data); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 775 | } |
| 776 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 777 | static dfuAPIResult dfuapi_req_clrstatus(struct dfuDev* dfuh) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 778 | { |
| 779 | struct usbControlSetup cs = { 0x21, DFU_CLRSTATUS, 0, 0, 0 }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 780 | return dfuapi_request(dfuh, &cs, NULL); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 781 | } |
| 782 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 783 | static dfuAPIResult dfuapi_req_abort(struct dfuDev* dfuh) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 784 | { |
| 785 | struct usbControlSetup cs = { 0x21, DFU_ABORT, 0, 0, 0 }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 786 | return dfuapi_request(dfuh, &cs, NULL); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 787 | } |
| 788 | |
| 789 | /* not implemented on DFU8702 */ |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 790 | static dfuAPIResult dfuapi_req_detach(struct dfuDev* dfuh, int tmo) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 791 | { |
| 792 | struct usbControlSetup cs = { 0x21, DFU_DETACH, tmo, 0, 0 }; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 793 | return dfuapi_request(dfuh, &cs, NULL); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 794 | } |
| 795 | #endif |
| 796 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 797 | static dfuAPIResult dfuapi_reset(struct dfuDev *dfuh) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 798 | { |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 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; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 808 | |
| 809 | /* device is in dfuDLSYNC state, waiting for a GETSTATUS request |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 810 | * to enter the next state, if she respond with dfuDLBUSY then |
| 811 | * we must wait to resend the GETSTATUS request */ |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 812 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 813 | if (dfuapi_req_getstatus(dfuh, status, poll_tmo, state) != DFUAPISuccess) |
| 814 | goto error; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 815 | |
| 816 | if (*state == dfuDNBUSY) { |
| 817 | if (*poll_tmo) |
| 818 | sleep_ms(*poll_tmo); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 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; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 824 | } |
| 825 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 826 | bye: |
| 827 | return dfuh->res; |
| 828 | error: |
| 829 | goto bye; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 830 | } |
| 831 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 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, |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 902 | unsigned char *data, unsigned long size) |
| 903 | { |
| 904 | unsigned int blknum, len, remaining; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 905 | int poll_tmo; |
| 906 | DFUStatus status; |
| 907 | DFUState state; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 908 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 909 | if (dfuapi_req_getstate(dfuh, &state) != DFUAPISuccess) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 910 | goto error; |
| 911 | |
| 912 | if (state != dfuIDLE) { |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 913 | dfuapi_set_err(dfuh, "Could not start DFU download: not idle"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 923 | if (dfuapi_send_packet(dfuh, blknum, len, data + blknum*DFU_PKT_SZ, |
| 924 | &status, &poll_tmo, &state, NULL) != DFUAPISuccess) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 925 | goto error; |
| 926 | |
| 927 | if (state != dfuDNLOAD_IDLE) { |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 928 | dfuapi_set_err(dfuh, "DFU download aborted: unexpected state"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 929 | goto error; |
| 930 | } |
| 931 | |
| 932 | remaining -= len; |
| 933 | blknum++; |
| 934 | } |
| 935 | |
| 936 | /* send ZLP */ |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 937 | DFUState pre_state = appIDLE; /* dummy state */ |
| 938 | if (dfuapi_send_packet(dfuh, blknum, 0, NULL, |
| 939 | &status, &poll_tmo, &state, &pre_state) != DFUAPISuccess) { |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 947 | dfuapi_set_err(dfuh, "DFU download failed: corrupt firmware"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 948 | else |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 949 | dfuapi_set_err(dfuh, "DFU download failed: unexpected state"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 950 | goto error; |
| 951 | } |
| 952 | |
| 953 | /* wait for manifest stage */ |
| 954 | if (poll_tmo) |
| 955 | sleep_ms(poll_tmo); |
| 956 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 957 | if (dfuapi_req_getstatus(dfuh, &status, NULL, &state) != DFUAPISuccess) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 958 | goto ok; /* 1223 .dfu file */ |
| 959 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 960 | /* XXX: next code never tested */ |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 961 | |
| 962 | if (state != dfuMANIFEST_WAIT_RESET) { |
| 963 | if (status == errVERIFY) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 964 | dfuapi_set_err(dfuh, "DFU manifest failed: wrong FW verification"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 965 | else |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 966 | dfuapi_set_err(dfuh, "DFU manifest failed: unexpected state"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 967 | goto error; |
| 968 | } |
| 969 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 970 | if (dfuapi_reset(dfuh) != DFUAPISuccess) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 971 | goto error; |
| 972 | |
| 973 | ok: |
| 974 | return 1; |
| 975 | error: |
| 976 | return 0; |
| 977 | } |
| 978 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 979 | /* exported functions */ |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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; |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 985 | uint32_t checksum; |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 986 | int ret = 1; /* ok */ |
| 987 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 988 | dfuh = dfuapi_create(); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 989 | |
| 990 | buf = malloc(size+4); |
| 991 | if (!buf) { |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 992 | dfuapi_set_err(dfuh, "Could not allocate memory for DFU buffer"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 993 | goto error; |
| 994 | } |
| 995 | |
| 996 | if (memcmp(data, IM3_IDENT, 4)) { |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 997 | dfuapi_set_err(dfuh, "Bad DFU image data"); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 998 | goto error; |
| 999 | } |
| 1000 | |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1001 | crc32_init(); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1002 | checksum = crc32(data, size, 0); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1003 | memcpy(buf, data, size); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1004 | put_uint32le(buf+size, ~checksum); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1005 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1006 | if (dfuapi_open(dfuh, pid) != DFUAPISuccess) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1007 | goto error; |
| 1008 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1009 | if (!ipoddfu_download_file(dfuh, buf, size+4)) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1010 | goto error; |
| 1011 | |
| 1012 | bye: |
| 1013 | if (buf) free(buf); |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1014 | dfuapi_destroy(dfuh); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1031 | dfuh = dfuapi_create(); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1032 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1033 | if (dfuapi_open(dfuh, pid) != DFUAPISuccess) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1034 | goto error; |
| 1035 | |
| 1036 | if (reset) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1037 | if (dfuapi_reset(dfuh) != DFUAPISuccess) |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1038 | goto error; |
| 1039 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1040 | if (state) { |
| 1041 | DFUState sts; |
| 1042 | if (dfuapi_req_getstate(dfuh, &sts) != DFUAPISuccess) |
| 1043 | goto error; |
| 1044 | *state = (int)sts; |
| 1045 | } |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1046 | |
| 1047 | bye: |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 1048 | dfuapi_destroy(dfuh); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 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 | } |