summaryrefslogtreecommitdiffstats
path: root/libirecovery.c
diff options
context:
space:
mode:
Diffstat (limited to 'libirecovery.c')
-rw-r--r--libirecovery.c1234
1 files changed, 1234 insertions, 0 deletions
diff --git a/libirecovery.c b/libirecovery.c
new file mode 100644
index 0000000..f3d6f28
--- /dev/null
+++ b/libirecovery.c
@@ -0,0 +1,1234 @@
1/**
2 * GreenPois0n iRecovery - libirecovery.c
3 * Copyright (C) 2010 Chronic-Dev Team
4 * Copyright (C) 2010 Joshua Hill
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#ifndef WIN32
26#include <libusb-1.0/libusb.h>
27#else
28#define WIN32_LEAN_AND_MEAN
29#include <windows.h>
30#include <setupapi.h>
31#endif
32
33#include "libirecovery.h"
34
35#define BUFFER_SIZE 0x1000
36#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__)
37
38static int libirecovery_debug = 0;
39#ifndef WIN32
40static libusb_context* libirecovery_context = NULL;
41#endif
42
43int irecv_write_file(const char* filename, const void* data, size_t size);
44int irecv_read_file(const char* filename, char** data, uint32_t* size);
45
46#ifdef WIN32
47static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
48static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
49
50typedef struct usb_control_request {
51 uint8_t bmRequestType;
52 uint8_t bRequest;
53 uint16_t wValue;
54 uint16_t wIndex;
55 uint16_t wLength;
56
57 char data[];
58} usb_control_request;
59
60irecv_error_t mobiledevice_openpipes(irecv_client_t client);
61void mobiledevice_closepipes(irecv_client_t client);
62
63irecv_error_t mobiledevice_connect(irecv_client_t* client) {
64 irecv_error_t ret;
65
66 SP_DEVICE_INTERFACE_DATA currentInterface;
67 HDEVINFO usbDevices;
68 DWORD i;
69 LPSTR path;
70 irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client));
71 memset(_client, 0, sizeof(struct irecv_client));
72
73 // Get DFU paths
74 usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DFU, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
75 if(!usbDevices) {
76 return IRECV_E_UNABLE_TO_CONNECT;
77 }
78 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
79 for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_DFU, i, &currentInterface); i++) {
80 DWORD requiredSize = 0;
81 PSP_DEVICE_INTERFACE_DETAIL_DATA details;
82 SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
83 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize);
84 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
85 if(!SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, details, requiredSize, NULL, NULL)) {
86 irecv_close(_client);
87 free(details);
88 SetupDiDestroyDeviceInfoList(usbDevices);
89 return IRECV_E_UNABLE_TO_CONNECT;
90 } else {
91 LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD));
92 memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD));
93 free(details);
94 path = (LPSTR) malloc(requiredSize - sizeof(DWORD));
95 memcpy((void*) path, (void*) result, requiredSize - sizeof(DWORD));
96 TCHAR* pathEnd = strstr(path, "#{");
97 *pathEnd = '\0';
98 _client->DfuPath = result;
99 break;
100 }
101 }
102 SetupDiDestroyDeviceInfoList(usbDevices);
103 // Get iBoot path
104 usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IBOOT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
105 if(!usbDevices) {
106 irecv_close(_client);
107 return IRECV_E_UNABLE_TO_CONNECT;
108 }
109 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
110 for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_IBOOT, i, &currentInterface); i++) {
111 DWORD requiredSize = 0;
112 PSP_DEVICE_INTERFACE_DETAIL_DATA details;
113 SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
114 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize);
115 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
116 if(!SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, details, requiredSize, NULL, NULL)) {
117 irecv_close(_client);
118 free(details);
119 SetupDiDestroyDeviceInfoList(usbDevices);
120 return IRECV_E_UNABLE_TO_CONNECT;
121 } else {
122 LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD));
123 memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD));
124 free(details);
125
126 if(strstr(result, path) == NULL) {
127 free(result);
128 continue;
129 }
130
131 _client->iBootPath = result;
132 break;
133 }
134 }
135 SetupDiDestroyDeviceInfoList(usbDevices);
136 free(path);
137
138 ret = mobiledevice_openpipes(_client);
139 if (ret != IRECV_E_SUCCESS) return ret;
140
141 *client = _client;
142 return IRECV_E_SUCCESS;
143}
144
145irecv_error_t mobiledevice_openpipes(irecv_client_t client) {
146 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))) {
147 irecv_close(client);
148 return IRECV_E_UNABLE_TO_CONNECT;
149 }
150 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))) {
151 irecv_close(client);
152 return IRECV_E_UNABLE_TO_CONNECT;
153 }
154
155 if (client->iBootPath == NULL) {
156 client->mode = kDfuMode;
157 client->handle = client->hDFU;
158 } else {
159 client->mode = kRecoveryMode2;
160 client->handle = client->hIB;
161 }
162
163 return IRECV_E_SUCCESS;
164}
165
166void mobiledevice_closepipes(irecv_client_t client) {
167 if (client->hDFU!=NULL) {
168 CloseHandle(client->hDFU);
169 client->hDFU = NULL;
170 }
171 if (client->hIB!=NULL) {
172 CloseHandle(client->hIB);
173 client->hIB = NULL;
174 }
175}
176#endif
177
178int check_context(irecv_client_t client) {
179 if (client == NULL || client->handle == NULL) {
180 return IRECV_E_NO_DEVICE;
181 }
182
183 return IRECV_E_SUCCESS;
184}
185
186void irecv_init() {
187#ifndef WIN32
188 libusb_init(&libirecovery_context);
189#endif
190}
191
192void irecv_exit() {
193#ifndef WIN32
194 if (libirecovery_context != NULL) {
195 libusb_exit(libirecovery_context);
196 libirecovery_context = NULL;
197 }
198#endif
199}
200
201#ifdef __APPLE__
202 void dummy_callback() { }
203#endif
204
205int irecv_control_transfer( irecv_client_t client,
206 uint8_t bmRequestType,
207 uint8_t bRequest,
208 uint16_t wValue,
209 uint16_t wIndex,
210 unsigned char *data,
211 uint16_t wLength,
212 unsigned int timeout) {
213#ifndef WIN32
214 return libusb_control_transfer(client->handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
215#else
216 DWORD count = 0;
217 DWORD ret;
218 BOOL bRet;
219 OVERLAPPED overlapped;
220
221 if (data == NULL) wLength = 0;
222
223 usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + wLength);
224 packet->bmRequestType = bmRequestType;
225 packet->bRequest = bRequest;
226 packet->wValue = wValue;
227 packet->wIndex = wIndex;
228 packet->wLength = wLength;
229
230 if (bmRequestType < 0x80 && wLength > 0) {
231 memcpy(packet->data, data, wLength);
232 }
233
234 memset(&overlapped, 0, sizeof(overlapped));
235 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
236 DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + wLength, packet, sizeof(usb_control_request) + wLength, NULL, &overlapped);
237 ret = WaitForSingleObject(overlapped.hEvent, timeout);
238 bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE);
239 CloseHandle(overlapped.hEvent);
240 if (!bRet) {
241 CancelIo(client->handle);
242 free(packet);
243 return -1;
244 }
245
246 count -= sizeof(usb_control_request);
247 if (count > 0) {
248 if (bmRequestType >= 0x80) {
249 memcpy(data, packet->data, count);
250 }
251 }
252 free(packet);
253 return count;
254#endif
255}
256
257int irecv_bulk_transfer(irecv_client_t client,
258 unsigned char endpoint,
259 unsigned char *data,
260 int length,
261 int *transferred,
262 unsigned int timeout) {
263#ifndef WIN32
264 return libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout);
265#else
266 int ret;
267 if (endpoint==0x4) {
268 ret = DeviceIoControl(client->handle, 0x220195, data, length, data, length, (PDWORD) transferred, NULL);
269 } else {
270 ret = 0;
271 }
272 return ret==0?-1:0;
273#endif
274}
275
276int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) {
277#ifndef WIN32
278 return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size);
279#else
280 irecv_error_t ret;
281 unsigned short langid = 0;
282 unsigned char data[255];
283 int di, si;
284 memset(data, 0, sizeof(data));
285 memset(buffer, 0, size);
286
287 ret = irecv_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data), 1000);
288
289 if (ret < 0) return ret;
290 if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR;
291 if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR;
292
293 for (di = 0, si = 2; si < data[0]; si += 2) {
294 if (di >= (size - 1)) break;
295 if (data[si + 1]) {
296 /* high byte */
297 buffer[di++] = '?';
298 } else {
299 buffer[di++] = data[si];
300 }
301 }
302 buffer[di] = 0;
303
304 return di;
305#endif
306}
307
308irecv_error_t irecv_open(irecv_client_t* pclient) {
309#ifndef WIN32
310 int i = 0;
311 struct libusb_device* usb_device = NULL;
312 struct libusb_device** usb_device_list = NULL;
313 struct libusb_device_handle* usb_handle = NULL;
314 struct libusb_device_descriptor usb_descriptor;
315
316 *pclient = NULL;
317 if(libirecovery_debug) {
318 irecv_set_debug_level(libirecovery_debug);
319 }
320
321 irecv_error_t error = IRECV_E_SUCCESS;
322 int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list);
323 for (i = 0; i < usb_device_count; i++) {
324 usb_device = usb_device_list[i];
325 libusb_get_device_descriptor(usb_device, &usb_descriptor);
326 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) {
327 /* verify this device is in a mode we understand */
328 if (usb_descriptor.idProduct == kRecoveryMode1 ||
329 usb_descriptor.idProduct == kRecoveryMode2 ||
330 usb_descriptor.idProduct == kRecoveryMode3 ||
331 usb_descriptor.idProduct == kRecoveryMode4 ||
332 usb_descriptor.idProduct == kDfuMode) {
333
334 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct);
335
336 libusb_open(usb_device, &usb_handle);
337 if (usb_handle == NULL) {
338 libusb_free_device_list(usb_device_list, 1);
339 libusb_close(usb_handle);
340 libusb_exit(libirecovery_context);
341 return IRECV_E_UNABLE_TO_CONNECT;
342 }
343 libusb_free_device_list(usb_device_list, 1);
344
345 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client));
346 if (client == NULL) {
347 libusb_close(usb_handle);
348 libusb_exit(libirecovery_context);
349 return IRECV_E_OUT_OF_MEMORY;
350 }
351
352 memset(client, '\0', sizeof(struct irecv_client));
353 client->interface = 0;
354 client->handle = usb_handle;
355 client->mode = usb_descriptor.idProduct;
356 if (client->mode != kDfuMode) {
357 error = irecv_set_configuration(client, 1);
358 if (error != IRECV_E_SUCCESS) {
359 return error;
360 }
361
362 error = irecv_set_interface(client, 0, 0);
363 if (error != IRECV_E_SUCCESS) {
364 return error;
365 }
366 }
367
368 /* cache usb serial */
369 irecv_get_string_descriptor_ascii(client, usb_descriptor.iSerialNumber, (unsigned char*) client->serial, 255);
370
371 *pclient = client;
372 return IRECV_E_SUCCESS;
373 }
374 }
375 }
376
377 return IRECV_E_UNABLE_TO_CONNECT;
378#else
379 int ret = mobiledevice_connect(pclient);
380 if (ret == IRECV_E_SUCCESS) {
381 irecv_get_string_descriptor_ascii(*pclient, 3, (unsigned char*) (*pclient)->serial, 255);
382 }
383 return ret;
384#endif
385}
386
387irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) {
388 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
389
390#ifndef WIN32
391 debug("Setting to configuration %d\n", configuration);
392
393 int current = 0;
394 libusb_get_configuration(client->handle, &current);
395 if (current != configuration) {
396 if (libusb_set_configuration(client->handle, configuration) < 0) {
397 return IRECV_E_USB_CONFIGURATION;
398 }
399 }
400
401 client->config = configuration;
402#endif
403
404 return IRECV_E_SUCCESS;
405}
406
407irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) {
408 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
409
410#ifndef WIN32
411 libusb_release_interface(client->handle, client->interface);
412
413 debug("Setting to interface %d:%d\n", interface, alt_interface);
414 if (libusb_claim_interface(client->handle, interface) < 0) {
415 return IRECV_E_USB_INTERFACE;
416 }
417
418 if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) {
419 return IRECV_E_USB_INTERFACE;
420 }
421
422 client->interface = interface;
423 client->alt_interface = alt_interface;
424#endif
425
426 return IRECV_E_SUCCESS;
427}
428
429irecv_error_t irecv_reset(irecv_client_t client) {
430 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
431
432#ifndef WIN32
433 libusb_reset_device(client->handle);
434#else
435 int ret;
436 DWORD count;
437 ret = DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL);
438#endif
439
440 return IRECV_E_SUCCESS;
441}
442
443irecv_error_t irecv_open_attempts(irecv_client_t* pclient, int attempts) {
444 int i;
445
446 for (i = 0; i < attempts; i++) {
447 if (irecv_open(pclient) != IRECV_E_SUCCESS) {
448 debug("Connection failed. Waiting 1 sec before retry.\n");
449 sleep(1);
450 } else {
451 return IRECV_E_SUCCESS;
452 }
453 }
454
455 return IRECV_E_UNABLE_TO_CONNECT;
456}
457
458irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) {
459 switch(type) {
460 case IRECV_RECEIVED:
461 client->received_callback = callback;
462 break;
463
464 case IRECV_PROGRESS:
465 client->progress_callback = callback;
466
467 case IRECV_CONNECTED:
468 client->connected_callback = callback;
469
470 case IRECV_PRECOMMAND:
471 client->precommand_callback = callback;
472 break;
473
474 case IRECV_POSTCOMMAND:
475 client->postcommand_callback = callback;
476 break;
477
478 case IRECV_DISCONNECTED:
479 client->disconnected_callback = callback;
480
481 default:
482 return IRECV_E_UNKNOWN_ERROR;
483 }
484
485 return IRECV_E_SUCCESS;
486}
487
488irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) {
489 switch(type) {
490 case IRECV_RECEIVED:
491 client->received_callback = NULL;
492 break;
493
494 case IRECV_PROGRESS:
495 client->progress_callback = NULL;
496
497 case IRECV_CONNECTED:
498 client->connected_callback = NULL;
499
500 case IRECV_PRECOMMAND:
501 client->precommand_callback = NULL;
502 break;
503
504 case IRECV_POSTCOMMAND:
505 client->postcommand_callback = NULL;
506 break;
507
508 case IRECV_DISCONNECTED:
509 client->disconnected_callback = NULL;
510
511 default:
512 return IRECV_E_UNKNOWN_ERROR;
513 }
514
515 return IRECV_E_SUCCESS;
516}
517
518irecv_error_t irecv_close(irecv_client_t client) {
519 if (client != NULL) {
520 if(client->disconnected_callback != NULL) {
521 irecv_event_t event;
522 event.size = 0;
523 event.data = NULL;
524 event.progress = 0;
525 event.type = IRECV_DISCONNECTED;
526 client->disconnected_callback(client, &event);
527 }
528#ifndef WIN32
529 if (client->handle != NULL) {
530 if (client->mode != kDfuMode) {
531 libusb_release_interface(client->handle, client->interface);
532 }
533 libusb_close(client->handle);
534 client->handle = NULL;
535 }
536#else
537 if (client->iBootPath!=NULL) {
538 free(client->iBootPath);
539 client->iBootPath = NULL;
540 }
541 if (client->DfuPath!=NULL) {
542 free(client->DfuPath);
543 client->DfuPath = NULL;
544 }
545 mobiledevice_closepipes(client);
546#endif
547 free(client);
548 client = NULL;
549 }
550
551 return IRECV_E_SUCCESS;
552}
553
554void irecv_set_debug_level(int level) {
555 libirecovery_debug = level;
556#ifndef WIN32
557 if(libirecovery_context) {
558 libusb_set_debug(libirecovery_context, libirecovery_debug);
559 }
560#endif
561}
562
563static irecv_error_t irecv_send_command_raw(irecv_client_t client, char* command) {
564 unsigned int length = strlen(command);
565 if (length >= 0x100) {
566 length = 0xFF;
567 }
568
569 if (length > 0) {
570 int ret = irecv_control_transfer(client, 0x40, 0, 0, 0, (unsigned char*) command, length + 1, 1000);
571 }
572
573 return IRECV_E_SUCCESS;
574}
575
576irecv_error_t irecv_send_command(irecv_client_t client, char* command) {
577 irecv_error_t error = 0;
578 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
579
580 unsigned int length = strlen(command);
581 if (length >= 0x100) {
582 length = 0xFF;
583 }
584
585 irecv_event_t event;
586 if(client->precommand_callback != NULL) {
587 event.size = length;
588 event.data = command;
589 event.type = IRECV_PRECOMMAND;
590 if(client->precommand_callback(client, &event)) {
591 return IRECV_E_SUCCESS;
592 }
593 }
594
595 error = irecv_send_command_raw(client, command);
596 if (error != IRECV_E_SUCCESS) {
597 debug("Failed to send command %s\n", command);
598 if (error != IRECV_E_PIPE)
599 return error;
600 }
601
602 if(client->postcommand_callback != NULL) {
603 event.size = length;
604 event.data = command;
605 event.type = IRECV_POSTCOMMAND;
606 if(client->postcommand_callback(client, &event)) {
607 return IRECV_E_SUCCESS;
608 }
609 }
610
611 return IRECV_E_SUCCESS;
612}
613
614irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfuNotifyFinished) {
615 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
616
617 FILE* file = fopen(filename, "rb");
618 if (file == NULL) {
619 return IRECV_E_FILE_NOT_FOUND;
620 }
621
622 fseek(file, 0, SEEK_END);
623 long length = ftell(file);
624 fseek(file, 0, SEEK_SET);
625
626 char* buffer = (char*) malloc(length);
627 if (buffer == NULL) {
628 fclose(file);
629 return IRECV_E_OUT_OF_MEMORY;
630 }
631
632 long bytes = fread(buffer, 1, length, file);
633 fclose(file);
634
635 if (bytes != length) {
636 free(buffer);
637 return IRECV_E_UNKNOWN_ERROR;
638 }
639
640 irecv_error_t error = irecv_send_buffer(client, buffer, length, dfuNotifyFinished);
641 free(buffer);
642 return error;
643}
644
645irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) {
646 if (check_context(client) != IRECV_E_SUCCESS) {
647 *status = 0;
648 return IRECV_E_NO_DEVICE;
649 }
650
651 unsigned char buffer[6];
652 memset(buffer, '\0', 6);
653 if (irecv_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) {
654 *status = 0;
655 return IRECV_E_USB_STATUS;
656 }
657
658 *status = (unsigned int) buffer[4];
659 return IRECV_E_SUCCESS;
660}
661
662irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished) {
663 irecv_error_t error = 0;
664 int recovery_mode = (client->mode != kDfuMode);
665 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
666
667 int packet_size = 0x800;
668 int last = length % packet_size;
669 int packets = length / packet_size;
670 if (last != 0) {
671 packets++;
672 } else {
673 last = packet_size;
674 }
675
676 /* initiate transfer */
677 if (recovery_mode) {
678 error = irecv_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, 1000);
679 if (error != IRECV_E_SUCCESS) {
680 return error;
681 }
682 }
683
684 int i = 0;
685 double progress = 0;
686 unsigned long count = 0;
687 unsigned int status = 0;
688 int bytes = 0;
689 for (i = 0; i < packets; i++) {
690 int size = (i + 1) < packets ? packet_size : last;
691
692 /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */
693 if (recovery_mode) {
694 error = irecv_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, 1000);
695 } else {
696 bytes = irecv_control_transfer(client, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000);
697 }
698
699 if (bytes != size) {
700 return IRECV_E_USB_UPLOAD;
701 }
702
703 if (!recovery_mode) {
704 error = irecv_get_status(client, &status);
705 }
706
707 if (error != IRECV_E_SUCCESS) {
708 return error;
709 }
710
711 if (!recovery_mode && status != 5) {
712 return IRECV_E_USB_UPLOAD;
713 }
714
715 count += size;
716 if(client->progress_callback != NULL) {
717 irecv_event_t event;
718 event.progress = ((double) count/ (double) length) * 100.0;
719 event.type = IRECV_PROGRESS;
720 event.data = "Uploading";
721 event.size = count;
722 client->progress_callback(client, &event);
723 } else {
724 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
725 }
726 }
727
728 if (dfuNotifyFinished && !recovery_mode) {
729 irecv_control_transfer(client, 0x21, 1, 0, 0, (unsigned char*) buffer, 0, 1000);
730
731 for (i = 0; i < 3; i++) {
732 error = irecv_get_status(client, &status);
733 if (error != IRECV_E_SUCCESS) {
734 return error;
735 }
736 }
737 irecv_reset(client);
738 }
739
740 return IRECV_E_SUCCESS;
741}
742
743irecv_error_t irecv_receive(irecv_client_t client) {
744 char buffer[BUFFER_SIZE];
745 memset(buffer, '\0', BUFFER_SIZE);
746 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
747
748 int bytes = 0;
749
750 while (irecv_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 1000) == 0) {
751 if (bytes > 0) {
752 if (client->received_callback != NULL) {
753 irecv_event_t event;
754 event.size = bytes;
755 event.data = buffer;
756 event.type = IRECV_RECEIVED;
757 if (client->received_callback(client, &event) != 0) {
758 return IRECV_E_SUCCESS;
759 }
760 }
761 } else break;
762 }
763
764 return IRECV_E_SUCCESS;
765}
766
767irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) {
768 int ret = 0;
769 char command[256];
770 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
771 *value = NULL;
772
773 if(variable == NULL) {
774 return IRECV_E_UNKNOWN_ERROR;
775 }
776
777 memset(command, '\0', sizeof(command));
778 snprintf(command, sizeof(command)-1, "getenv %s", variable);
779 irecv_error_t error = irecv_send_command_raw(client, command);
780 if(error == IRECV_E_PIPE) {
781 return IRECV_E_SUCCESS;
782 }
783 if(error != IRECV_E_SUCCESS) {
784 return error;
785 }
786
787 char* response = (char*) malloc(256);
788 if (response == NULL) {
789 return IRECV_E_OUT_OF_MEMORY;
790 }
791
792 memset(response, '\0', 256);
793 ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000);
794
795 *value = response;
796 return IRECV_E_SUCCESS;
797}
798
799irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) {
800 int ret = 0;
801 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
802 *value = NULL;
803
804 char* response = (char*) malloc(256);
805 if (response == NULL) {
806 return IRECV_E_OUT_OF_MEMORY;
807 }
808
809 memset(response, '\0', 256);
810 ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000);
811
812 *value = response;
813 return IRECV_E_SUCCESS;
814}
815
816irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) {
817 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
818
819 char* cpid_string = strstr(client->serial, "CPID:");
820 if (cpid_string == NULL) {
821 *cpid = 0;
822 return IRECV_E_UNKNOWN_ERROR;
823 }
824 sscanf(cpid_string, "CPID:%d", cpid);
825
826 return IRECV_E_SUCCESS;
827}
828
829irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) {
830 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
831
832 char* bdid_string = strstr(client->serial, "BDID:");
833 if (bdid_string == NULL) {
834 *bdid = 0;
835 return IRECV_E_UNKNOWN_ERROR;
836 }
837 sscanf(bdid_string, "BDID:%d", bdid);
838
839 return IRECV_E_SUCCESS;
840}
841
842irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) {
843 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
844
845 char* ecid_string = strstr(client->serial, "ECID:");
846 if (ecid_string == NULL) {
847 *ecid = 0;
848 return IRECV_E_UNKNOWN_ERROR;
849 }
850 sscanf(ecid_string, "ECID:%qX", ecid);
851
852 return IRECV_E_SUCCESS;
853}
854
855irecv_error_t irecv_send_exploit(irecv_client_t client) {
856 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
857 irecv_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, 1000);
858 return IRECV_E_SUCCESS;
859}
860
861irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) {
862 irecv_error_t error = IRECV_E_SUCCESS;
863 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
864
865 char* file_data = NULL;
866 unsigned int file_size = 0;
867 if(irecv_read_file(filename, &file_data, &file_size) < 0) {
868 return IRECV_E_FILE_NOT_FOUND;
869 }
870
871 char* line = strtok(file_data, "\n");
872 while(line != NULL) {
873 if(line[0] != '#') {
874 error = irecv_send_command(client, line);
875 if(error != IRECV_E_SUCCESS) {
876 return error;
877 }
878
879 error = irecv_receive(client);
880 if(error != IRECV_E_SUCCESS) {
881 return error;
882 }
883 }
884 line = strtok(NULL, "\n");
885 }
886
887 return IRECV_E_SUCCESS;
888}
889
890irecv_error_t irecv_saveenv(irecv_client_t client) {
891 irecv_error_t error = irecv_send_command_raw(client, "saveenv");
892 if(error != IRECV_E_SUCCESS) {
893 return error;
894 }
895 return IRECV_E_SUCCESS;
896}
897
898irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) {
899 char command[256];
900 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
901
902 if(variable == NULL || value == NULL) {
903 return IRECV_E_UNKNOWN_ERROR;
904 }
905
906 memset(command, '\0', sizeof(command));
907 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value);
908 irecv_error_t error = irecv_send_command_raw(client, command);
909 if(error != IRECV_E_SUCCESS) {
910 return error;
911 }
912
913 return IRECV_E_SUCCESS;
914}
915
916const char* irecv_strerror(irecv_error_t error) {
917 switch (error) {
918 case IRECV_E_SUCCESS:
919 return "Command completed successfully";
920
921 case IRECV_E_NO_DEVICE:
922 return "Unable to find device";
923
924 case IRECV_E_OUT_OF_MEMORY:
925 return "Out of memory";
926
927 case IRECV_E_UNABLE_TO_CONNECT:
928 return "Unable to connect to device";
929
930 case IRECV_E_INVALID_INPUT:
931 return "Invalid input";
932
933 case IRECV_E_FILE_NOT_FOUND:
934 return "File not found";
935
936 case IRECV_E_USB_UPLOAD:
937 return "Unable to upload data to device";
938
939 case IRECV_E_USB_STATUS:
940 return "Unable to get device status";
941
942 case IRECV_E_USB_INTERFACE:
943 return "Unable to set device interface";
944
945 case IRECV_E_USB_CONFIGURATION:
946 return "Unable to set device configuration";
947
948 case IRECV_E_PIPE:
949 return "Broken pipe";
950
951 case IRECV_E_TIMEOUT:
952 return "Timeout talking to device";
953
954 default:
955 return "Unknown error";
956 }
957
958 return NULL;
959}
960
961int irecv_write_file(const char* filename, const void* data, size_t size) {
962 size_t bytes = 0;
963 FILE* file = NULL;
964
965 debug("Writing data to %s\n", filename);
966 file = fopen(filename, "wb");
967 if (file == NULL) {
968 //error("read_file: Unable to open file %s\n", filename);
969 return -1;
970 }
971
972 bytes = fwrite(data, 1, size, file);
973 fclose(file);
974
975 if (bytes != size) {
976 //error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size);
977 return -1;
978 }
979
980 return size;
981}
982
983int irecv_read_file(const char* filename, char** data, uint32_t* size) {
984 size_t bytes = 0;
985 size_t length = 0;
986 FILE* file = NULL;
987 char* buffer = NULL;
988 debug("Reading data from %s\n", filename);
989
990 *size = 0;
991 *data = NULL;
992
993 file = fopen(filename, "rb");
994 if (file == NULL) {
995 //error("read_file: File %s not found\n", filename);
996 return -1;
997 }
998
999 fseek(file, 0, SEEK_END);
1000 length = ftell(file);
1001 rewind(file);
1002
1003 buffer = (char*) malloc(length);
1004 if(buffer == NULL) {
1005 //error("ERROR: Out of memory\n");
1006 fclose(file);
1007 return -1;
1008 }
1009 bytes = fread(buffer, 1, length, file);
1010 fclose(file);
1011
1012 if(bytes != length) {
1013 //error("ERROR: Unable to read entire file\n");
1014 free(buffer);
1015 return -1;
1016 }
1017
1018 *size = length;
1019 *data = buffer;
1020 return 0;
1021}
1022
1023irecv_error_t irecv_reset_counters(irecv_client_t client) {
1024 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
1025 if (client->mode == kDfuMode) {
1026 irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, 1000);
1027 }
1028 return IRECV_E_SUCCESS;
1029}
1030
1031irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length) {
1032 irecv_error_t error = 0;
1033 int recovery_mode = (client->mode != kDfuMode);
1034
1035 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
1036
1037 int packet_size = recovery_mode ? 0x2000: 0x800;
1038 int last = length % packet_size;
1039 int packets = length / packet_size;
1040 if (last != 0) {
1041 packets++;
1042 } else {
1043 last = packet_size;
1044 }
1045
1046 int i = 0;
1047 int bytes = 0;
1048 double progress = 0;
1049 unsigned long count = 0;
1050 unsigned int status = 0;
1051 for (i = 0; i < packets; i++) {
1052 unsigned short size = (i+1) < packets ? packet_size : last;
1053 bytes = irecv_control_transfer(client, 0xA1, 2, 0, 0, &buffer[i * packet_size], size, 1000);
1054
1055 if (bytes != size) {
1056 return IRECV_E_USB_UPLOAD;
1057 }
1058
1059 count += size;
1060 if(client->progress_callback != NULL) {
1061 irecv_event_t event;
1062 event.progress = ((double) count/ (double) length) * 100.0;
1063 event.type = IRECV_PROGRESS;
1064 event.data = "Downloading";
1065 event.size = count;
1066 client->progress_callback(client, &event);
1067 } else {
1068 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
1069 }
1070 }
1071
1072 return IRECV_E_SUCCESS;
1073}
1074
1075irecv_error_t irecv_finish_transfer(irecv_client_t client) {
1076 int i = 0;
1077 unsigned int status = 0;
1078
1079 if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
1080
1081 irecv_control_transfer(client, 0x21, 1, 0, 0, 0, 0, 1000);
1082
1083 for(i = 0; i < 3; i++){
1084 irecv_get_status(client, &status);
1085 }
1086 irecv_reset(client);
1087 return IRECV_E_SUCCESS;
1088}
1089
1090irecv_error_t irecv_get_device(irecv_client_t client, irecv_device_t* device) {
1091 int device_id = DEVICE_UNKNOWN;
1092 uint32_t bdid = 0;
1093 uint32_t cpid = 0;
1094
1095 if (irecv_get_cpid(client, &cpid) < 0) {
1096 return IRECV_E_UNKNOWN_ERROR;
1097 }
1098
1099 switch (cpid) {
1100 case CPID_IPHONE2G:
1101 // iPhone1,1 iPhone1,2 and iPod1,1 all share the same ChipID
1102 // so we need to check the BoardID
1103 if (irecv_get_bdid(client, &bdid) < 0) {
1104 break;
1105 }
1106
1107 switch (bdid) {
1108 case BDID_IPHONE2G:
1109 device_id = DEVICE_IPHONE2G;
1110 break;
1111
1112 case BDID_IPHONE3G:
1113 device_id = DEVICE_IPHONE3G;
1114 break;
1115
1116 case BDID_IPOD1G:
1117 device_id = DEVICE_IPOD1G;
1118 break;
1119
1120 default:
1121 device_id = DEVICE_UNKNOWN;
1122 break;
1123 }
1124 break;
1125
1126 case CPID_IPHONE3GS:
1127 device_id = DEVICE_IPHONE3GS;
1128 break;
1129
1130 case CPID_IPOD2G:
1131 device_id = DEVICE_IPOD2G;
1132 break;
1133
1134 case CPID_IPOD3G:
1135 device_id = DEVICE_IPOD3G;
1136 break;
1137
1138 case CPID_IPAD1G:
1139 // iPhone3,1 iPad4,1 and iPad1,1 all share the same ChipID
1140 // so we need to check the BoardID
1141 if (irecv_get_bdid(client, &bdid) < 0) {
1142 break;
1143 }
1144
1145 switch (bdid) {
1146 case BDID_IPAD1G:
1147 device_id = DEVICE_IPAD1G;
1148 break;
1149
1150 case BDID_IPHONE4:
1151 device_id = DEVICE_IPHONE4;
1152 break;
1153
1154 case BDID_IPOD4G:
1155 device_id = DEVICE_IPOD4G;
1156 break;
1157
1158 case BDID_APPLETV2:
1159 device_id = DEVICE_APPLETV2;
1160 break;
1161
1162 default:
1163 device_id = DEVICE_UNKNOWN;
1164 break;
1165 }
1166 break;
1167
1168 default:
1169 device_id = DEVICE_UNKNOWN;
1170 break;
1171 }
1172
1173 *device = &irecv_devices[device_id];
1174 return IRECV_E_SUCCESS;
1175}
1176
1177irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause) {
1178 irecv_error_t error = 0;
1179 irecv_client_t new_client = NULL;
1180 irecv_event_cb_t progress_callback = client->progress_callback;
1181
1182 if (check_context(client) == IRECV_E_SUCCESS) {
1183 irecv_close(client);
1184 }
1185
1186 if (initial_pause > 0) {
1187 debug("Waiting %d seconds for the device to pop up...\n", initial_pause);
1188 sleep(initial_pause);
1189 }
1190
1191 error = irecv_open_attempts(&new_client, 10);
1192 if(error != IRECV_E_SUCCESS) {
1193 return NULL;
1194 }
1195
1196 new_client->progress_callback = progress_callback;
1197 return new_client;
1198}
1199
1200void irecv_hexdump(unsigned char* buf, unsigned int len, unsigned int addr) {
1201 int i, j;
1202 printf("0x%08x: ", addr);
1203 for (i = 0; i < len; i++) {
1204 if (i % 16 == 0 && i != 0) {
1205 for (j=i-16; j < i; j++) {
1206 unsigned char car = buf[j];
1207 if (car < 0x20 || car > 0x7f) car = '.';
1208 printf("%c", car);
1209 }
1210 printf("\n");
1211 addr += 0x10;
1212 printf("0x%08x: ", addr);
1213 }
1214 printf("%02x ", buf[i]);
1215 }
1216
1217 int done = (i % 16);
1218 int remains = 16 - done;
1219 if (done > 0) {
1220 for (j = 0; j < remains; j++) {
1221 printf(" ");
1222 }
1223 }
1224
1225 if ((i - done) >= 0) {
1226 if (done == 0 && i > 0) done = 16;
1227 for (j = (i - done); j < i; j++) {
1228 unsigned char car = buf[j];
1229 if (car < 0x20 || car > 0x7f) car = '.';
1230 printf("%c", car);
1231 }
1232 }
1233 printf("\n");
1234}