summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/irecovery.c397
-rw-r--r--src/libirecovery.c1684
2 files changed, 2081 insertions, 0 deletions
diff --git a/src/irecovery.c b/src/irecovery.c
new file mode 100644
index 0000000..89f30a6
--- /dev/null
+++ b/src/irecovery.c
@@ -0,0 +1,397 @@
1/**
2 * GreenPois0n iRecovery - irecovery.c
3 * Copyright (C) 2010-2011 Chronic-Dev Team
4 * Copyright (C) 2010-2011 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 <stdlib.h>
23#include <unistd.h>
24#include <string.h>
25#include <libirecovery.h>
26#include <readline/readline.h>
27#include <readline/history.h>
28
29#define FILE_HISTORY_PATH ".irecovery"
30#define debug(...) if(verbose) fprintf(stderr, __VA_ARGS__)
31
32enum {
33 kResetDevice, kStartShell, kSendCommand, kSendFile, kSendExploit, kSendScript
34};
35
36static unsigned int quit = 0;
37static unsigned int verbose = 0;
38
39void print_progress_bar(double progress);
40int received_cb(irecv_client_t client, const irecv_event_t* event);
41int progress_cb(irecv_client_t client, const irecv_event_t* event);
42int precommand_cb(irecv_client_t client, const irecv_event_t* event);
43int postcommand_cb(irecv_client_t client, const irecv_event_t* event);
44
45void shell_usage() {
46 printf("Usage:\n");
47 printf("\t/upload <file>\tSend file to client.\n");
48 printf("\t/exploit [file]\tSend usb exploit with optional payload\n");
49 printf("\t/deviceinfo\tShow device information (ECID, IMEI, etc.)\n");
50 printf("\t/help\t\tShow this help.\n");
51 printf("\t/exit\t\tExit interactive shell.\n");
52}
53
54void parse_command(irecv_client_t client, unsigned char* command, unsigned int size) {
55 char* cmd = strdup(command);
56 char* action = strtok(cmd, " ");
57 debug("Executing %s\n", action);
58 if (!strcmp(cmd, "/exit")) {
59 quit = 1;
60 } else
61
62 if (!strcmp(cmd, "/help")) {
63 shell_usage();
64 } else
65
66 if (!strcmp(cmd, "/upload")) {
67 char* filename = strtok(NULL, " ");
68 debug("Uploading files %s\n", filename);
69 if (filename != NULL) {
70 irecv_send_file(client, filename, 0);
71 }
72 } else
73
74 if (!strcmp(cmd, "/deviceinfo")) {
75 int ret;
76 unsigned int cpid, bdid;
77 unsigned long long ecid;
78 unsigned char srnm[12], imei[15], bt[15];
79
80 ret = irecv_get_cpid(client, &cpid);
81 if(ret == IRECV_E_SUCCESS) {
82 printf("CPID: %d\n", cpid);
83 }
84
85 ret = irecv_get_bdid(client, &bdid);
86 if(ret == IRECV_E_SUCCESS) {
87 printf("BDID: %d\n", bdid);
88 }
89
90 ret = irecv_get_ecid(client, &ecid);
91 if(ret == IRECV_E_SUCCESS) {
92 printf("ECID: %lld\n", ecid);
93 }
94
95 ret = irecv_get_srnm(client, srnm);
96 if(ret == IRECV_E_SUCCESS) {
97 printf("SRNM: %s\n", srnm);
98 }
99
100 ret = irecv_get_imei(client, imei);
101 if(ret == IRECV_E_SUCCESS) {
102 printf("IMEI: %s\n", imei);
103 }
104 } else
105
106 if (!strcmp(cmd, "/exploit")) {
107 char* filename = strtok(NULL, " ");
108 debug("Sending exploit %s\n", filename);
109 if (filename != NULL) {
110 irecv_send_file(client, filename, 0);
111 }
112 irecv_send_exploit(client);
113 } else
114
115 if (!strcmp(cmd, "/execute")) {
116 char* filename = strtok(NULL, " ");
117 debug("Executing script %s\n", filename);
118 if (filename != NULL) {
119 irecv_execute_script(client, filename);
120 }
121 }
122
123
124 free(action);
125}
126
127void load_command_history() {
128 read_history(FILE_HISTORY_PATH);
129}
130
131void append_command_to_history(char* cmd) {
132 add_history(cmd);
133 write_history(FILE_HISTORY_PATH);
134}
135
136void init_shell(irecv_client_t client) {
137 irecv_error_t error = 0;
138 load_command_history();
139 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
140 irecv_event_subscribe(client, IRECV_RECEIVED, &received_cb, NULL);
141 irecv_event_subscribe(client, IRECV_PRECOMMAND, &precommand_cb, NULL);
142 irecv_event_subscribe(client, IRECV_POSTCOMMAND, &postcommand_cb, NULL);
143 while (!quit) {
144 error = irecv_receive(client);
145
146 if (error != IRECV_E_SUCCESS) {
147 debug("%s\n", irecv_strerror(error));
148 break;
149 }
150
151 char* cmd = readline("> ");
152 if (cmd && *cmd) {
153 error = irecv_send_command(client, cmd);
154 if (error != IRECV_E_SUCCESS) {
155 quit = 1;
156 }
157
158 append_command_to_history(cmd);
159 free(cmd);
160 }
161 }
162}
163
164int received_cb(irecv_client_t client, const irecv_event_t* event) {
165 if (event->type == IRECV_RECEIVED) {
166 int i = 0;
167 int size = event->size;
168 char* data = event->data;
169 for (i = 0; i < size; i++) {
170 printf("%c", data[i]);
171 }
172 }
173 return 0;
174}
175
176int precommand_cb(irecv_client_t client, const irecv_event_t* event) {
177 if (event->type == IRECV_PRECOMMAND) {
178 irecv_error_t error = 0;
179 if (event->data[0] == '/') {
180 parse_command(client, event->data, event->size);
181 return -1;
182 }
183 }
184 return 0;
185}
186
187int postcommand_cb(irecv_client_t client, const irecv_event_t* event) {
188 char* value = NULL;
189 char* action = NULL;
190 char* command = NULL;
191 char* argument = NULL;
192 irecv_error_t error = IRECV_E_SUCCESS;
193
194 if (event->type == IRECV_POSTCOMMAND) {
195 command = strdup(event->data);
196 action = strtok(command, " ");
197 if (!strcmp(action, "getenv")) {
198 argument = strtok(NULL, " ");
199 error = irecv_getenv(client, argument, &value);
200 if (error != IRECV_E_SUCCESS) {
201 debug("%s\n", irecv_strerror(error));
202 free(command);
203 return error;
204 }
205 printf("%s\n", value);
206 free(value);
207 }
208
209 if (!strcmp(action, "reboot")) {
210 quit = 1;
211 }
212 }
213
214 if (command) free(command);
215 return 0;
216}
217
218int progress_cb(irecv_client_t client, const irecv_event_t* event) {
219 if (event->type == IRECV_PROGRESS) {
220 print_progress_bar(event->progress);
221 }
222 return 0;
223}
224
225void print_progress_bar(double progress) {
226 int i = 0;
227 if(progress < 0) {
228 return;
229 }
230
231 if(progress > 100) {
232 progress = 100;
233 }
234
235 printf("\r[");
236 for(i = 0; i < 50; i++) {
237 if(i < progress / 2) {
238 printf("=");
239 } else {
240 printf(" ");
241 }
242 }
243
244 printf("] %3.1f%%", progress);
245 fflush(stdout);
246 if(progress == 100) {
247 printf("\n");
248 }
249}
250
251void print_usage() {
252 printf("iRecovery - iDevice Recovery Utility\n");
253 printf("Usage: irecovery [args]\n");
254 printf("\t-i <ecid>\tTarget specific device by its hexadecimal ECID\n");
255 printf("\t-v\t\tStart irecovery in verbose mode.\n");
256 printf("\t-c <cmd>\tSend command to client.\n");
257 printf("\t-f <file>\tSend file to client.\n");
258 printf("\t-k [payload]\tSend usb exploit to client.\n");
259 printf("\t-h\t\tShow this help.\n");
260 printf("\t-r\t\tReset client.\n");
261 printf("\t-s\t\tStart interactive shell.\n");
262 printf("\t-e <script>\tExecutes recovery shell script.\n");
263 exit(1);
264}
265
266int main(int argc, char* argv[]) {
267 int i = 0;
268 int opt = 0;
269 int action = 0;
270 unsigned long long ecid = 0;
271 char* argument = NULL;
272 irecv_error_t error = 0;
273 if (argc == 1) print_usage();
274 while ((opt = getopt(argc, argv, "i:vhrsc:f:e:k::")) > 0) {
275 switch (opt) {
276 case 'i':
277 if (optarg) {
278 char* tail = NULL;
279 ecid = strtoull(optarg, &tail, 16);
280 if (tail && (tail[0] != '\0')) {
281 ecid = 0;
282 }
283 if (ecid == 0) {
284 fprintf(stderr, "ERROR: Could not parse ECID from argument '%s'\n", optarg);
285 return -1;
286 }
287 }
288 break;
289
290 case 'v':
291 verbose += 1;
292 break;
293
294 case 'h':
295 print_usage();
296 break;
297
298 case 'r':
299 action = kResetDevice;
300 break;
301
302 case 's':
303 action = kStartShell;
304 break;
305
306 case 'f':
307 action = kSendFile;
308 argument = optarg;
309 break;
310
311 case 'c':
312 action = kSendCommand;
313 argument = optarg;
314 break;
315
316 case 'k':
317 action = kSendExploit;
318 argument = optarg;
319 break;
320
321 case 'e':
322 action = kSendScript;
323 argument = optarg;
324 break;
325
326 default:
327 fprintf(stderr, "Unknown argument\n");
328 return -1;
329 }
330 }
331
332 if (verbose) irecv_set_debug_level(verbose);
333
334 irecv_init();
335 irecv_client_t client = NULL;
336 for (i = 0; i <= 5; i++) {
337 debug("Attempting to connect... \n");
338
339 if (irecv_open(&client, ecid) != IRECV_E_SUCCESS)
340 sleep(1);
341 else
342 break;
343
344 if (i == 5) {
345 return -1;
346 }
347 }
348
349 switch (action) {
350 case kResetDevice:
351 irecv_reset(client);
352 break;
353
354 case kSendFile:
355 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
356 error = irecv_send_file(client, argument, 1);
357 debug("%s\n", irecv_strerror(error));
358 break;
359
360 case kSendCommand:
361 error = irecv_send_command(client, argument);
362 debug("%s\n", irecv_strerror(error));
363 break;
364
365 case kSendExploit:
366 if (argument != NULL) {
367 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
368 error = irecv_send_file(client, argument, 0);
369 if (error != IRECV_E_SUCCESS) {
370 debug("%s\n", irecv_strerror(error));
371 break;
372 }
373 }
374 error = irecv_send_exploit(client);
375 debug("%s\n", irecv_strerror(error));
376 break;
377
378 case kStartShell:
379 init_shell(client);
380 break;
381
382 case kSendScript:
383 error = irecv_execute_script(client, argument);
384 if(error != IRECV_E_SUCCESS) {
385 debug("%s\n", irecv_strerror(error));
386 }
387 break;
388
389 default:
390 fprintf(stderr, "Unknown action\n");
391 break;
392 }
393
394 irecv_close(client);
395 return 0;
396}
397
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
46static int libirecovery_debug = 0;
47#ifndef WIN32
48static libusb_context* libirecovery_context = NULL;
49#endif
50
51int irecv_write_file(const char* filename, const void* data, size_t size);
52int irecv_read_file(const char* filename, char** data, uint32_t* size);
53
54static 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
125static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
126static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
127
128typedef 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
138int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size);
139
140irecv_error_t mobiledevice_openpipes(irecv_client_t client);
141void mobiledevice_closepipes(irecv_client_t client);
142
143irecv_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(&currentInterface, '\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, &currentInterface); 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, &currentInterface, 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, &currentInterface, 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(&currentInterface, '\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, &currentInterface); 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, &currentInterface, 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, &currentInterface, 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
329irecv_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
360void 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
372int 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
380void irecv_init() {
381#ifndef WIN32
382 libusb_init(&libirecovery_context);
383#endif
384}
385
386void 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
399int 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
451int 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
476int 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
508irecv_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
641irecv_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, &current);
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
661irecv_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
687irecv_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
701irecv_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
720irecv_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
750irecv_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
780irecv_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
816void 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
825static 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
838irecv_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
876irecv_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
907irecv_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
924irecv_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
1056irecv_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
1079irecv_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
1111irecv_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
1128irecv_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
1151irecv_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
1164irecv_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
1177irecv_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
1196irecv_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
1216irecv_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
1269irecv_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
1275irecv_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
1304irecv_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
1312irecv_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
1330const 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
1375int 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
1397int 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
1437irecv_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
1445irecv_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
1489irecv_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
1504irecv_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
1624irecv_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
1650void 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}