diff options
Diffstat (limited to 'src/libirecovery.c')
| -rw-r--r-- | src/libirecovery.c | 1684 |
1 files changed, 1684 insertions, 0 deletions
diff --git a/src/libirecovery.c b/src/libirecovery.c new file mode 100644 index 0000000..4419b50 --- /dev/null +++ b/src/libirecovery.c | |||
| @@ -0,0 +1,1684 @@ | |||
| 1 | /** | ||
| 2 | * GreenPois0n iRecovery - libirecovery.c | ||
| 3 | * Copyright (C) 2010 Chronic-Dev Team | ||
| 4 | * Copyright (C) 2010 Joshua Hill | ||
| 5 | * Copyright (C) 2008-2011 Nicolas Haunold | ||
| 6 | * | ||
| 7 | * This program is free software: you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation, either version 3 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 19 | **/ | ||
| 20 | |||
| 21 | #include <stdio.h> | ||
| 22 | #include <stdint.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | #include <unistd.h> | ||
| 26 | |||
| 27 | #ifndef WIN32 | ||
| 28 | #include <libusb-1.0/libusb.h> | ||
| 29 | #define _FMT_qX "%qX" | ||
| 30 | #define _FMT_016llx "%016llx" | ||
| 31 | #else | ||
| 32 | #define WIN32_LEAN_AND_MEAN | ||
| 33 | #include <windows.h> | ||
| 34 | #include <setupapi.h> | ||
| 35 | #define _FMT_qX "%I64X" | ||
| 36 | #define _FMT_016llx "%016I64x" | ||
| 37 | #endif | ||
| 38 | |||
| 39 | #include "libirecovery.h" | ||
| 40 | |||
| 41 | #define USB_TIMEOUT 10000 | ||
| 42 | |||
| 43 | #define BUFFER_SIZE 0x1000 | ||
| 44 | #define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__) | ||
| 45 | |||
| 46 | static int libirecovery_debug = 0; | ||
| 47 | #ifndef WIN32 | ||
| 48 | static libusb_context* libirecovery_context = NULL; | ||
| 49 | #endif | ||
| 50 | |||
| 51 | int irecv_write_file(const char* filename, const void* data, size_t size); | ||
| 52 | int irecv_read_file(const char* filename, char** data, uint32_t* size); | ||
| 53 | |||
| 54 | static unsigned int dfu_hash_t1[256] = { | ||
| 55 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, | ||
| 56 | 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, | ||
| 57 | 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, | ||
| 58 | 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, | ||
| 59 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, | ||
| 60 | 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, | ||
| 61 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, | ||
| 62 | 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, | ||
| 63 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, | ||
| 64 | 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, | ||
| 65 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, | ||
| 66 | 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, | ||
| 67 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, | ||
| 68 | 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, | ||
| 69 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, | ||
| 70 | 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, | ||
| 71 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, | ||
| 72 | 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, | ||
| 73 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, | ||
| 74 | 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, | ||
| 75 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, | ||
| 76 | 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, | ||
| 77 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, | ||
| 78 | 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, | ||
| 79 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, | ||
| 80 | 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, | ||
| 81 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, | ||
| 82 | 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, | ||
| 83 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, | ||
| 84 | 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, | ||
| 85 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, | ||
| 86 | 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, | ||
| 87 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, | ||
| 88 | 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, | ||
| 89 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, | ||
| 90 | 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, | ||
| 91 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, | ||
| 92 | 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, | ||
| 93 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, | ||
| 94 | 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, | ||
| 95 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, | ||
| 96 | 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, | ||
| 97 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, | ||
| 98 | 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, | ||
| 99 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, | ||
| 100 | 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, | ||
| 101 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, | ||
| 102 | 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, | ||
| 103 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, | ||
| 104 | 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, | ||
| 105 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, | ||
| 106 | 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, | ||
| 107 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, | ||
| 108 | 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, | ||
| 109 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, | ||
| 110 | 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, | ||
| 111 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, | ||
| 112 | 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, | ||
| 113 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, | ||
| 114 | 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, | ||
| 115 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, | ||
| 116 | 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, | ||
| 117 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, | ||
| 118 | 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, | ||
| 119 | }; | ||
| 120 | |||
| 121 | #define dfu_hash_step(a,b) \ | ||
| 122 | a = (dfu_hash_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8)) | ||
| 123 | |||
| 124 | #ifdef WIN32 | ||
| 125 | static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}}; | ||
| 126 | static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; | ||
| 127 | |||
| 128 | typedef struct usb_control_request { | ||
| 129 | uint8_t bmRequestType; | ||
| 130 | uint8_t bRequest; | ||
| 131 | uint16_t wValue; | ||
| 132 | uint16_t wIndex; | ||
| 133 | uint16_t wLength; | ||
| 134 | |||
| 135 | char data[]; | ||
| 136 | } usb_control_request; | ||
| 137 | |||
| 138 | int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size); | ||
| 139 | |||
| 140 | irecv_error_t mobiledevice_openpipes(irecv_client_t client); | ||
| 141 | void mobiledevice_closepipes(irecv_client_t client); | ||
| 142 | |||
| 143 | irecv_error_t mobiledevice_connect(irecv_client_t* client, unsigned long long ecid) { | ||
| 144 | irecv_error_t ret; | ||
| 145 | int found = 0; | ||
| 146 | SP_DEVICE_INTERFACE_DATA currentInterface; | ||
| 147 | HDEVINFO usbDevices; | ||
| 148 | DWORD i; | ||
| 149 | irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client)); | ||
| 150 | memset(_client, 0, sizeof(struct irecv_client)); | ||
| 151 | |||
| 152 | // Get DFU paths | ||
| 153 | usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DFU, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); | ||
| 154 | memset(¤tInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA)); | ||
| 155 | currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | ||
| 156 | for(i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_DFU, i, ¤tInterface); i++) { | ||
| 157 | if (_client->DfuPath) { | ||
| 158 | free(_client->DfuPath); | ||
| 159 | _client->DfuPath = NULL; | ||
| 160 | } | ||
| 161 | _client->handle = NULL; | ||
| 162 | DWORD requiredSize = 0; | ||
| 163 | PSP_DEVICE_INTERFACE_DETAIL_DATA details; | ||
| 164 | SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); | ||
| 165 | details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); | ||
| 166 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | ||
| 167 | if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { | ||
| 168 | free(details); | ||
| 169 | continue; | ||
| 170 | } else { | ||
| 171 | LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); | ||
| 172 | memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); | ||
| 173 | free(details); | ||
| 174 | |||
| 175 | _client->DfuPath = result; | ||
| 176 | if (mobiledevice_openpipes(_client) != IRECV_E_SUCCESS) { | ||
| 177 | mobiledevice_closepipes(_client); | ||
| 178 | continue; | ||
| 179 | } | ||
| 180 | |||
| 181 | if (ecid == kWTFMode) { | ||
| 182 | if (_client->mode != kWTFMode) { | ||
| 183 | // special ecid case, ignore !kWTFMode | ||
| 184 | continue; | ||
| 185 | } else { | ||
| 186 | ecid = 0; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | if ((ecid != 0) && (_client->mode == kWTFMode)) { | ||
| 191 | // we can't get ecid in WTF mode | ||
| 192 | mobiledevice_closepipes(_client); | ||
| 193 | continue; | ||
| 194 | } | ||
| 195 | |||
| 196 | _client->serial[0] = '\0'; | ||
| 197 | if ((sscanf(result, "\\\\?\\usb#vid_%*04x&pid_%*04x#%s#", _client->serial) != 1) || (_client->serial[0] == '\0')) { | ||
| 198 | mobiledevice_closepipes(_client); | ||
| 199 | continue; | ||
| 200 | } | ||
| 201 | |||
| 202 | char* p = strchr(_client->serial, '#'); | ||
| 203 | if (p) { | ||
| 204 | *p = '\0'; | ||
| 205 | } | ||
| 206 | |||
| 207 | int j; | ||
| 208 | for (j = 0; j < strlen(_client->serial); j++) { | ||
| 209 | if (_client->serial[j] == '_') { | ||
| 210 | _client->serial[j] = ' '; | ||
| 211 | } else { | ||
| 212 | _client->serial[j] = toupper(_client->serial[j]); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | if (ecid != 0) { | ||
| 217 | char* ecid_string = strstr(_client->serial, "ECID:"); | ||
| 218 | if (ecid_string == NULL) { | ||
| 219 | debug("%s: could not get ECID for device\n", __func__); | ||
| 220 | mobiledevice_closepipes(_client); | ||
| 221 | continue; | ||
| 222 | } | ||
| 223 | |||
| 224 | unsigned long long this_ecid = 0; | ||
| 225 | sscanf(ecid_string, "ECID:" _FMT_qX, (unsigned long long*)&this_ecid); | ||
| 226 | if (this_ecid != ecid) { | ||
| 227 | mobiledevice_closepipes(_client); | ||
| 228 | continue; | ||
| 229 | } | ||
| 230 | debug("found device with ECID " _FMT_016llx "\n", (unsigned long long)ecid); | ||
| 231 | } | ||
| 232 | found = 1; | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | SetupDiDestroyDeviceInfoList(usbDevices); | ||
| 237 | |||
| 238 | if (found) { | ||
| 239 | *client = _client; | ||
| 240 | return IRECV_E_SUCCESS; | ||
| 241 | } | ||
| 242 | |||
| 243 | // Get iBoot path | ||
| 244 | usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IBOOT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); | ||
| 245 | memset(¤tInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA)); | ||
| 246 | currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | ||
| 247 | for(i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_IBOOT, i, ¤tInterface); i++) { | ||
| 248 | if (_client->iBootPath) { | ||
| 249 | free(_client->iBootPath); | ||
| 250 | _client->iBootPath = NULL; | ||
| 251 | } | ||
| 252 | _client->handle = NULL; | ||
| 253 | DWORD requiredSize = 0; | ||
| 254 | PSP_DEVICE_INTERFACE_DETAIL_DATA details; | ||
| 255 | SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); | ||
| 256 | details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); | ||
| 257 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | ||
| 258 | if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { | ||
| 259 | free(details); | ||
| 260 | continue; | ||
| 261 | } else { | ||
| 262 | LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); | ||
| 263 | memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); | ||
| 264 | free(details); | ||
| 265 | |||
| 266 | _client->iBootPath = result; | ||
| 267 | if (mobiledevice_openpipes(_client) != IRECV_E_SUCCESS) { | ||
| 268 | mobiledevice_closepipes(_client); | ||
| 269 | continue; | ||
| 270 | } | ||
| 271 | |||
| 272 | if ((ecid != 0) && (_client->mode == kWTFMode)) { | ||
| 273 | // we can't get ecid in WTF mode | ||
| 274 | mobiledevice_closepipes(_client); | ||
| 275 | continue; | ||
| 276 | } | ||
| 277 | |||
| 278 | _client->serial[0] = '\0'; | ||
| 279 | if ((sscanf(result, "\\\\?\\usb#vid_%*04x&pid_%*04x#%s#", _client->serial) != 1) || (_client->serial[0] == '\0')) { | ||
| 280 | mobiledevice_closepipes(_client); | ||
| 281 | continue; | ||
| 282 | } | ||
| 283 | |||
| 284 | char* p = strchr(_client->serial, '#'); | ||
| 285 | if (p) { | ||
| 286 | *p = '\0'; | ||
| 287 | } | ||
| 288 | |||
| 289 | int j; | ||
| 290 | for (j = 0; j < strlen(_client->serial); j++) { | ||
| 291 | if (_client->serial[j] == '_') { | ||
| 292 | _client->serial[j] = ' '; | ||
| 293 | } else { | ||
| 294 | _client->serial[j] = toupper(_client->serial[j]); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | if (ecid != 0) { | ||
| 299 | char* ecid_string = strstr(_client->serial, "ECID:"); | ||
| 300 | if (ecid_string == NULL) { | ||
| 301 | debug("%s: could not get ECID for device\n", __func__); | ||
| 302 | mobiledevice_closepipes(_client); | ||
| 303 | continue; | ||
| 304 | } | ||
| 305 | |||
| 306 | unsigned long long this_ecid = 0; | ||
| 307 | sscanf(ecid_string, "ECID:" _FMT_qX, (unsigned long long*)&this_ecid); | ||
| 308 | if (this_ecid != ecid) { | ||
| 309 | mobiledevice_closepipes(_client); | ||
| 310 | continue; | ||
| 311 | } | ||
| 312 | debug("found device with ECID " _FMT_016llx" \n", (unsigned long long)ecid); | ||
| 313 | } | ||
| 314 | found = 1; | ||
| 315 | break; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | SetupDiDestroyDeviceInfoList(usbDevices); | ||
| 319 | |||
| 320 | if (!found) { | ||
| 321 | irecv_close(_client); | ||
| 322 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 323 | } | ||
| 324 | |||
| 325 | *client = _client; | ||
| 326 | return IRECV_E_SUCCESS; | ||
| 327 | } | ||
| 328 | |||
| 329 | irecv_error_t mobiledevice_openpipes(irecv_client_t client) { | ||
| 330 | if (client->iBootPath && !(client->hIB = CreateFile(client->iBootPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) { | ||
| 331 | irecv_close(client); | ||
| 332 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 333 | } | ||
| 334 | if (client->DfuPath && !(client->hDFU = CreateFile(client->DfuPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) { | ||
| 335 | irecv_close(client); | ||
| 336 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 337 | } | ||
| 338 | |||
| 339 | client->mode = 0; | ||
| 340 | if (client->iBootPath == NULL) { | ||
| 341 | if (strncmp(client->DfuPath, "\\\\?\\usb#vid_05ac&pid_", 21) == 0) { | ||
| 342 | sscanf(client->DfuPath+21, "%x#", &client->mode); | ||
| 343 | } | ||
| 344 | client->handle = client->hDFU; | ||
| 345 | } else { | ||
| 346 | if (strncmp(client->iBootPath, "\\\\?\\usb#vid_05ac&pid_", 21) == 0) { | ||
| 347 | sscanf(client->iBootPath+21, "%x#", &client->mode); | ||
| 348 | } | ||
| 349 | client->handle = client->hIB; | ||
| 350 | } | ||
| 351 | |||
| 352 | if (client->mode == 0) { | ||
| 353 | irecv_close(client); | ||
| 354 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 355 | } | ||
| 356 | |||
| 357 | return IRECV_E_SUCCESS; | ||
| 358 | } | ||
| 359 | |||
| 360 | void mobiledevice_closepipes(irecv_client_t client) { | ||
| 361 | if (client->hDFU!=NULL) { | ||
| 362 | CloseHandle(client->hDFU); | ||
| 363 | client->hDFU = NULL; | ||
| 364 | } | ||
| 365 | if (client->hIB!=NULL) { | ||
| 366 | CloseHandle(client->hIB); | ||
| 367 | client->hIB = NULL; | ||
| 368 | } | ||
| 369 | } | ||
| 370 | #endif | ||
| 371 | |||
| 372 | int check_context(irecv_client_t client) { | ||
| 373 | if (client == NULL || client->handle == NULL) { | ||
| 374 | return IRECV_E_NO_DEVICE; | ||
| 375 | } | ||
| 376 | |||
| 377 | return IRECV_E_SUCCESS; | ||
| 378 | } | ||
| 379 | |||
| 380 | void irecv_init() { | ||
| 381 | #ifndef WIN32 | ||
| 382 | libusb_init(&libirecovery_context); | ||
| 383 | #endif | ||
| 384 | } | ||
| 385 | |||
| 386 | void irecv_exit() { | ||
| 387 | #ifndef WIN32 | ||
| 388 | if (libirecovery_context != NULL) { | ||
| 389 | libusb_exit(libirecovery_context); | ||
| 390 | libirecovery_context = NULL; | ||
| 391 | } | ||
| 392 | #endif | ||
| 393 | } | ||
| 394 | |||
| 395 | #ifdef __APPLE__ | ||
| 396 | void dummy_callback() { } | ||
| 397 | #endif | ||
| 398 | |||
| 399 | int irecv_control_transfer( irecv_client_t client, | ||
| 400 | uint8_t bmRequestType, | ||
| 401 | uint8_t bRequest, | ||
| 402 | uint16_t wValue, | ||
| 403 | uint16_t wIndex, | ||
| 404 | unsigned char *data, | ||
| 405 | uint16_t wLength, | ||
| 406 | unsigned int timeout) { | ||
| 407 | #ifndef WIN32 | ||
| 408 | return libusb_control_transfer(client->handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout); | ||
| 409 | #else | ||
| 410 | DWORD count = 0; | ||
| 411 | DWORD ret; | ||
| 412 | BOOL bRet; | ||
| 413 | OVERLAPPED overlapped; | ||
| 414 | |||
| 415 | if (data == NULL) wLength = 0; | ||
| 416 | |||
| 417 | usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + wLength); | ||
| 418 | packet->bmRequestType = bmRequestType; | ||
| 419 | packet->bRequest = bRequest; | ||
| 420 | packet->wValue = wValue; | ||
| 421 | packet->wIndex = wIndex; | ||
| 422 | packet->wLength = wLength; | ||
| 423 | |||
| 424 | if (bmRequestType < 0x80 && wLength > 0) { | ||
| 425 | memcpy(packet->data, data, wLength); | ||
| 426 | } | ||
| 427 | |||
| 428 | memset(&overlapped, 0, sizeof(overlapped)); | ||
| 429 | overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
| 430 | DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + wLength, packet, sizeof(usb_control_request) + wLength, NULL, &overlapped); | ||
| 431 | ret = WaitForSingleObject(overlapped.hEvent, timeout); | ||
| 432 | bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE); | ||
| 433 | CloseHandle(overlapped.hEvent); | ||
| 434 | if (!bRet) { | ||
| 435 | CancelIo(client->handle); | ||
| 436 | free(packet); | ||
| 437 | return -1; | ||
| 438 | } | ||
| 439 | |||
| 440 | count -= sizeof(usb_control_request); | ||
| 441 | if (count > 0) { | ||
| 442 | if (bmRequestType >= 0x80) { | ||
| 443 | memcpy(data, packet->data, count); | ||
| 444 | } | ||
| 445 | } | ||
| 446 | free(packet); | ||
| 447 | return count; | ||
| 448 | #endif | ||
| 449 | } | ||
| 450 | |||
| 451 | int irecv_bulk_transfer(irecv_client_t client, | ||
| 452 | unsigned char endpoint, | ||
| 453 | unsigned char *data, | ||
| 454 | int length, | ||
| 455 | int *transferred, | ||
| 456 | unsigned int timeout) { | ||
| 457 | int ret; | ||
| 458 | |||
| 459 | #ifndef WIN32 | ||
| 460 | ret = libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout); | ||
| 461 | if (ret < 0) { | ||
| 462 | libusb_clear_halt(client->handle, endpoint); | ||
| 463 | } | ||
| 464 | #else | ||
| 465 | if (endpoint==0x4) { | ||
| 466 | ret = DeviceIoControl(client->handle, 0x220195, data, length, data, length, (PDWORD) transferred, NULL); | ||
| 467 | } else { | ||
| 468 | ret = 0; | ||
| 469 | } | ||
| 470 | ret = (ret==0) ? -1 : 0; | ||
| 471 | #endif | ||
| 472 | |||
| 473 | return ret; | ||
| 474 | } | ||
| 475 | |||
| 476 | int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) { | ||
| 477 | #ifndef WIN32 | ||
| 478 | return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size); | ||
| 479 | #else | ||
| 480 | irecv_error_t ret; | ||
| 481 | unsigned short langid = 0; | ||
| 482 | unsigned char data[255]; | ||
| 483 | int di, si; | ||
| 484 | memset(data, 0, sizeof(data)); | ||
| 485 | memset(buffer, 0, size); | ||
| 486 | |||
| 487 | ret = irecv_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data), USB_TIMEOUT); | ||
| 488 | |||
| 489 | if (ret < 0) return ret; | ||
| 490 | if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR; | ||
| 491 | if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR; | ||
| 492 | |||
| 493 | for (di = 0, si = 2; si < data[0]; si += 2) { | ||
| 494 | if (di >= (size - 1)) break; | ||
| 495 | if (data[si + 1]) { | ||
| 496 | /* high byte */ | ||
| 497 | buffer[di++] = '?'; | ||
| 498 | } else { | ||
| 499 | buffer[di++] = data[si]; | ||
| 500 | } | ||
| 501 | } | ||
| 502 | buffer[di] = 0; | ||
| 503 | |||
| 504 | return di; | ||
| 505 | #endif | ||
| 506 | } | ||
| 507 | |||
| 508 | irecv_error_t irecv_open(irecv_client_t* pclient, unsigned long long ecid) { | ||
| 509 | if(libirecovery_debug) { | ||
| 510 | irecv_set_debug_level(libirecovery_debug); | ||
| 511 | } | ||
| 512 | #ifndef WIN32 | ||
| 513 | int i = 0; | ||
| 514 | struct libusb_device* usb_device = NULL; | ||
| 515 | struct libusb_device** usb_device_list = NULL; | ||
| 516 | struct libusb_device_handle* usb_handle = NULL; | ||
| 517 | struct libusb_device_descriptor usb_descriptor; | ||
| 518 | |||
| 519 | *pclient = NULL; | ||
| 520 | irecv_error_t error = IRECV_E_SUCCESS; | ||
| 521 | int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list); | ||
| 522 | for (i = 0; i < usb_device_count; i++) { | ||
| 523 | usb_device = usb_device_list[i]; | ||
| 524 | libusb_get_device_descriptor(usb_device, &usb_descriptor); | ||
| 525 | if (usb_descriptor.idVendor == APPLE_VENDOR_ID) { | ||
| 526 | /* verify this device is in a mode we understand */ | ||
| 527 | if (usb_descriptor.idProduct == kRecoveryMode1 || | ||
| 528 | usb_descriptor.idProduct == kRecoveryMode2 || | ||
| 529 | usb_descriptor.idProduct == kRecoveryMode3 || | ||
| 530 | usb_descriptor.idProduct == kRecoveryMode4 || | ||
| 531 | usb_descriptor.idProduct == kWTFMode || | ||
| 532 | usb_descriptor.idProduct == kDfuMode) { | ||
| 533 | |||
| 534 | if (ecid == kWTFMode) { | ||
| 535 | if (usb_descriptor.idProduct != kWTFMode) { | ||
| 536 | // special ecid case, ignore !kWTFMode | ||
| 537 | continue; | ||
| 538 | } else { | ||
| 539 | ecid = 0; | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | if ((ecid != 0) && (usb_descriptor.idProduct == kWTFMode)) { | ||
| 544 | // we can't get ecid in WTF mode | ||
| 545 | continue; | ||
| 546 | } | ||
| 547 | |||
| 548 | debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct); | ||
| 549 | |||
| 550 | libusb_open(usb_device, &usb_handle); | ||
| 551 | if (usb_handle == NULL) { | ||
| 552 | debug("%s: can't connect to device...\n", __func__); | ||
| 553 | libusb_close(usb_handle); | ||
| 554 | if (ecid != 0) { | ||
| 555 | continue; | ||
| 556 | } | ||
| 557 | libusb_free_device_list(usb_device_list, 1); | ||
| 558 | libusb_exit(libirecovery_context); | ||
| 559 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 560 | } | ||
| 561 | |||
| 562 | irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client)); | ||
| 563 | if (client == NULL) { | ||
| 564 | libusb_free_device_list(usb_device_list, 1); | ||
| 565 | libusb_close(usb_handle); | ||
| 566 | libusb_exit(libirecovery_context); | ||
| 567 | return IRECV_E_OUT_OF_MEMORY; | ||
| 568 | } | ||
| 569 | |||
| 570 | memset(client, '\0', sizeof(struct irecv_client)); | ||
| 571 | client->interface = 0; | ||
| 572 | client->handle = usb_handle; | ||
| 573 | client->mode = usb_descriptor.idProduct; | ||
| 574 | |||
| 575 | /* cache usb serial */ | ||
| 576 | irecv_get_string_descriptor_ascii(client, usb_descriptor.iSerialNumber, (unsigned char*) client->serial, 255); | ||
| 577 | |||
| 578 | if (ecid != 0) { | ||
| 579 | char* ecid_string = strstr(client->serial, "ECID:"); | ||
| 580 | if (ecid_string == NULL) { | ||
| 581 | debug("%s: could not get ECID for device\n", __func__); | ||
| 582 | irecv_close(client); | ||
| 583 | continue; | ||
| 584 | } | ||
| 585 | |||
| 586 | unsigned long long this_ecid = 0; | ||
| 587 | sscanf(ecid_string, "ECID:" _FMT_qX, (unsigned long long*)&this_ecid); | ||
| 588 | if (this_ecid != ecid) { | ||
| 589 | irecv_close(client); | ||
| 590 | continue; | ||
| 591 | } | ||
| 592 | debug("found device with ECID " _FMT_016llx "\n", (unsigned long long)ecid); | ||
| 593 | } | ||
| 594 | |||
| 595 | error = irecv_set_configuration(client, 1); | ||
| 596 | if (error != IRECV_E_SUCCESS) { | ||
| 597 | return error; | ||
| 598 | } | ||
| 599 | |||
| 600 | if ((client->mode != kDfuMode) && (client->mode != kWTFMode)) { | ||
| 601 | error = irecv_set_interface(client, 0, 0); | ||
| 602 | if (client->mode > kRecoveryMode2) { | ||
| 603 | error = irecv_set_interface(client, 1, 1); | ||
| 604 | } | ||
| 605 | } else { | ||
| 606 | error = irecv_set_interface(client, 0, 0); | ||
| 607 | } | ||
| 608 | |||
| 609 | if (error != IRECV_E_SUCCESS) { | ||
| 610 | return error; | ||
| 611 | } | ||
| 612 | |||
| 613 | *pclient = client; | ||
| 614 | return IRECV_E_SUCCESS; | ||
| 615 | } | ||
| 616 | } | ||
| 617 | } | ||
| 618 | |||
| 619 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 620 | #else | ||
| 621 | int ret = mobiledevice_connect(pclient, ecid); | ||
| 622 | if (ret == IRECV_E_SUCCESS) { | ||
| 623 | irecv_client_t client = *pclient; | ||
| 624 | int error = IRECV_E_SUCCESS; | ||
| 625 | if ((client->mode != kDfuMode) && (client->mode != kWTFMode)) { | ||
| 626 | error = irecv_set_interface(client, 0, 0); | ||
| 627 | if (client->mode > kRecoveryMode2) { | ||
| 628 | error = irecv_set_interface(client, 1, 1); | ||
| 629 | } | ||
| 630 | } else { | ||
| 631 | error = irecv_set_interface(client, 0, 0); | ||
| 632 | } | ||
| 633 | if (error != IRECV_E_SUCCESS) { | ||
| 634 | debug("WARNING: set interface failed, error %d\n", error); | ||
| 635 | } | ||
| 636 | } | ||
| 637 | return ret; | ||
| 638 | #endif | ||
| 639 | } | ||
| 640 | |||
| 641 | irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) { | ||
| 642 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 643 | |||
| 644 | #ifndef WIN32 | ||
| 645 | debug("Setting to configuration %d\n", configuration); | ||
| 646 | |||
| 647 | int current = 0; | ||
| 648 | libusb_get_configuration(client->handle, ¤t); | ||
| 649 | if (current != configuration) { | ||
| 650 | if (libusb_set_configuration(client->handle, configuration) < 0) { | ||
| 651 | return IRECV_E_USB_CONFIGURATION; | ||
| 652 | } | ||
| 653 | } | ||
| 654 | |||
| 655 | client->config = configuration; | ||
| 656 | #endif | ||
| 657 | |||
| 658 | return IRECV_E_SUCCESS; | ||
| 659 | } | ||
| 660 | |||
| 661 | irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) { | ||
| 662 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 663 | |||
| 664 | debug("Setting to interface %d:%d\n", interface, alt_interface); | ||
| 665 | #ifndef WIN32 | ||
| 666 | // pod2g 2011-01-07: we may want to claim multiple interfaces | ||
| 667 | //libusb_release_interface(client->handle, client->interface); | ||
| 668 | |||
| 669 | if (libusb_claim_interface(client->handle, interface) < 0) { | ||
| 670 | return IRECV_E_USB_INTERFACE; | ||
| 671 | } | ||
| 672 | |||
| 673 | if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) { | ||
| 674 | return IRECV_E_USB_INTERFACE; | ||
| 675 | } | ||
| 676 | #else | ||
| 677 | if (irecv_control_transfer(client, 0, 0x0B, alt_interface, interface, NULL, 0, USB_TIMEOUT) < 0) { | ||
| 678 | return IRECV_E_USB_INTERFACE; | ||
| 679 | } | ||
| 680 | #endif | ||
| 681 | client->interface = interface; | ||
| 682 | client->alt_interface = alt_interface; | ||
| 683 | |||
| 684 | return IRECV_E_SUCCESS; | ||
| 685 | } | ||
| 686 | |||
| 687 | irecv_error_t irecv_reset(irecv_client_t client) { | ||
| 688 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 689 | |||
| 690 | #ifndef WIN32 | ||
| 691 | libusb_reset_device(client->handle); | ||
| 692 | #else | ||
| 693 | int ret; | ||
| 694 | DWORD count; | ||
| 695 | ret = DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL); | ||
| 696 | #endif | ||
| 697 | |||
| 698 | return IRECV_E_SUCCESS; | ||
| 699 | } | ||
| 700 | |||
| 701 | irecv_error_t irecv_open_attempts(irecv_client_t* pclient, unsigned long long ecid, int attempts) { | ||
| 702 | int i; | ||
| 703 | |||
| 704 | for (i = 0; i < attempts; i++) { | ||
| 705 | if(*pclient) { | ||
| 706 | irecv_close(*pclient); | ||
| 707 | *pclient = NULL; | ||
| 708 | } | ||
| 709 | if (irecv_open(pclient, ecid) != IRECV_E_SUCCESS) { | ||
| 710 | debug("Connection failed. Waiting 1 sec before retry.\n"); | ||
| 711 | sleep(1); | ||
| 712 | } else { | ||
| 713 | return IRECV_E_SUCCESS; | ||
| 714 | } | ||
| 715 | } | ||
| 716 | |||
| 717 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 718 | } | ||
| 719 | |||
| 720 | irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) { | ||
| 721 | switch(type) { | ||
| 722 | case IRECV_RECEIVED: | ||
| 723 | client->received_callback = callback; | ||
| 724 | break; | ||
| 725 | |||
| 726 | case IRECV_PROGRESS: | ||
| 727 | client->progress_callback = callback; | ||
| 728 | |||
| 729 | case IRECV_CONNECTED: | ||
| 730 | client->connected_callback = callback; | ||
| 731 | |||
| 732 | case IRECV_PRECOMMAND: | ||
| 733 | client->precommand_callback = callback; | ||
| 734 | break; | ||
| 735 | |||
| 736 | case IRECV_POSTCOMMAND: | ||
| 737 | client->postcommand_callback = callback; | ||
| 738 | break; | ||
| 739 | |||
| 740 | case IRECV_DISCONNECTED: | ||
| 741 | client->disconnected_callback = callback; | ||
| 742 | |||
| 743 | default: | ||
| 744 | return IRECV_E_UNKNOWN_ERROR; | ||
| 745 | } | ||
| 746 | |||
| 747 | return IRECV_E_SUCCESS; | ||
| 748 | } | ||
| 749 | |||
| 750 | irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) { | ||
| 751 | switch(type) { | ||
| 752 | case IRECV_RECEIVED: | ||
| 753 | client->received_callback = NULL; | ||
| 754 | break; | ||
| 755 | |||
| 756 | case IRECV_PROGRESS: | ||
| 757 | client->progress_callback = NULL; | ||
| 758 | |||
| 759 | case IRECV_CONNECTED: | ||
| 760 | client->connected_callback = NULL; | ||
| 761 | |||
| 762 | case IRECV_PRECOMMAND: | ||
| 763 | client->precommand_callback = NULL; | ||
| 764 | break; | ||
| 765 | |||
| 766 | case IRECV_POSTCOMMAND: | ||
| 767 | client->postcommand_callback = NULL; | ||
| 768 | break; | ||
| 769 | |||
| 770 | case IRECV_DISCONNECTED: | ||
| 771 | client->disconnected_callback = NULL; | ||
| 772 | |||
| 773 | default: | ||
| 774 | return IRECV_E_UNKNOWN_ERROR; | ||
| 775 | } | ||
| 776 | |||
| 777 | return IRECV_E_SUCCESS; | ||
| 778 | } | ||
| 779 | |||
| 780 | irecv_error_t irecv_close(irecv_client_t client) { | ||
| 781 | if (client != NULL) { | ||
| 782 | if(client->disconnected_callback != NULL) { | ||
| 783 | irecv_event_t event; | ||
| 784 | event.size = 0; | ||
| 785 | event.data = NULL; | ||
| 786 | event.progress = 0; | ||
| 787 | event.type = IRECV_DISCONNECTED; | ||
| 788 | client->disconnected_callback(client, &event); | ||
| 789 | } | ||
| 790 | #ifndef WIN32 | ||
| 791 | if (client->handle != NULL) { | ||
| 792 | if ((client->mode != kDfuMode) && (client->mode != kWTFMode)) { | ||
| 793 | libusb_release_interface(client->handle, client->interface); | ||
| 794 | } | ||
| 795 | libusb_close(client->handle); | ||
| 796 | client->handle = NULL; | ||
| 797 | } | ||
| 798 | #else | ||
| 799 | if (client->iBootPath!=NULL) { | ||
| 800 | free(client->iBootPath); | ||
| 801 | client->iBootPath = NULL; | ||
| 802 | } | ||
| 803 | if (client->DfuPath!=NULL) { | ||
| 804 | free(client->DfuPath); | ||
| 805 | client->DfuPath = NULL; | ||
| 806 | } | ||
| 807 | mobiledevice_closepipes(client); | ||
| 808 | #endif | ||
| 809 | free(client); | ||
| 810 | client = NULL; | ||
| 811 | } | ||
| 812 | |||
| 813 | return IRECV_E_SUCCESS; | ||
| 814 | } | ||
| 815 | |||
| 816 | void irecv_set_debug_level(int level) { | ||
| 817 | libirecovery_debug = level; | ||
| 818 | #ifndef WIN32 | ||
| 819 | if(libirecovery_context) { | ||
| 820 | libusb_set_debug(libirecovery_context, libirecovery_debug); | ||
| 821 | } | ||
| 822 | #endif | ||
| 823 | } | ||
| 824 | |||
| 825 | static irecv_error_t irecv_send_command_raw(irecv_client_t client, char* command) { | ||
| 826 | unsigned int length = strlen(command); | ||
| 827 | if (length >= 0x100) { | ||
| 828 | length = 0xFF; | ||
| 829 | } | ||
| 830 | |||
| 831 | if (length > 0) { | ||
| 832 | int ret = irecv_control_transfer(client, 0x40, 0, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT); | ||
| 833 | } | ||
| 834 | |||
| 835 | return IRECV_E_SUCCESS; | ||
| 836 | } | ||
| 837 | |||
| 838 | irecv_error_t irecv_send_command(irecv_client_t client, char* command) { | ||
| 839 | irecv_error_t error = 0; | ||
| 840 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 841 | |||
| 842 | unsigned int length = strlen(command); | ||
| 843 | if (length >= 0x100) { | ||
| 844 | length = 0xFF; | ||
| 845 | } | ||
| 846 | |||
| 847 | irecv_event_t event; | ||
| 848 | if(client->precommand_callback != NULL) { | ||
| 849 | event.size = length; | ||
| 850 | event.data = command; | ||
| 851 | event.type = IRECV_PRECOMMAND; | ||
| 852 | if(client->precommand_callback(client, &event)) { | ||
| 853 | return IRECV_E_SUCCESS; | ||
| 854 | } | ||
| 855 | } | ||
| 856 | |||
| 857 | error = irecv_send_command_raw(client, command); | ||
| 858 | if (error != IRECV_E_SUCCESS) { | ||
| 859 | debug("Failed to send command %s\n", command); | ||
| 860 | if (error != IRECV_E_PIPE) | ||
| 861 | return error; | ||
| 862 | } | ||
| 863 | |||
| 864 | if(client->postcommand_callback != NULL) { | ||
| 865 | event.size = length; | ||
| 866 | event.data = command; | ||
| 867 | event.type = IRECV_POSTCOMMAND; | ||
| 868 | if(client->postcommand_callback(client, &event)) { | ||
| 869 | return IRECV_E_SUCCESS; | ||
| 870 | } | ||
| 871 | } | ||
| 872 | |||
| 873 | return IRECV_E_SUCCESS; | ||
| 874 | } | ||
| 875 | |||
| 876 | irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfuNotifyFinished) { | ||
| 877 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 878 | |||
| 879 | FILE* file = fopen(filename, "rb"); | ||
| 880 | if (file == NULL) { | ||
| 881 | return IRECV_E_FILE_NOT_FOUND; | ||
| 882 | } | ||
| 883 | |||
| 884 | fseek(file, 0, SEEK_END); | ||
| 885 | long length = ftell(file); | ||
| 886 | fseek(file, 0, SEEK_SET); | ||
| 887 | |||
| 888 | char* buffer = (char*) malloc(length); | ||
| 889 | if (buffer == NULL) { | ||
| 890 | fclose(file); | ||
| 891 | return IRECV_E_OUT_OF_MEMORY; | ||
| 892 | } | ||
| 893 | |||
| 894 | long bytes = fread(buffer, 1, length, file); | ||
| 895 | fclose(file); | ||
| 896 | |||
| 897 | if (bytes != length) { | ||
| 898 | free(buffer); | ||
| 899 | return IRECV_E_UNKNOWN_ERROR; | ||
| 900 | } | ||
| 901 | |||
| 902 | irecv_error_t error = irecv_send_buffer(client, buffer, length, dfuNotifyFinished); | ||
| 903 | free(buffer); | ||
| 904 | return error; | ||
| 905 | } | ||
| 906 | |||
| 907 | irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { | ||
| 908 | if (check_context(client) != IRECV_E_SUCCESS) { | ||
| 909 | *status = 0; | ||
| 910 | return IRECV_E_NO_DEVICE; | ||
| 911 | } | ||
| 912 | |||
| 913 | unsigned char buffer[6]; | ||
| 914 | memset(buffer, '\0', 6); | ||
| 915 | if (irecv_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, USB_TIMEOUT) != 6) { | ||
| 916 | *status = 0; | ||
| 917 | return IRECV_E_USB_STATUS; | ||
| 918 | } | ||
| 919 | |||
| 920 | *status = (unsigned int) buffer[4]; | ||
| 921 | return IRECV_E_SUCCESS; | ||
| 922 | } | ||
| 923 | |||
| 924 | irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished) { | ||
| 925 | irecv_error_t error = 0; | ||
| 926 | int recovery_mode = ((client->mode != kDfuMode) && (client->mode != kWTFMode)); | ||
| 927 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 928 | |||
| 929 | unsigned int h1 = 0xFFFFFFFF; | ||
| 930 | unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10}; | ||
| 931 | int packet_size = recovery_mode ? 0x8000 : 0x800; | ||
| 932 | int last = length % packet_size; | ||
| 933 | int packets = length / packet_size; | ||
| 934 | if (last != 0) { | ||
| 935 | packets++; | ||
| 936 | } else { | ||
| 937 | last = packet_size; | ||
| 938 | } | ||
| 939 | |||
| 940 | /* initiate transfer */ | ||
| 941 | if (recovery_mode) { | ||
| 942 | error = irecv_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT); | ||
| 943 | } else { | ||
| 944 | char dump[4]; | ||
| 945 | if (irecv_control_transfer(client, 0xa1, 5, 0, 0, dump, 1, USB_TIMEOUT) == 1) { | ||
| 946 | error = IRECV_E_SUCCESS; | ||
| 947 | } else { | ||
| 948 | error = IRECV_E_USB_UPLOAD; | ||
| 949 | } | ||
| 950 | } | ||
| 951 | if (error != IRECV_E_SUCCESS) { | ||
| 952 | return error; | ||
| 953 | } | ||
| 954 | |||
| 955 | int i = 0; | ||
| 956 | double progress = 0; | ||
| 957 | unsigned long count = 0; | ||
| 958 | unsigned int status = 0; | ||
| 959 | int bytes = 0; | ||
| 960 | for (i = 0; i < packets; i++) { | ||
| 961 | int size = (i + 1) < packets ? packet_size : last; | ||
| 962 | |||
| 963 | /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ | ||
| 964 | if (recovery_mode) { | ||
| 965 | error = irecv_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT); | ||
| 966 | } else { | ||
| 967 | int j; | ||
| 968 | for (j = 0; j < size; j++) { | ||
| 969 | dfu_hash_step(h1, buffer[i*packet_size + j]); | ||
| 970 | } | ||
| 971 | if (i+1 == packets) { | ||
| 972 | for (j = 0; j < 2; j++) { | ||
| 973 | dfu_hash_step(h1, dfu_xbuf[j*6 + 0]); | ||
| 974 | dfu_hash_step(h1, dfu_xbuf[j*6 + 1]); | ||
| 975 | dfu_hash_step(h1, dfu_xbuf[j*6 + 2]); | ||
| 976 | dfu_hash_step(h1, dfu_xbuf[j*6 + 3]); | ||
| 977 | dfu_hash_step(h1, dfu_xbuf[j*6 + 4]); | ||
| 978 | dfu_hash_step(h1, dfu_xbuf[j*6 + 5]); | ||
| 979 | } | ||
| 980 | |||
| 981 | char* newbuf = (char*)malloc(size + 16); | ||
| 982 | memcpy(newbuf, &buffer[i * packet_size], size); | ||
| 983 | memcpy(newbuf+size, dfu_xbuf, 12); | ||
| 984 | newbuf[size+12] = h1 & 0xFF; | ||
| 985 | newbuf[size+13] = (h1 >> 8) & 0xFF; | ||
| 986 | newbuf[size+14] = (h1 >> 16) & 0xFF; | ||
| 987 | newbuf[size+15] = (h1 >> 24) & 0xFF; | ||
| 988 | size += 16; | ||
| 989 | bytes = irecv_control_transfer(client, 0x21, 1, i, 0, newbuf, size, USB_TIMEOUT); | ||
| 990 | free(newbuf); | ||
| 991 | } else { | ||
| 992 | bytes = irecv_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT); | ||
| 993 | } | ||
| 994 | } | ||
| 995 | |||
| 996 | if (bytes != size) { | ||
| 997 | return IRECV_E_USB_UPLOAD; | ||
| 998 | } | ||
| 999 | |||
| 1000 | if (!recovery_mode) { | ||
| 1001 | error = irecv_get_status(client, &status); | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | if (error != IRECV_E_SUCCESS) { | ||
| 1005 | return error; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | if (!recovery_mode && status != 5) { | ||
| 1009 | int retry = 0; | ||
| 1010 | while (retry < 20) { | ||
| 1011 | irecv_get_status(client, &status); | ||
| 1012 | if (status == 5) { | ||
| 1013 | break; | ||
| 1014 | } | ||
| 1015 | sleep(1); | ||
| 1016 | } | ||
| 1017 | if (status != 5) { | ||
| 1018 | return IRECV_E_USB_UPLOAD; | ||
| 1019 | } | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | count += size; | ||
| 1023 | if(client->progress_callback != NULL) { | ||
| 1024 | irecv_event_t event; | ||
| 1025 | event.progress = ((double) count/ (double) length) * 100.0; | ||
| 1026 | event.type = IRECV_PROGRESS; | ||
| 1027 | event.data = "Uploading"; | ||
| 1028 | event.size = count; | ||
| 1029 | client->progress_callback(client, &event); | ||
| 1030 | } else { | ||
| 1031 | debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length); | ||
| 1032 | } | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | if (dfuNotifyFinished && !recovery_mode) { | ||
| 1036 | irecv_control_transfer(client, 0x21, 1, packets, 0, (unsigned char*) buffer, 0, USB_TIMEOUT); | ||
| 1037 | |||
| 1038 | for (i = 0; i < 2; i++) { | ||
| 1039 | error = irecv_get_status(client, &status); | ||
| 1040 | if (error != IRECV_E_SUCCESS) { | ||
| 1041 | return error; | ||
| 1042 | } | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | if (dfuNotifyFinished == 2) { | ||
| 1046 | // we send a pseudo ZLP here just in case | ||
| 1047 | irecv_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | irecv_reset(client); | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | return IRECV_E_SUCCESS; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | irecv_error_t irecv_receive(irecv_client_t client) { | ||
| 1057 | char buffer[BUFFER_SIZE]; | ||
| 1058 | memset(buffer, '\0', BUFFER_SIZE); | ||
| 1059 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1060 | |||
| 1061 | int bytes = 0; | ||
| 1062 | while (irecv_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 1000) == 0) { | ||
| 1063 | if (bytes > 0) { | ||
| 1064 | if (client->received_callback != NULL) { | ||
| 1065 | irecv_event_t event; | ||
| 1066 | event.size = bytes; | ||
| 1067 | event.data = buffer; | ||
| 1068 | event.type = IRECV_RECEIVED; | ||
| 1069 | if (client->received_callback(client, &event) != 0) { | ||
| 1070 | return IRECV_E_SUCCESS; | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | } else break; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | return IRECV_E_SUCCESS; | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { | ||
| 1080 | int ret = 0; | ||
| 1081 | char command[256]; | ||
| 1082 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1083 | *value = NULL; | ||
| 1084 | |||
| 1085 | if(variable == NULL) { | ||
| 1086 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | memset(command, '\0', sizeof(command)); | ||
| 1090 | snprintf(command, sizeof(command)-1, "getenv %s", variable); | ||
| 1091 | irecv_error_t error = irecv_send_command_raw(client, command); | ||
| 1092 | if(error == IRECV_E_PIPE) { | ||
| 1093 | return IRECV_E_SUCCESS; | ||
| 1094 | } | ||
| 1095 | if(error != IRECV_E_SUCCESS) { | ||
| 1096 | return error; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | char* response = (char*) malloc(256); | ||
| 1100 | if (response == NULL) { | ||
| 1101 | return IRECV_E_OUT_OF_MEMORY; | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | memset(response, '\0', 256); | ||
| 1105 | ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT); | ||
| 1106 | |||
| 1107 | *value = response; | ||
| 1108 | return IRECV_E_SUCCESS; | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) { | ||
| 1112 | int ret = 0; | ||
| 1113 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1114 | *value = 0; | ||
| 1115 | |||
| 1116 | char* response = (char*) malloc(256); | ||
| 1117 | if (response == NULL) { | ||
| 1118 | return IRECV_E_OUT_OF_MEMORY; | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | memset(response, '\0', 256); | ||
| 1122 | ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT); | ||
| 1123 | |||
| 1124 | *value = (unsigned int) *response; | ||
| 1125 | return IRECV_E_SUCCESS; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) { | ||
| 1129 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1130 | |||
| 1131 | if (client->mode == kWTFMode) { | ||
| 1132 | char s_cpid[8] = {0,}; | ||
| 1133 | strncpy(s_cpid, client->serial, 4); | ||
| 1134 | if (sscanf(s_cpid, "%x", cpid) != 1) { | ||
| 1135 | *cpid = 0; | ||
| 1136 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1137 | } | ||
| 1138 | return IRECV_E_SUCCESS; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | char* cpid_string = strstr(client->serial, "CPID:"); | ||
| 1142 | if (cpid_string == NULL) { | ||
| 1143 | *cpid = 0; | ||
| 1144 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1145 | } | ||
| 1146 | sscanf(cpid_string, "CPID:%x", cpid); | ||
| 1147 | |||
| 1148 | return IRECV_E_SUCCESS; | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) { | ||
| 1152 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1153 | |||
| 1154 | char* bdid_string = strstr(client->serial, "BDID:"); | ||
| 1155 | if (bdid_string == NULL) { | ||
| 1156 | *bdid = 0; | ||
| 1157 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1158 | } | ||
| 1159 | sscanf(bdid_string, "BDID:%x", bdid); | ||
| 1160 | |||
| 1161 | return IRECV_E_SUCCESS; | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) { | ||
| 1165 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1166 | |||
| 1167 | char* ecid_string = strstr(client->serial, "ECID:"); | ||
| 1168 | if (ecid_string == NULL) { | ||
| 1169 | *ecid = 0; | ||
| 1170 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1171 | } | ||
| 1172 | sscanf(ecid_string, "ECID:" _FMT_qX, ecid); | ||
| 1173 | |||
| 1174 | return IRECV_E_SUCCESS; | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | irecv_error_t irecv_get_srnm(irecv_client_t client, unsigned char* srnm) { | ||
| 1178 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1179 | |||
| 1180 | char* srnmp; | ||
| 1181 | char* srnm_string = strstr(client->serial, "SRNM:["); | ||
| 1182 | if(srnm_string == NULL) { | ||
| 1183 | srnm = NULL; | ||
| 1184 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | sscanf(srnm_string, "SRNM:[%s]", srnm); | ||
| 1188 | srnmp = strrchr(srnm, ']'); | ||
| 1189 | if(srnmp != NULL) { | ||
| 1190 | *srnmp = '\0'; | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | return IRECV_E_SUCCESS; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | irecv_error_t irecv_get_imei(irecv_client_t client, unsigned char* imei) { | ||
| 1197 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1198 | |||
| 1199 | char* imeip; | ||
| 1200 | char* imei_string = strstr(client->serial, "IMEI:["); | ||
| 1201 | if (imei_string == NULL) { | ||
| 1202 | *imei = 0; | ||
| 1203 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | |||
| 1207 | sscanf(imei_string, "IMEI:[%s]", imei); | ||
| 1208 | imeip = strrchr(imei, ']'); | ||
| 1209 | if(imeip != NULL) { | ||
| 1210 | *imeip = '\0'; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | return IRECV_E_SUCCESS; | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | irecv_error_t irecv_get_nonce(irecv_client_t client, unsigned char** nonce, int* nonce_size) { | ||
| 1217 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1218 | |||
| 1219 | unsigned char buf[255]; | ||
| 1220 | int len; | ||
| 1221 | |||
| 1222 | *nonce = NULL; | ||
| 1223 | *nonce_size = 0; | ||
| 1224 | |||
| 1225 | len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*) buf, 255); | ||
| 1226 | debug("%s: got length: %d\n", __func__, len); | ||
| 1227 | if (len < 0) { | ||
| 1228 | return len; | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | buf[len] = 0; | ||
| 1232 | debug("%s: buf='%s'\n", __func__, buf); | ||
| 1233 | |||
| 1234 | char* nonce_string = strstr(buf, "NONC:"); | ||
| 1235 | if (nonce_string == NULL) { | ||
| 1236 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1237 | } | ||
| 1238 | nonce_string+=5; | ||
| 1239 | |||
| 1240 | int nlen = (len - ((unsigned char*)nonce_string - &buf[0])) / 2; | ||
| 1241 | unsigned char *nn = malloc(nlen); | ||
| 1242 | if (!nn) { | ||
| 1243 | return IRECV_E_OUT_OF_MEMORY; | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | int i = 0; | ||
| 1247 | for (i = 0; i < nlen; i++) { | ||
| 1248 | int val = 0; | ||
| 1249 | if (sscanf(nonce_string+(i*2), "%02X", &val) == 1) { | ||
| 1250 | nn[i] = (unsigned char)val; | ||
| 1251 | } else { | ||
| 1252 | debug("%s: ERROR: unexpected data in nonce result (%2s)\n", __func__, nonce_string+(i*2)); | ||
| 1253 | break; | ||
| 1254 | } | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | if (i != nlen) { | ||
| 1258 | debug("%s: ERROR: unable to parse nonce\n", __func__); | ||
| 1259 | free(nn); | ||
| 1260 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | *nonce = nn; | ||
| 1264 | *nonce_size = nlen; | ||
| 1265 | |||
| 1266 | return IRECV_E_SUCCESS; | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | irecv_error_t irecv_send_exploit(irecv_client_t client) { | ||
| 1270 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1271 | irecv_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, USB_TIMEOUT); | ||
| 1272 | return IRECV_E_SUCCESS; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) { | ||
| 1276 | irecv_error_t error = IRECV_E_SUCCESS; | ||
| 1277 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1278 | |||
| 1279 | char* file_data = NULL; | ||
| 1280 | unsigned int file_size = 0; | ||
| 1281 | if(irecv_read_file(filename, &file_data, &file_size) < 0) { | ||
| 1282 | return IRECV_E_FILE_NOT_FOUND; | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | char* line = strtok(file_data, "\n"); | ||
| 1286 | while(line != NULL) { | ||
| 1287 | if(line[0] != '#') { | ||
| 1288 | error = irecv_send_command(client, line); | ||
| 1289 | if(error != IRECV_E_SUCCESS) { | ||
| 1290 | return error; | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | error = irecv_receive(client); | ||
| 1294 | if(error != IRECV_E_SUCCESS) { | ||
| 1295 | return error; | ||
| 1296 | } | ||
| 1297 | } | ||
| 1298 | line = strtok(NULL, "\n"); | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | return IRECV_E_SUCCESS; | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | irecv_error_t irecv_saveenv(irecv_client_t client) { | ||
| 1305 | irecv_error_t error = irecv_send_command_raw(client, "saveenv"); | ||
| 1306 | if(error != IRECV_E_SUCCESS) { | ||
| 1307 | return error; | ||
| 1308 | } | ||
| 1309 | return IRECV_E_SUCCESS; | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) { | ||
| 1313 | char command[256]; | ||
| 1314 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1315 | |||
| 1316 | if(variable == NULL || value == NULL) { | ||
| 1317 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | memset(command, '\0', sizeof(command)); | ||
| 1321 | snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value); | ||
| 1322 | irecv_error_t error = irecv_send_command_raw(client, command); | ||
| 1323 | if(error != IRECV_E_SUCCESS) { | ||
| 1324 | return error; | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | return IRECV_E_SUCCESS; | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | const char* irecv_strerror(irecv_error_t error) { | ||
| 1331 | switch (error) { | ||
| 1332 | case IRECV_E_SUCCESS: | ||
| 1333 | return "Command completed successfully"; | ||
| 1334 | |||
| 1335 | case IRECV_E_NO_DEVICE: | ||
| 1336 | return "Unable to find device"; | ||
| 1337 | |||
| 1338 | case IRECV_E_OUT_OF_MEMORY: | ||
| 1339 | return "Out of memory"; | ||
| 1340 | |||
| 1341 | case IRECV_E_UNABLE_TO_CONNECT: | ||
| 1342 | return "Unable to connect to device"; | ||
| 1343 | |||
| 1344 | case IRECV_E_INVALID_INPUT: | ||
| 1345 | return "Invalid input"; | ||
| 1346 | |||
| 1347 | case IRECV_E_FILE_NOT_FOUND: | ||
| 1348 | return "File not found"; | ||
| 1349 | |||
| 1350 | case IRECV_E_USB_UPLOAD: | ||
| 1351 | return "Unable to upload data to device"; | ||
| 1352 | |||
| 1353 | case IRECV_E_USB_STATUS: | ||
| 1354 | return "Unable to get device status"; | ||
| 1355 | |||
| 1356 | case IRECV_E_USB_INTERFACE: | ||
| 1357 | return "Unable to set device interface"; | ||
| 1358 | |||
| 1359 | case IRECV_E_USB_CONFIGURATION: | ||
| 1360 | return "Unable to set device configuration"; | ||
| 1361 | |||
| 1362 | case IRECV_E_PIPE: | ||
| 1363 | return "Broken pipe"; | ||
| 1364 | |||
| 1365 | case IRECV_E_TIMEOUT: | ||
| 1366 | return "Timeout talking to device"; | ||
| 1367 | |||
| 1368 | default: | ||
| 1369 | return "Unknown error"; | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | return NULL; | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | int irecv_write_file(const char* filename, const void* data, size_t size) { | ||
| 1376 | size_t bytes = 0; | ||
| 1377 | FILE* file = NULL; | ||
| 1378 | |||
| 1379 | debug("Writing data to %s\n", filename); | ||
| 1380 | file = fopen(filename, "wb"); | ||
| 1381 | if (file == NULL) { | ||
| 1382 | //error("read_file: Unable to open file %s\n", filename); | ||
| 1383 | return -1; | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | bytes = fwrite(data, 1, size, file); | ||
| 1387 | fclose(file); | ||
| 1388 | |||
| 1389 | if (bytes != size) { | ||
| 1390 | //error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size); | ||
| 1391 | return -1; | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | return size; | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | int irecv_read_file(const char* filename, char** data, uint32_t* size) { | ||
| 1398 | size_t bytes = 0; | ||
| 1399 | size_t length = 0; | ||
| 1400 | FILE* file = NULL; | ||
| 1401 | char* buffer = NULL; | ||
| 1402 | debug("Reading data from %s\n", filename); | ||
| 1403 | |||
| 1404 | *size = 0; | ||
| 1405 | *data = NULL; | ||
| 1406 | |||
| 1407 | file = fopen(filename, "rb"); | ||
| 1408 | if (file == NULL) { | ||
| 1409 | //error("read_file: File %s not found\n", filename); | ||
| 1410 | return -1; | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | fseek(file, 0, SEEK_END); | ||
| 1414 | length = ftell(file); | ||
| 1415 | rewind(file); | ||
| 1416 | |||
| 1417 | buffer = (char*) malloc(length); | ||
| 1418 | if(buffer == NULL) { | ||
| 1419 | //error("ERROR: Out of memory\n"); | ||
| 1420 | fclose(file); | ||
| 1421 | return -1; | ||
| 1422 | } | ||
| 1423 | bytes = fread(buffer, 1, length, file); | ||
| 1424 | fclose(file); | ||
| 1425 | |||
| 1426 | if(bytes != length) { | ||
| 1427 | //error("ERROR: Unable to read entire file\n"); | ||
| 1428 | free(buffer); | ||
| 1429 | return -1; | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | *size = length; | ||
| 1433 | *data = buffer; | ||
| 1434 | return 0; | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | irecv_error_t irecv_reset_counters(irecv_client_t client) { | ||
| 1438 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1439 | if ((client->mode == kDfuMode) || (client->mode == kWTFMode)) { | ||
| 1440 | irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, USB_TIMEOUT); | ||
| 1441 | } | ||
| 1442 | return IRECV_E_SUCCESS; | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length) { | ||
| 1446 | irecv_error_t error = 0; | ||
| 1447 | int recovery_mode = ((client->mode != kDfuMode) && (client->mode != kWTFMode)); | ||
| 1448 | |||
| 1449 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1450 | |||
| 1451 | int packet_size = recovery_mode ? 0x2000: 0x800; | ||
| 1452 | int last = length % packet_size; | ||
| 1453 | int packets = length / packet_size; | ||
| 1454 | if (last != 0) { | ||
| 1455 | packets++; | ||
| 1456 | } else { | ||
| 1457 | last = packet_size; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | int i = 0; | ||
| 1461 | int bytes = 0; | ||
| 1462 | double progress = 0; | ||
| 1463 | unsigned long count = 0; | ||
| 1464 | unsigned int status = 0; | ||
| 1465 | for (i = 0; i < packets; i++) { | ||
| 1466 | unsigned short size = (i+1) < packets ? packet_size : last; | ||
| 1467 | bytes = irecv_control_transfer(client, 0xA1, 2, 0, 0, &buffer[i * packet_size], size, USB_TIMEOUT); | ||
| 1468 | |||
| 1469 | if (bytes != size) { | ||
| 1470 | return IRECV_E_USB_UPLOAD; | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | count += size; | ||
| 1474 | if(client->progress_callback != NULL) { | ||
| 1475 | irecv_event_t event; | ||
| 1476 | event.progress = ((double) count/ (double) length) * 100.0; | ||
| 1477 | event.type = IRECV_PROGRESS; | ||
| 1478 | event.data = "Downloading"; | ||
| 1479 | event.size = count; | ||
| 1480 | client->progress_callback(client, &event); | ||
| 1481 | } else { | ||
| 1482 | debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length); | ||
| 1483 | } | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | return IRECV_E_SUCCESS; | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | irecv_error_t irecv_finish_transfer(irecv_client_t client) { | ||
| 1490 | int i = 0; | ||
| 1491 | unsigned int status = 0; | ||
| 1492 | |||
| 1493 | if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; | ||
| 1494 | |||
| 1495 | irecv_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT); | ||
| 1496 | |||
| 1497 | for(i = 0; i < 3; i++){ | ||
| 1498 | irecv_get_status(client, &status); | ||
| 1499 | } | ||
| 1500 | irecv_reset(client); | ||
| 1501 | return IRECV_E_SUCCESS; | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | irecv_error_t irecv_get_device(irecv_client_t client, irecv_device_t* device) { | ||
| 1505 | int device_id = DEVICE_UNKNOWN; | ||
| 1506 | uint32_t bdid = 0; | ||
| 1507 | uint32_t cpid = 0; | ||
| 1508 | |||
| 1509 | if (irecv_get_cpid(client, &cpid) < 0) { | ||
| 1510 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | switch (cpid) { | ||
| 1514 | case CPID_IPHONE2G: | ||
| 1515 | // iPhone1,1 iPhone1,2 and iPod1,1 all share the same ChipID | ||
| 1516 | // so we need to check the BoardID | ||
| 1517 | if (irecv_get_bdid(client, &bdid) < 0) { | ||
| 1518 | break; | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | switch (bdid) { | ||
| 1522 | case BDID_IPHONE2G: | ||
| 1523 | device_id = DEVICE_IPHONE2G; | ||
| 1524 | break; | ||
| 1525 | |||
| 1526 | case BDID_IPHONE3G: | ||
| 1527 | device_id = DEVICE_IPHONE3G; | ||
| 1528 | break; | ||
| 1529 | |||
| 1530 | case BDID_IPOD1G: | ||
| 1531 | device_id = DEVICE_IPOD1G; | ||
| 1532 | break; | ||
| 1533 | |||
| 1534 | default: | ||
| 1535 | device_id = DEVICE_UNKNOWN; | ||
| 1536 | break; | ||
| 1537 | } | ||
| 1538 | break; | ||
| 1539 | |||
| 1540 | case CPID_IPHONE3GS: | ||
| 1541 | device_id = DEVICE_IPHONE3GS; | ||
| 1542 | break; | ||
| 1543 | |||
| 1544 | case CPID_IPOD2G: | ||
| 1545 | device_id = DEVICE_IPOD2G; | ||
| 1546 | break; | ||
| 1547 | |||
| 1548 | case CPID_IPOD3G: | ||
| 1549 | device_id = DEVICE_IPOD3G; | ||
| 1550 | break; | ||
| 1551 | |||
| 1552 | case CPID_IPAD1G: | ||
| 1553 | // iPhone3,1 iPhone3,3 iPad4,1 and iPad1,1 all share the same ChipID | ||
| 1554 | // so we need to check the BoardID | ||
| 1555 | if (irecv_get_bdid(client, &bdid) < 0) { | ||
| 1556 | break; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | switch (bdid) { | ||
| 1560 | case BDID_IPAD1G: | ||
| 1561 | device_id = DEVICE_IPAD1G; | ||
| 1562 | break; | ||
| 1563 | |||
| 1564 | case BDID_IPHONE4: | ||
| 1565 | device_id = DEVICE_IPHONE4; | ||
| 1566 | break; | ||
| 1567 | |||
| 1568 | case BDID_IPOD4G: | ||
| 1569 | device_id = DEVICE_IPOD4G; | ||
| 1570 | break; | ||
| 1571 | |||
| 1572 | case BDID_APPLETV2: | ||
| 1573 | device_id = DEVICE_APPLETV2; | ||
| 1574 | break; | ||
| 1575 | |||
| 1576 | case BDID_IPHONE42: | ||
| 1577 | device_id = DEVICE_IPHONE42; | ||
| 1578 | break; | ||
| 1579 | |||
| 1580 | default: | ||
| 1581 | device_id = DEVICE_UNKNOWN; | ||
| 1582 | break; | ||
| 1583 | } | ||
| 1584 | break; | ||
| 1585 | |||
| 1586 | case CPID_IPAD21: | ||
| 1587 | // iPad2,1 iPad2,2 iPad2,3 and iPhone4,1 share the same ChipID, so we need to check the BoardID | ||
| 1588 | if (irecv_get_bdid(client, &bdid) < 0) { | ||
| 1589 | break; | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | switch (bdid) { | ||
| 1593 | case BDID_IPAD21: | ||
| 1594 | device_id = DEVICE_IPAD21; | ||
| 1595 | break; | ||
| 1596 | |||
| 1597 | case BDID_IPAD22: | ||
| 1598 | device_id = DEVICE_IPAD22; | ||
| 1599 | break; | ||
| 1600 | |||
| 1601 | case BDID_IPAD23: | ||
| 1602 | device_id = DEVICE_IPAD23; | ||
| 1603 | break; | ||
| 1604 | |||
| 1605 | case BDID_IPHONE4S: | ||
| 1606 | device_id = DEVICE_IPHONE4S; | ||
| 1607 | break; | ||
| 1608 | |||
| 1609 | default: | ||
| 1610 | device_id = DEVICE_UNKNOWN; | ||
| 1611 | break; | ||
| 1612 | } | ||
| 1613 | break; | ||
| 1614 | |||
| 1615 | default: | ||
| 1616 | device_id = DEVICE_UNKNOWN; | ||
| 1617 | break; | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | *device = &irecv_devices[device_id]; | ||
| 1621 | return IRECV_E_SUCCESS; | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause) { | ||
| 1625 | irecv_error_t error = 0; | ||
| 1626 | irecv_client_t new_client = NULL; | ||
| 1627 | irecv_event_cb_t progress_callback = client->progress_callback; | ||
| 1628 | |||
| 1629 | unsigned long long ecid = 0; | ||
| 1630 | irecv_get_ecid(client, &ecid); | ||
| 1631 | |||
| 1632 | if (check_context(client) == IRECV_E_SUCCESS) { | ||
| 1633 | irecv_close(client); | ||
| 1634 | } | ||
| 1635 | |||
| 1636 | if (initial_pause > 0) { | ||
| 1637 | debug("Waiting %d seconds for the device to pop up...\n", initial_pause); | ||
| 1638 | sleep(initial_pause); | ||
| 1639 | } | ||
| 1640 | |||
| 1641 | error = irecv_open_attempts(&new_client, ecid, 10); | ||
| 1642 | if(error != IRECV_E_SUCCESS) { | ||
| 1643 | return NULL; | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | new_client->progress_callback = progress_callback; | ||
| 1647 | return new_client; | ||
| 1648 | } | ||
| 1649 | |||
| 1650 | void irecv_hexdump(unsigned char* buf, unsigned int len, unsigned int addr) { | ||
| 1651 | int i, j; | ||
| 1652 | printf("0x%08x: ", addr); | ||
| 1653 | for (i = 0; i < len; i++) { | ||
| 1654 | if (i % 16 == 0 && i != 0) { | ||
| 1655 | for (j=i-16; j < i; j++) { | ||
| 1656 | unsigned char car = buf[j]; | ||
| 1657 | if (car < 0x20 || car > 0x7f) car = '.'; | ||
| 1658 | printf("%c", car); | ||
| 1659 | } | ||
| 1660 | printf("\n"); | ||
| 1661 | addr += 0x10; | ||
| 1662 | printf("0x%08x: ", addr); | ||
| 1663 | } | ||
| 1664 | printf("%02x ", buf[i]); | ||
| 1665 | } | ||
| 1666 | |||
| 1667 | int done = (i % 16); | ||
| 1668 | int remains = 16 - done; | ||
| 1669 | if (done > 0) { | ||
| 1670 | for (j = 0; j < remains; j++) { | ||
| 1671 | printf(" "); | ||
| 1672 | } | ||
| 1673 | } | ||
| 1674 | |||
| 1675 | if ((i - done) >= 0) { | ||
| 1676 | if (done == 0 && i > 0) done = 16; | ||
| 1677 | for (j = (i - done); j < i; j++) { | ||
| 1678 | unsigned char car = buf[j]; | ||
| 1679 | if (car < 0x20 || car > 0x7f) car = '.'; | ||
| 1680 | printf("%c", car); | ||
| 1681 | } | ||
| 1682 | } | ||
| 1683 | printf("\n"); | ||
| 1684 | } | ||
