summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am24
-rw-r--r--src/irecovery.c355
-rw-r--r--src/libirecovery-1.0.pc.in11
-rw-r--r--src/libirecovery.c4132
4 files changed, 3906 insertions, 616 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..f80ffa7
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,24 @@
1AM_CPPFLAGS = -I$(top_srcdir)/include
2
3AM_CFLAGS = \
4 $(GLOBAL_CFLAGS) \
5 $(LFS_CFLAGS) \
6 $(limd_glue_CFLAGS) \
7 $(libusb_CFLAGS)
8
9AM_LDFLAGS = \
10 $(GLOBAL_LDFLAGS) \
11 $(limd_glue_LIBS) \
12 $(libusb_LIBS)
13
14lib_LTLIBRARIES = libirecovery-1.0.la
15libirecovery_1_0_la_CFLAGS = $(AM_CFLAGS)
16libirecovery_1_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIRECOVERY_SO_VERSION) -no-undefined
17libirecovery_1_0_la_SOURCES = libirecovery.c
18
19if WIN32
20libirecovery_1_0_la_LDFLAGS += -avoid-version
21endif
22
23pkgconfigdir = $(libdir)/pkgconfig
24pkgconfig_DATA = libirecovery-1.0.pc
diff --git a/src/irecovery.c b/src/irecovery.c
deleted file mode 100644
index 5a59cd0..0000000
--- a/src/irecovery.c
+++ /dev/null
@@ -1,355 +0,0 @@
1/**
2 * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode
3 * Copyright (C) 2008 - 2009 westbaer
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 **/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <libirecovery.h>
23#include <readline/readline.h>
24#include <readline/history.h>
25
26#define FILE_HISTORY_PATH ".irecovery"
27#define debug(...) if(verbose) fprintf(stderr, __VA_ARGS__)
28
29enum {
30 kResetDevice, kStartShell, kSendCommand, kSendFile, kSendExploit, kSendScript
31};
32
33static unsigned int quit = 0;
34static unsigned int verbose = 0;
35
36void print_progress_bar(double progress);
37int received_cb(irecv_client_t client, const irecv_event_t* event);
38int progress_cb(irecv_client_t client, const irecv_event_t* event);
39int precommand_cb(irecv_client_t client, const irecv_event_t* event);
40int postcommand_cb(irecv_client_t client, const irecv_event_t* event);
41
42void shell_usage() {
43 printf("Usage:\n");
44 printf("\t/upload <file>\tSend file to client.\n");
45 printf("\t/exploit [file]\tSend usb exploit with optional payload\n");
46 printf("\t/help\t\tShow this help.\n");
47 printf("\t/exit\t\tExit interactive shell.\n");
48}
49
50void parse_command(irecv_client_t client, unsigned char* command, unsigned int size) {
51 irecv_error_t error = 0;
52 char* cmd = strdup(command);
53 char* action = strtok(cmd, " ");
54 debug("Executing %s\n", action);
55 if (!strcmp(cmd, "/exit")) {
56 quit = 1;
57 } else
58
59 if (!strcmp(cmd, "/help")) {
60 shell_usage();
61 } else
62
63 if (!strcmp(cmd, "/upload")) {
64 char* filename = strtok(NULL, " ");
65 debug("Uploading files %s\n", filename);
66 if (filename != NULL) {
67 error = irecv_send_file(client, filename);
68 debug("%s\n", irecv_strerror(error));
69 }
70 } else
71
72 if (!strcmp(cmd, "/exploit")) {
73 char* filename = strtok(NULL, " ");
74 debug("Sending exploit %s\n", filename);
75 if (filename != NULL) {
76 error = irecv_send_file(client, filename);
77 debug("%s\n", irecv_strerror(error));
78 }
79 irecv_send_exploit(client);
80 } else
81
82 if (!strcmp(cmd, "/execute")) {
83 char* filename = strtok(NULL, " ");
84 debug("Executing script %s\n", filename);
85 if (filename != NULL) {
86 irecv_execute_script(client, filename);
87 }
88 }
89
90
91 free(action);
92}
93
94void load_command_history() {
95 read_history(FILE_HISTORY_PATH);
96}
97
98void append_command_to_history(char* cmd) {
99 add_history(cmd);
100 write_history(FILE_HISTORY_PATH);
101}
102
103void init_shell(irecv_client_t client) {
104 irecv_error_t error = 0;
105 load_command_history();
106 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
107 irecv_event_subscribe(client, IRECV_RECEIVED, &received_cb, NULL);
108 irecv_event_subscribe(client, IRECV_PRECOMMAND, &precommand_cb, NULL);
109 irecv_event_subscribe(client, IRECV_POSTCOMMAND, &postcommand_cb, NULL);
110
111 error = irecv_receive(client);
112 if (error != IRECV_E_SUCCESS) {
113 debug("%s\n", irecv_strerror(error));
114 }
115
116 while (!quit) {
117 char* cmd = readline("> ");
118 if (cmd && *cmd) {
119 error = irecv_send_command(client, cmd);
120 if (error != IRECV_E_SUCCESS) {
121 quit = 1;
122 }
123
124 append_command_to_history(cmd);
125 free(cmd);
126 }
127 }
128}
129
130int received_cb(irecv_client_t client, const irecv_event_t* event) {
131 if (event->type == IRECV_RECEIVED) {
132 int i = 0;
133 int size = event->size;
134 char* data = event->data;
135 for (i = 0; i < size; i++) {
136 printf("%c", data[i]);
137 }
138 }
139 return 0;
140}
141
142int precommand_cb(irecv_client_t client, const irecv_event_t* event) {
143 char* value = NULL;
144 char* action = NULL;
145 char* command = NULL;
146 char* argument = NULL;
147 irecv_error_t error = IRECV_E_SUCCESS;
148
149 if (event->type == IRECV_PRECOMMAND) {
150 if (event->data[0] == '/') {
151 parse_command(client, event->data, event->size);
152 return -1;
153 }
154
155 command = strdup(event->data);
156 action = strtok(command, " ");
157
158 if (!strcmp(action, "getenv")) {
159 argument = strtok(NULL, " ");
160 error = irecv_getenv(client, argument, &value);
161 if (error != IRECV_E_SUCCESS) {
162 debug("%s\n", irecv_strerror(error));
163 free(command);
164 return error;
165 }
166 if (value) {
167 printf("%s\n", value);
168 free(value);
169 }
170 return 1;
171 }
172 }
173 return 0;
174}
175
176int postcommand_cb(irecv_client_t client, const irecv_event_t* event) {
177 char* action = NULL;
178 char* command = NULL;
179
180 if (event->type == IRECV_POSTCOMMAND) {
181 command = strdup(event->data);
182 action = strtok(command, " ");
183
184 if (!strcmp(action, "reboot")) {
185 quit = 1;
186 }
187 }
188
189 if (command) free(command);
190 return 0;
191}
192
193int progress_cb(irecv_client_t client, const irecv_event_t* event) {
194 if (event->type == IRECV_PROGRESS) {
195 print_progress_bar(event->progress);
196 }
197 return 0;
198}
199
200void print_progress_bar(double progress) {
201 int i = 0;
202 if(progress < 0) {
203 return;
204 }
205
206 if(progress > 100) {
207 progress = 100;
208 }
209
210 printf("\r[");
211 for(i = 0; i < 50; i++) {
212 if(i < progress / 2) {
213 printf("=");
214 } else {
215 printf(" ");
216 }
217 }
218
219 printf("] %3.1f%%", progress);
220 fflush(stdout);
221 if(progress == 100) {
222 printf("\n");
223 }
224}
225
226void print_usage() {
227 printf("iRecovery - iDevice Recovery Utility\n");
228 printf("Usage: ./irecovery [args]\n");
229 printf("\t-v\t\tStart irecovery in verbose mode.\n");
230 printf("\t-c <cmd>\tSend command to client.\n");
231 printf("\t-f <file>\tSend file to client.\n");
232 printf("\t-k [payload]\tSend usb exploit to client.\n");
233 printf("\t-h\t\tShow this help.\n");
234 printf("\t-r\t\tReset client.\n");
235 printf("\t-s\t\tStart interactive shell.\n");
236 printf("\t-e <script>\tExecutes recovery shell script.\n");
237 exit(1);
238}
239
240int main(int argc, char** argv) {
241 int i = 0;
242 int opt = 0;
243 int action = 0;
244 char* argument = NULL;
245 irecv_error_t error = 0;
246 if (argc == 1) print_usage();
247 while ((opt = getopt(argc, argv, "vhrsc:f:e:k::")) > 0) {
248 switch (opt) {
249 case 'v':
250 verbose += 1;
251 break;
252
253 case 'h':
254 print_usage();
255 break;
256
257 case 'r':
258 action = kResetDevice;
259 break;
260
261 case 's':
262 action = kStartShell;
263 break;
264
265 case 'f':
266 action = kSendFile;
267 argument = optarg;
268 break;
269
270 case 'c':
271 action = kSendCommand;
272 argument = optarg;
273 break;
274
275 case 'k':
276 action = kSendExploit;
277 argument = optarg;
278 break;
279
280 case 'e':
281 action = kSendScript;
282 argument = optarg;
283 break;
284
285 default:
286 fprintf(stderr, "Unknown argument\n");
287 return -1;
288 }
289 }
290
291 if (verbose) irecv_set_debug_level(verbose);
292
293 irecv_client_t client = NULL;
294 for (i = 0; i <= 5; i++) {
295 debug("Attempting to connect... \n");
296
297 if (irecv_open(&client) != IRECV_E_SUCCESS)
298 sleep(1);
299 else
300 break;
301
302 if (i == 5) {
303 return -1;
304 }
305 }
306
307 switch (action) {
308 case kResetDevice:
309 irecv_reset(client);
310 break;
311
312 case kSendFile:
313 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
314 error = irecv_send_file(client, argument);
315 debug("%s\n", irecv_strerror(error));
316 break;
317
318 case kSendCommand:
319 error = irecv_send_command(client, argument);
320 debug("%s\n", irecv_strerror(error));
321 break;
322
323 case kSendExploit:
324 if (argument != NULL) {
325 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
326 error = irecv_send_file(client, argument);
327 if (error != IRECV_E_SUCCESS) {
328 debug("%s\n", irecv_strerror(error));
329 break;
330 }
331 }
332 error = irecv_send_exploit(client);
333 debug("%s\n", irecv_strerror(error));
334 break;
335
336 case kStartShell:
337 init_shell(client);
338 break;
339
340 case kSendScript:
341 error = irecv_execute_script(client, argument);
342 if(error != IRECV_E_SUCCESS) {
343 debug("%s\n", irecv_strerror(error));
344 }
345 break;
346
347 default:
348 fprintf(stderr, "Unknown action\n");
349 break;
350 }
351
352 irecv_close(client);
353 return 0;
354}
355
diff --git a/src/libirecovery-1.0.pc.in b/src/libirecovery-1.0.pc.in
new file mode 100644
index 0000000..e6c94a3
--- /dev/null
+++ b/src/libirecovery-1.0.pc.in
@@ -0,0 +1,11 @@
1prefix=@prefix@
2exec_prefix=@exec_prefix@
3libdir=@libdir@
4includedir=@includedir@
5
6Name: @PACKAGE_NAME@
7Description: A library to communicate with iBoot/iBSS on iOS devices via USB
8Version: @PACKAGE_VERSION@
9Libs: -L${libdir} -lirecovery-1.0
10Cflags: -I${includedir}
11Requires.private: libimobiledevice-glue-1.0 >= @LIMD_GLUE_VERSION@ @LIBUSB_REQUIRED@
diff --git a/src/libirecovery.c b/src/libirecovery.c
index 38e0fc3..bf9a0d6 100644
--- a/src/libirecovery.c
+++ b/src/libirecovery.c
@@ -1,117 +1,2056 @@
1/** 1/*
2 * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode 2 * libirecovery.c
3 * Copyright (C) 2008 - 2009 westbaer 3 * Communication to iBoot/iBSS on Apple iOS devices via USB
4 * 4 *
5 * This program is free software: you can redistribute it and/or modify 5 * Copyright (c) 2011-2023 Nikias Bassen <nikias@gmx.li>
6 * it under the terms of the GNU General Public License as published by 6 * Copyright (c) 2012-2020 Martin Szulecki <martin.szulecki@libimobiledevice.org>
7 * the Free Software Foundation, either version 3 of the License, or 7 * Copyright (c) 2010 Chronic-Dev Team
8 * (at your option) any later version. 8 * Copyright (c) 2010 Joshua Hill
9 * Copyright (c) 2008-2011 Nicolas Haunold
9 * 10 *
10 * This program is distributed in the hope that it will be useful, 11 * All rights reserved. This program and the accompanying materials
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * are made available under the terms of the GNU Lesser General Public License
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * (LGPL) version 2.1 which accompanies this distribution, and is available at
13 * GNU General Public License for more details. 14 * http://www.gnu.org/licenses/lgpl-2.1.html
14 * 15 *
15 * You should have received a copy of the GNU General Public License 16 * This library is distributed in the hope that it will be useful,
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **/ 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
18 25
19#include <stdio.h> 26#include <stdio.h>
27#include <stdint.h>
20#include <stdlib.h> 28#include <stdlib.h>
21#include <string.h> 29#include <string.h>
30#include <inttypes.h>
31#include <ctype.h>
22#include <unistd.h> 32#include <unistd.h>
23#include <libusb-1.0/libusb.h> 33#include <sys/stat.h>
34
35#include <libimobiledevice-glue/collection.h>
36#include <libimobiledevice-glue/thread.h>
37
38#ifndef USE_DUMMY
39#ifndef _WIN32
40#ifndef HAVE_IOKIT
41#include <libusb.h>
42#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102))
43#define HAVE_LIBUSB_HOTPLUG_API 1
44#endif
45#else
46#include <CoreFoundation/CoreFoundation.h>
47#include <IOKit/usb/IOUSBLib.h>
48#include <IOKit/IOCFPlugIn.h>
49#endif
50#else
51#define WIN32_LEAN_AND_MEAN
52#include <windows.h>
53#include <setupapi.h>
54#ifndef sleep
55#define sleep(n) Sleep(1000 * n)
56#endif
57#endif
58#endif
59
60#ifdef IRECV_STATIC
61 #define IRECV_API
62#elif defined(_WIN32)
63 #define IRECV_API __declspec( dllexport )
64#else
65 #if __GNUC__ >= 4
66 #define IRECV_API __attribute__((visibility("default")))
67 #else
68 #define IRECV_API
69 #endif
70#endif
24 71
25#include "libirecovery.h" 72#include "libirecovery.h"
26 73
74// Reference: https://stackoverflow.com/a/2390626/1806760
75// Initializer/finalizer sample for MSVC and GCC/Clang.
76// 2010-2016 Joe Lowe. Released into the public domain.
77
78#ifdef __cplusplus
79 #define INITIALIZER(f) \
80 static void f(void); \
81 struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
82 static void f(void)
83#elif defined(_MSC_VER)
84 #pragma section(".CRT$XCU",read)
85 #define INITIALIZER2_(f,p) \
86 static void f(void); \
87 __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
88 __pragma(comment(linker,"/include:" p #f "_")) \
89 static void f(void)
90 #ifdef _WIN64
91 #define INITIALIZER(f) INITIALIZER2_(f,"")
92 #else
93 #define INITIALIZER(f) INITIALIZER2_(f,"_")
94 #endif
95#else
96 #define INITIALIZER(f) \
97 static void f(void) __attribute__((__constructor__)); \
98 static void f(void)
99#endif
100
101struct irecv_client_private {
102 int debug;
103 int usb_config;
104 int usb_interface;
105 int usb_alt_interface;
106 unsigned int mode;
107 int isKIS;
108 struct irecv_device_info device_info;
109#ifndef USE_DUMMY
110#ifndef _WIN32
111#ifndef HAVE_IOKIT
112 libusb_device_handle* handle;
113#else
114 IOUSBDeviceInterface320 **handle;
115 IOUSBInterfaceInterface300 **usbInterface;
116#endif
117#else
118 HANDLE handle;
119#endif
120 irecv_event_cb_t progress_callback;
121 irecv_event_cb_t received_callback;
122 irecv_event_cb_t connected_callback;
123 irecv_event_cb_t precommand_callback;
124 irecv_event_cb_t postcommand_callback;
125 irecv_event_cb_t disconnected_callback;
126#endif
127};
128
129#define USB_TIMEOUT 10000
130#define APPLE_VENDOR_ID 0x05AC
131
132// KIS
133#define KIS_PRODUCT_ID 0x1881
134
135#define KIS_PORTAL_CONFIG 0x01
136#define KIS_PORTAL_RSM 0x10
137
138#define KIS_INDEX_UPLOAD 0x0D
139#define KIS_INDEX_ENABLE_A 0x0A // macOS writes to this
140#define KIS_INDEX_ENABLE_B 0x14 // macOS writes to this
141#define KIS_INDEX_GET_INFO 0x100
142#define KIS_INDEX_BOOT_IMG 0x103
143
144#define KIS_ENABLE_A_VAL 0x21 // Value to write to KIS_INDEX_ENABLE_A
145#define KIS_ENABLE_B_VAL 0x01 // Value to write to KIS_INDEX_ENABLE_B
146
27#define BUFFER_SIZE 0x1000 147#define BUFFER_SIZE 0x1000
28#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__) 148#define debug(...) if (libirecovery_debug) fprintf(stderr, __VA_ARGS__)
29 149
30static int libirecovery_debug = 0; 150static int libirecovery_debug = 0;
151#ifndef USE_DUMMY
152#ifndef _WIN32
153#ifndef HAVE_IOKIT
31static libusb_context* libirecovery_context = NULL; 154static libusb_context* libirecovery_context = NULL;
155#endif
156#endif
157#endif
158
159static struct irecv_device irecv_devices[] = {
160 /* iPhone */
161 { "iPhone1,1", "m68ap", 0x00, 0x8900, "iPhone 2G" },
162 { "iPhone1,2", "n82ap", 0x04, 0x8900, "iPhone 3G" },
163 { "iPhone2,1", "n88ap", 0x00, 0x8920, "iPhone 3Gs" },
164 { "iPhone3,1", "n90ap", 0x00, 0x8930, "iPhone 4 (GSM)" },
165 { "iPhone3,2", "n90bap", 0x04, 0x8930, "iPhone 4 (GSM) R2 2012" },
166 { "iPhone3,3", "n92ap", 0x06, 0x8930, "iPhone 4 (CDMA)" },
167 { "iPhone4,1", "n94ap", 0x08, 0x8940, "iPhone 4s" },
168 { "iPhone5,1", "n41ap", 0x00, 0x8950, "iPhone 5 (GSM)" },
169 { "iPhone5,2", "n42ap", 0x02, 0x8950, "iPhone 5 (Global)" },
170 { "iPhone5,3", "n48ap", 0x0a, 0x8950, "iPhone 5c (GSM)" },
171 { "iPhone5,4", "n49ap", 0x0e, 0x8950, "iPhone 5c (Global)" },
172 { "iPhone6,1", "n51ap", 0x00, 0x8960, "iPhone 5s (GSM)" },
173 { "iPhone6,2", "n53ap", 0x02, 0x8960, "iPhone 5s (Global)" },
174 { "iPhone7,1", "n56ap", 0x04, 0x7000, "iPhone 6 Plus" },
175 { "iPhone7,2", "n61ap", 0x06, 0x7000, "iPhone 6" },
176 { "iPhone8,1", "n71ap", 0x04, 0x8000, "iPhone 6s" },
177 { "iPhone8,1", "n71map", 0x04, 0x8003, "iPhone 6s" },
178 { "iPhone8,2", "n66ap", 0x06, 0x8000, "iPhone 6s Plus" },
179 { "iPhone8,2", "n66map", 0x06, 0x8003, "iPhone 6s Plus" },
180 { "iPhone8,4", "n69ap", 0x02, 0x8003, "iPhone SE (1st gen)" },
181 { "iPhone8,4", "n69uap", 0x02, 0x8000, "iPhone SE (1st gen)" },
182 { "iPhone9,1", "d10ap", 0x08, 0x8010, "iPhone 7 (Global)" },
183 { "iPhone9,2", "d11ap", 0x0a, 0x8010, "iPhone 7 Plus (Global)" },
184 { "iPhone9,3", "d101ap", 0x0c, 0x8010, "iPhone 7 (GSM)" },
185 { "iPhone9,4", "d111ap", 0x0e, 0x8010, "iPhone 7 Plus (GSM)" },
186 { "iPhone10,1", "d20ap", 0x02, 0x8015, "iPhone 8 (Global)" },
187 { "iPhone10,2", "d21ap", 0x04, 0x8015, "iPhone 8 Plus (Global)" },
188 { "iPhone10,3", "d22ap", 0x06, 0x8015, "iPhone X (Global)" },
189 { "iPhone10,4", "d201ap", 0x0a, 0x8015, "iPhone 8 (GSM)" },
190 { "iPhone10,5", "d211ap", 0x0c, 0x8015, "iPhone 8 Plus (GSM)" },
191 { "iPhone10,6", "d221ap", 0x0e, 0x8015, "iPhone X (GSM)" },
192 { "iPhone11,2", "d321ap", 0x0e, 0x8020, "iPhone XS" },
193 { "iPhone11,4", "d331ap", 0x0a, 0x8020, "iPhone XS Max (China)" },
194 { "iPhone11,6", "d331pap", 0x1a, 0x8020, "iPhone XS Max" },
195 { "iPhone11,8", "n841ap", 0x0c, 0x8020, "iPhone XR" },
196 { "iPhone12,1", "n104ap", 0x04, 0x8030, "iPhone 11" },
197 { "iPhone12,3", "d421ap", 0x06, 0x8030, "iPhone 11 Pro" },
198 { "iPhone12,5", "d431ap", 0x02, 0x8030, "iPhone 11 Pro Max" },
199 { "iPhone12,8", "d79ap", 0x10, 0x8030, "iPhone SE (2nd gen)" },
200 { "iPhone13,1", "d52gap", 0x0A, 0x8101, "iPhone 12 mini" },
201 { "iPhone13,2", "d53gap", 0x0C, 0x8101, "iPhone 12" },
202 { "iPhone13,3", "d53pap", 0x0E, 0x8101, "iPhone 12 Pro" },
203 { "iPhone13,4", "d54pap", 0x08, 0x8101, "iPhone 12 Pro Max" },
204 { "iPhone14,2", "d63ap", 0x0C, 0x8110, "iPhone 13 Pro" },
205 { "iPhone14,3", "d64ap", 0x0E, 0x8110, "iPhone 13 Pro Max" },
206 { "iPhone14,4", "d16ap", 0x08, 0x8110, "iPhone 13 mini" },
207 { "iPhone14,5", "d17ap", 0x0A, 0x8110, "iPhone 13" },
208 { "iPhone14,6", "d49ap", 0x10, 0x8110, "iPhone SE (3rd gen)" },
209 { "iPhone14,7", "d27ap", 0x18, 0x8110, "iPhone 14" },
210 { "iPhone14,8", "d28ap", 0x1A, 0x8110, "iPhone 14 Plus" },
211 { "iPhone15,2", "d73ap", 0x0C, 0x8120, "iPhone 14 Pro" },
212 { "iPhone15,3", "d74ap", 0x0E, 0x8120, "iPhone 14 Pro Max" },
213 { "iPhone15,4", "d37ap", 0x08, 0x8120, "iPhone 15" },
214 { "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" },
215 { "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" },
216 { "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" },
217 { "iPhone17,1", "d93ap", 0x0C, 0x8140, "iPhone 16 Pro" },
218 { "iPhone17,2", "d94ap", 0x0E, 0x8140, "iPhone 16 Pro Max" },
219 { "iPhone17,3", "d47ap", 0x08, 0x8140, "iPhone 16" },
220 { "iPhone17,4", "d48ap", 0x0A, 0x8140, "iPhone 16 Plus" },
221 { "iPhone17,5", "v59ap", 0x04, 0x8140, "iPhone 16e" },
222 { "iPhone18,1", "v53ap", 0x0C, 0x8150, "iPhone 17 Pro" },
223 { "iPhone18,2", "v54ap", 0x0E, 0x8150, "iPhone 17 Pro Max" },
224 { "iPhone18,3", "v57ap", 0x08, 0x8150, "iPhone 17" },
225 { "iPhone18,4", "d23ap", 0x0A, 0x8150, "iPhone Air" },
226 /* iPod */
227 { "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" },
228 { "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" },
229 { "iPod3,1", "n18ap", 0x02, 0x8922, "iPod Touch (3rd gen)" },
230 { "iPod4,1", "n81ap", 0x08, 0x8930, "iPod Touch (4th gen)" },
231 { "iPod5,1", "n78ap", 0x00, 0x8942, "iPod Touch (5th gen)" },
232 { "iPod7,1", "n102ap", 0x10, 0x7000, "iPod Touch (6th gen)" },
233 { "iPod9,1", "n112ap", 0x16, 0x8010, "iPod Touch (7th gen)" },
234 /* iPad */
235 { "iPad1,1", "k48ap", 0x02, 0x8930, "iPad" },
236 { "iPad2,1", "k93ap", 0x04, 0x8940, "iPad 2 (WiFi)" },
237 { "iPad2,2", "k94ap", 0x06, 0x8940, "iPad 2 (GSM)" },
238 { "iPad2,3", "k95ap", 0x02, 0x8940, "iPad 2 (CDMA)" },
239 { "iPad2,4", "k93aap", 0x06, 0x8942, "iPad 2 (WiFi) R2 2012" },
240 { "iPad2,5", "p105ap", 0x0a, 0x8942, "iPad mini (WiFi)" },
241 { "iPad2,6", "p106ap", 0x0c, 0x8942, "iPad mini (GSM)" },
242 { "iPad2,7", "p107ap", 0x0e, 0x8942, "iPad mini (Global)" },
243 { "iPad3,1", "j1ap", 0x00, 0x8945, "iPad (3rd gen, WiFi)" },
244 { "iPad3,2", "j2ap", 0x02, 0x8945, "iPad (3rd gen, CDMA)" },
245 { "iPad3,3", "j2aap", 0x04, 0x8945, "iPad (3rd gen, GSM)" },
246 { "iPad3,4", "p101ap", 0x00, 0x8955, "iPad (4th gen, WiFi)" },
247 { "iPad3,5", "p102ap", 0x02, 0x8955, "iPad (4th gen, GSM)" },
248 { "iPad3,6", "p103ap", 0x04, 0x8955, "iPad (4th gen, Global)" },
249 { "iPad4,1", "j71ap", 0x10, 0x8960, "iPad Air (WiFi)" },
250 { "iPad4,2", "j72ap", 0x12, 0x8960, "iPad Air (Cellular)" },
251 { "iPad4,3", "j73ap", 0x14, 0x8960, "iPad Air (China)" },
252 { "iPad4,4", "j85ap", 0x0a, 0x8960, "iPad mini 2 (WiFi)" },
253 { "iPad4,5", "j86ap", 0x0c, 0x8960, "iPad mini 2 (Cellular)" },
254 { "iPad4,6", "j87ap", 0x0e, 0x8960, "iPad mini 2 (China)" },
255 { "iPad4,7", "j85map", 0x32, 0x8960, "iPad mini 3 (WiFi)" },
256 { "iPad4,8", "j86map", 0x34, 0x8960, "iPad mini 3 (Cellular)" },
257 { "iPad4,9", "j87map", 0x36, 0x8960, "iPad mini 3 (China)" },
258 { "iPad5,1", "j96ap", 0x08, 0x7000, "iPad mini 4 (WiFi)" },
259 { "iPad5,2", "j97ap", 0x0A, 0x7000, "iPad mini 4 (Cellular)" },
260 { "iPad5,3", "j81ap", 0x06, 0x7001, "iPad Air 2 (WiFi)" },
261 { "iPad5,4", "j82ap", 0x02, 0x7001, "iPad Air 2 (Cellular)" },
262 { "iPad6,3", "j127ap", 0x08, 0x8001, "iPad Pro 9.7-inch (WiFi)" },
263 { "iPad6,4", "j128ap", 0x0a, 0x8001, "iPad Pro 9.7-inch (Cellular)" },
264 { "iPad6,7", "j98aap", 0x10, 0x8001, "iPad Pro 12.9-inch (1st gen, WiFi)" },
265 { "iPad6,8", "j99aap", 0x12, 0x8001, "iPad Pro 12.9-inch (1st gen, Cellular)" },
266 { "iPad6,11", "j71sap", 0x10, 0x8000, "iPad (5th gen, WiFi)" },
267 { "iPad6,11", "j71tap", 0x10, 0x8003, "iPad (5th gen, WiFi)" },
268 { "iPad6,12", "j72sap", 0x12, 0x8000, "iPad (5th gen, Cellular)" },
269 { "iPad6,12", "j72tap", 0x12, 0x8003, "iPad (5th gen, Cellular)" },
270 { "iPad7,1", "j120ap", 0x0C, 0x8011, "iPad Pro 12.9-inch (2nd gen, WiFi)" },
271 { "iPad7,2", "j121ap", 0x0E, 0x8011, "iPad Pro 12.9-inch (2nd gen, Cellular)" },
272 { "iPad7,3", "j207ap", 0x04, 0x8011, "iPad Pro 10.5-inch (WiFi)" },
273 { "iPad7,4", "j208ap", 0x06, 0x8011, "iPad Pro 10.5-inch (Cellular)" },
274 { "iPad7,5", "j71bap", 0x18, 0x8010, "iPad (6th gen, WiFi)" },
275 { "iPad7,6", "j72bap", 0x1A, 0x8010, "iPad (6th gen, Cellular)" },
276 { "iPad7,11", "j171ap", 0x1C, 0x8010, "iPad (7th gen, WiFi)" },
277 { "iPad7,12", "j172ap", 0x1E, 0x8010, "iPad (7th gen, Cellular)" },
278 { "iPad8,1", "j317ap", 0x0C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi)" },
279 { "iPad8,2", "j317xap", 0x1C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi, 1TB)" },
280 { "iPad8,3", "j318ap", 0x0E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular)" },
281 { "iPad8,4", "j318xap", 0x1E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular, 1TB)" },
282 { "iPad8,5", "j320ap", 0x08, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi)" },
283 { "iPad8,6", "j320xap", 0x18, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi, 1TB)" },
284 { "iPad8,7", "j321ap", 0x0A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular)" },
285 { "iPad8,8", "j321xap", 0x1A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular, 1TB)" },
286 { "iPad8,9", "j417ap", 0x3C, 0x8027, "iPad Pro 11-inch (2nd gen, WiFi)" },
287 { "iPad8,10", "j418ap", 0x3E, 0x8027, "iPad Pro 11-inch (2nd gen, Cellular)" },
288 { "iPad8,11", "j420ap", 0x38, 0x8027, "iPad Pro 12.9-inch (4th gen, WiFi)" },
289 { "iPad8,12", "j421ap", 0x3A, 0x8027, "iPad Pro 12.9-inch (4th gen, Cellular)" },
290 { "iPad11,1", "j210ap", 0x14, 0x8020, "iPad mini (5th gen, WiFi)" },
291 { "iPad11,2", "j211ap", 0x16, 0x8020, "iPad mini (5th gen, Cellular)" },
292 { "iPad11,3", "j217ap", 0x1C, 0x8020, "iPad Air (3rd gen, WiFi)" },
293 { "iPad11,4", "j218ap", 0x1E, 0x8020, "iPad Air (3rd gen, Cellular)" },
294 { "iPad11,6", "j171aap", 0x24, 0x8020, "iPad (8th gen, WiFi)" },
295 { "iPad11,7", "j172aap", 0x26, 0x8020, "iPad (8th gen, Cellular)" },
296 { "iPad12,1", "j181ap", 0x18, 0x8030, "iPad (9th gen, WiFi)" },
297 { "iPad12,2", "j182ap", 0x1A, 0x8030, "iPad (9th gen, Cellular)" },
298 { "iPad13,1", "j307ap", 0x04, 0x8101, "iPad Air (4th gen, WiFi)" },
299 { "iPad13,2", "j308ap", 0x06, 0x8101, "iPad Air (4th gen, Cellular)" },
300 { "iPad13,4", "j517ap", 0x08, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi)" },
301 { "iPad13,5", "j517xap", 0x0A, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi, 2TB)" },
302 { "iPad13,6", "j518ap", 0x0C, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular)" },
303 { "iPad13,7", "j518xap", 0x0E, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular, 2TB)" },
304 { "iPad13,8", "j522ap", 0x18, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi)" },
305 { "iPad13,9", "j522xap", 0x1A, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi, 2TB)" },
306 { "iPad13,10", "j523ap", 0x1C, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular)" },
307 { "iPad13,11", "j523xap", 0x1E, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular, 2TB)" },
308 { "iPad13,16", "j407ap", 0x10, 0x8103, "iPad Air (5th gen, WiFi)" },
309 { "iPad13,17", "j408ap", 0x12, 0x8103, "iPad Air (5th gen, Cellular)" },
310 { "iPad13,18", "j271ap", 0x14, 0x8101, "iPad (10th gen, WiFi)" },
311 { "iPad13,19", "j272ap", 0x16, 0x8101, "iPad (10th gen, Cellular)" },
312 { "iPad14,1", "j310ap", 0x04, 0x8110, "iPad mini (6th gen, WiFi)" },
313 { "iPad14,2", "j311ap", 0x06, 0x8110, "iPad mini (6th gen, Cellular)" },
314 { "iPad14,3", "j617ap", 0x08, 0x8112, "iPad Pro 11-inch (4th gen, WiFi)" },
315 { "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" },
316 { "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" },
317 { "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" },
318 { "iPad14,8", "j507ap", 0x10, 0x8112, "iPad Air 11-inch (M2, WiFi)" },
319 { "iPad14,9", "j508ap", 0x12, 0x8112, "iPad Air 11-inch (M2, Cellular)" },
320 { "iPad14,10", "j537ap", 0x14, 0x8112, "iPad Air 13-inch (M2, WiFi)" },
321 { "iPad14,11", "j538ap", 0x16, 0x8112, "iPad Air 13-inch (M2, Cellular)" },
322 { "iPad15,3", "j607ap", 0x08, 0x8122, "iPad Air 11-inch (M3, WiFi)" },
323 { "iPad15,4", "j608ap", 0x0A, 0x8122, "iPad Air 11-inch (M3, Cellular)" },
324 { "iPad15,5", "j637ap", 0x0C, 0x8122, "iPad Air 13-inch (M3, WiFi)" },
325 { "iPad15,6", "j638ap", 0x0E, 0x8122, "iPad Air 13-inch (M3, Cellular)" },
326 { "iPad15,7", "j481ap", 0x10, 0x8120, "iPad (A16, WiFi)" },
327 { "iPad15,8", "j482ap", 0x12, 0x8120, "iPad (A16, Cellular)" },
328 { "iPad16,1", "j410ap", 0x08, 0x8130, "iPad mini (A17 Pro, WiFi)" },
329 { "iPad16,2", "j411ap", 0x0A, 0x8130, "iPad mini (A17 Pro, Cellular)" },
330 { "iPad16,3", "j717ap", 0x08, 0x8132, "iPad Pro 11-inch (M4, WiFi)" },
331 { "iPad16,4", "j718ap", 0x0A, 0x8132, "iPad Pro 11-inch (M4, Cellular)" },
332 { "iPad16,5", "j720ap", 0x0C, 0x8132, "iPad Pro 13-inch (M4, WiFi)" },
333 { "iPad16,6", "j721ap", 0x0E, 0x8132, "iPad Pro 13-inch (M4, Cellular)" },
334 { "iPad17,1", "j817ap", 0x08, 0x8142, "iPad Pro 11-inch (M5, WiFi)" },
335 { "iPad17,2", "j818ap", 0x0A, 0x8142, "iPad Pro 11-inch (M5, Cellular)" },
336 { "iPad17,3", "j820ap", 0x0C, 0x8142, "iPad Pro 13-inch (M5, WiFi)" },
337 { "iPad17,4", "j821ap", 0x0E, 0x8142, "iPad Pro 13-inch (M5, Cellular)" },
338 /* Apple TV */
339 { "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" },
340 { "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" },
341 { "AppleTV3,2", "j33iap", 0x00, 0x8947, "Apple TV 3 (2013)" },
342 { "AppleTV5,3", "j42dap", 0x34, 0x7000, "Apple TV 4" },
343 { "AppleTV6,2", "j105aap", 0x02, 0x8011, "Apple TV 4K" },
344 { "AppleTV11,1", "j305ap", 0x08, 0x8020, "Apple TV 4K (2nd gen)" },
345 { "AppleTV14,1", "j255ap", 0x02, 0x8110, "Apple TV 4K (3rd gen)" },
346 /* HomePod */
347 { "AudioAccessory1,1", "b238aap", 0x38, 0x7000, "HomePod (1st gen)" },
348 { "AudioAccessory1,2", "b238ap", 0x1A, 0x7000, "HomePod (1st gen)" },
349 { "AudioAccessory5,1", "b520ap", 0x22, 0x8006, "HomePod mini" },
350 { "AudioAccessory6,1", "b620ap", 0x18, 0x8301, "HomePod (2nd gen)" },
351 /* Apple Watch */
352 { "Watch1,1", "n27aap", 0x02, 0x7002, "Apple Watch 38mm (1st gen)" },
353 { "Watch1,2", "n28aap", 0x04, 0x7002, "Apple Watch 42mm (1st gen)" },
354 { "Watch2,6", "n27dap", 0x02, 0x8002, "Apple Watch Series 1 (38mm)" },
355 { "Watch2,7", "n28dap", 0x04, 0x8002, "Apple Watch Series 1 (42mm)" },
356 { "Watch2,3", "n74ap", 0x0C, 0x8002, "Apple Watch Series 2 (38mm)" },
357 { "Watch2,4", "n75ap", 0x0E, 0x8002, "Apple Watch Series 2 (42mm)" },
358 { "Watch3,1", "n111sap", 0x1C, 0x8004, "Apple Watch Series 3 (38mm Cellular)" },
359 { "Watch3,2", "n111bap", 0x1E, 0x8004, "Apple Watch Series 3 (42mm Cellular)" },
360 { "Watch3,3", "n121sap", 0x18, 0x8004, "Apple Watch Series 3 (38mm)" },
361 { "Watch3,4", "n121bap", 0x1A, 0x8004, "Apple Watch Series 3 (42mm)" },
362 { "Watch4,1", "n131sap", 0x08, 0x8006, "Apple Watch Series 4 (40mm)" },
363 { "Watch4,2", "n131bap", 0x0A, 0x8006, "Apple Watch Series 4 (44mm)" },
364 { "Watch4,3", "n141sap", 0x0C, 0x8006, "Apple Watch Series 4 (40mm Cellular)" },
365 { "Watch4,4", "n141bap", 0x0E, 0x8006, "Apple Watch Series 4 (44mm Cellular)" },
366 { "Watch5,1", "n144sap", 0x10, 0x8006, "Apple Watch Series 5 (40mm)" },
367 { "Watch5,2", "n144bap", 0x12, 0x8006, "Apple Watch Series 5 (44mm)" },
368 { "Watch5,3", "n146sap", 0x14, 0x8006, "Apple Watch Series 5 (40mm Cellular)" },
369 { "Watch5,4", "n146bap", 0x16, 0x8006, "Apple Watch Series 5 (44mm Cellular)" },
370 { "Watch5,9", "n140sap", 0x28, 0x8006, "Apple Watch SE (40mm)" },
371 { "Watch5,10", "n140bap", 0x2A, 0x8006, "Apple Watch SE (44mm)" },
372 { "Watch5,11", "n142sap", 0x2C, 0x8006, "Apple Watch SE (40mm Cellular)" },
373 { "Watch5,12", "n142bap", 0x2E, 0x8006, "Apple Watch SE (44mm Cellular)" },
374 { "Watch6,1", "n157sap", 0x08, 0x8301, "Apple Watch Series 6 (40mm)" },
375 { "Watch6,2", "n157bap", 0x0A, 0x8301, "Apple Watch Series 6 (44mm)" },
376 { "Watch6,3", "n158sap", 0x0C, 0x8301, "Apple Watch Series 6 (40mm Cellular)" },
377 { "Watch6,4", "n158bap", 0x0E, 0x8301, "Apple Watch Series 6 (44mm Cellular)" },
378 { "Watch6,6", "n187sap", 0x10, 0x8301, "Apple Watch Series 7 (41mm)" },
379 { "Watch6,7", "n187bap", 0x12, 0x8301, "Apple Watch Series 7 (45mm)" },
380 { "Watch6,8", "n188sap", 0x14, 0x8301, "Apple Watch Series 7 (41mm Cellular)" },
381 { "Watch6,9", "n188bap", 0x16, 0x8301, "Apple Watch Series 7 (45mm Cellular)" },
382 { "Watch6,10", "n143sap", 0x28, 0x8301, "Apple Watch SE 2 (40mm)" },
383 { "Watch6,11", "n143bap", 0x2A, 0x8301, "Apple Watch SE 2 (44mm)" },
384 { "Watch6,12", "n149sap", 0x2C, 0x8301, "Apple Watch SE 2 (40mm Cellular)" },
385 { "Watch6,13", "n149bap", 0x2E, 0x8301, "Apple Watch SE 2 (44mm Cellular)" },
386 { "Watch6,14", "n197sap", 0x30, 0x8301, "Apple Watch Series 8 (41mm)" },
387 { "Watch6,15", "n197bap", 0x32, 0x8301, "Apple Watch Series 8 (45mm)" },
388 { "Watch6,16", "n198sap", 0x34, 0x8301, "Apple Watch Series 8 (41mm Cellular)" },
389 { "Watch6,17", "n198bap", 0x36, 0x8301, "Apple Watch Series 8 (45mm Cellular)" },
390 { "Watch6,18", "n199ap", 0x26, 0x8301, "Apple Watch Ultra" },
391 { "Watch7,1", "n207sap", 0x08, 0x8310, "Apple Watch Series 9 (41mm)" },
392 { "Watch7,2", "n207bap", 0x0A, 0x8310, "Apple Watch Series 9 (45mm)" },
393 { "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" },
394 { "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" },
395 { "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" },
396 { "Watch7,8", "n217sap", 0x10, 0x8310, "Apple Watch Series 10 (42mm)" },
397 { "Watch7,9", "n217bap", 0x12, 0x8310, "Apple Watch Series 10 (46mm)" },
398 { "Watch7,10", "n218sap", 0x14, 0x8310, "Apple Watch Series 10 (42mm Cellular)" },
399 { "Watch7,11", "n218bap", 0x16, 0x8310, "Apple Watch Series 10 (46mm Cellular)" },
400 { "Watch7,12", "n230ap", 0x22, 0x8310, "Apple Watch Ultra 3" },
401 { "Watch7,13", "n243sap", 0x28, 0x8310, "Apple Watch SE 3 (40mm)" },
402 { "Watch7,14", "n243bap", 0x2A, 0x8310, "Apple Watch SE 3 (44mm)" },
403 { "Watch7,15", "n244sap", 0x2C, 0x8310, "Apple Watch SE 3 (40mm Cellular)" },
404 { "Watch7,16", "n244bap", 0x2E, 0x8310, "Apple Watch SE 3 (44mm Cellular)" },
405 { "Watch7,17", "n227sap", 0x18, 0x8310, "Apple Watch Series 11 (42mm)" },
406 { "Watch7,18", "n227bap", 0x1A, 0x8310, "Apple Watch Series 11 (46mm)" },
407 { "Watch7,19", "n228sap", 0x1C, 0x8310, "Apple Watch Series 11 (42mm Cellular)" },
408 { "Watch7,20", "n228bap", 0x1E, 0x8310, "Apple Watch Series 11 (46mm Cellular)" },
409 /* Apple Silicon Macs */
410 { "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" },
411 { "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" },
412 { "MacBookPro17,1", "j293ap", 0x24, 0x8103, "MacBook Pro (M1, 13-inch, 2020)" },
413 { "MacBookPro18,1", "j316sap", 0x0A, 0x6000, "MacBook Pro (M1 Pro, 16-inch, 2021)" },
414 { "MacBookPro18,2", "j316cap", 0x0A, 0x6001, "MacBook Pro (M1 Max, 16-inch, 2021)" },
415 { "MacBookPro18,3", "j314sap", 0x08, 0x6000, "MacBook Pro (M1 Pro, 14-inch, 2021)" },
416 { "MacBookPro18,4", "j314cap", 0x08, 0x6001, "MacBook Pro (M1 Max, 14-inch, 2021)" },
417 { "MacBookAir10,1", "j313ap", 0x26, 0x8103, "MacBook Air (M1, 2020)" },
418 { "iMac21,1", "j456ap", 0x28, 0x8103, "iMac 24-inch (M1, Two Ports, 2021)" },
419 { "iMac21,2", "j457ap", 0x2A, 0x8103, "iMac 24-inch (M1, Four Ports, 2021)" },
420 { "Mac13,1", "j375cap", 0x04, 0x6001, "Mac Studio (M1 Max, 2022)" },
421 { "Mac13,2", "j375dap", 0x0C, 0x6002, "Mac Studio (M1 Ultra, 2022)" },
422 { "Mac14,2", "j413ap", 0x28, 0x8112, "MacBook Air (M2, 2022)" },
423 { "Mac14,7", "j493ap", 0x2A, 0x8112, "MacBook Pro (M2, 13-inch, 2022)" },
424 { "Mac14,3", "j473ap", 0x24, 0x8112, "Mac mini (M2, 2023)" },
425 { "Mac14,5", "j414cap", 0x04, 0x6021, "MacBook Pro (14-inch, M2 Max, 2023)" },
426 { "Mac14,6", "j416cap", 0x06, 0x6021, "MacBook Pro (16-inch, M2 Max, 2023)" },
427 { "Mac14,8", "j180dap", 0x08, 0x6022, "Mac Pro (2023)" },
428 { "Mac14,9", "j414sap", 0x04, 0x6020, "MacBook Pro (14-inch, M2 Pro, 2023)" },
429 { "Mac14,10", "j416sap", 0x06, 0x6020, "MacBook Pro (16-inch, M2 Pro, 2023)" },
430 { "Mac14,12", "j474sap", 0x02, 0x6020, "Mac mini (M2 Pro, 2023)" },
431 { "Mac14,13", "j475cap", 0x0A, 0x6021, "Mac Studio (M2 Max, 2023)" },
432 { "Mac14,14", "j475dap", 0x0A, 0x6022, "Mac Studio (M2 Ultra, 2023)" },
433 { "Mac14,15", "j415ap", 0x2E, 0x8112, "MacBook Air (M2, 15-inch, 2023)" },
434 { "Mac15,3", "j504ap", 0x22, 0x8122, "MacBook Pro (14-inch, M3, Nov 2023)" },
435 { "Mac15,4", "j433ap", 0x28, 0x8122, "iMac 24-inch (M3, Two Ports, 2023)" },
436 { "Mac15,5", "j434ap", 0x2A, 0x8122, "iMac 24-inch (M3, Four Ports, 2023)" },
437 { "Mac15,6", "j514sap", 0x04, 0x6030, "MacBook Pro (14-inch, M3 Pro, Nov 2023)" },
438 { "Mac15,7", "j516sap", 0x06, 0x6030, "MacBook Pro (16-inch, M3 Pro, Nov 2023)" },
439 { "Mac15,8", "j514cap", 0x44, 0x6031, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
440 { "Mac15,9", "j516cap", 0x46, 0x6031, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
441 { "Mac15,10", "j514map", 0x44, 0x6034, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
442 { "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
443 { "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" },
444 { "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-inch, M3, 2024)" },
445 { "Mac15,14", "j575dap", 0x44, 0x6032, "Mac Studio (M3 Ultra, 2025)" },
446 { "Mac16,1", "j604ap", 0x22, 0x8132, "MacBook Pro (14-inch, M4, Nov 2024)" },
447 { "Mac16,2", "j623ap", 0x24, 0x8132, "iMac 24-inch (M4, Two Ports, 2024)" },
448 { "Mac16,3", "j624ap", 0x26, 0x8132, "iMac 24-inch (M4, Four Ports, 2024)" },
449 { "Mac16,5", "j616cap", 0x06, 0x6041, "MacBook Pro (16-inch, M4 Max, Nov 2024)" },
450 { "Mac16,6", "j614cap", 0x04, 0x6041, "MacBook Pro (14-inch, M4 Max, Nov 2024)" },
451 { "Mac16,7", "j616sap", 0x06, 0x6040, "MacBook Pro (16-inch, M4 Pro, Nov 2024)" },
452 { "Mac16,8", "j614sap", 0x04, 0x6040, "MacBook Pro (14-inch, M4 Pro, Nov 2024)" },
453 { "Mac16,9", "j575cap", 0x02, 0x6041, "Mac Studio (M4 Max, 2025)" },
454 { "Mac16,10", "j773gap", 0x2A, 0x8132, "Mac mini (M4, 2024)" },
455 { "Mac16,11", "j773sap", 0x02, 0x6040, "Mac mini (M4 Pro, 2024)" },
456 { "Mac16,12", "j713ap", 0x2C, 0x8132, "MacBook Air (13-inch, M4, 2025)" },
457 { "Mac16,13", "j715ap", 0x2E, 0x8132, "MacBook Air (15-inch, M4, 2025)" },
458 { "Mac17,2", "j704ap", 0x22, 0x8142, "MacBook Pro (14-inch, M5, 2025)" },
459 /* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */
460 { "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" },
461 /* Apple T2 Coprocessor */
462 { "iBridge2,1", "j137ap", 0x0A, 0x8012, "Apple T2 iMacPro1,1 (j137)" },
463 { "iBridge2,3", "j680ap", 0x0B, 0x8012, "Apple T2 MacBookPro15,1 (j680)" },
464 { "iBridge2,4", "j132ap", 0x0C, 0x8012, "Apple T2 MacBookPro15,2 (j132)" },
465 { "iBridge2,5", "j174ap", 0x0E, 0x8012, "Apple T2 Macmini8,1 (j174)" },
466 { "iBridge2,6", "j160ap", 0x0F, 0x8012, "Apple T2 MacPro7,1 (j160)" },
467 { "iBridge2,7", "j780ap", 0x07, 0x8012, "Apple T2 MacBookPro15,3 (j780)" },
468 { "iBridge2,8", "j140kap", 0x17, 0x8012, "Apple T2 MacBookAir8,1 (j140k)" },
469 { "iBridge2,10", "j213ap", 0x18, 0x8012, "Apple T2 MacBookPro15,4 (j213)" },
470 { "iBridge2,12", "j140aap", 0x37, 0x8012, "Apple T2 MacBookAir8,2 (j140a)" },
471 { "iBridge2,14", "j152fap", 0x3A, 0x8012, "Apple T2 MacBookPro16,1 (j152f)" },
472 { "iBridge2,15", "j230kap", 0x3F, 0x8012, "Apple T2 MacBookAir9,1 (j230k)" },
473 { "iBridge2,16", "j214kap", 0x3E, 0x8012, "Apple T2 MacBookPro16,2 (j214k)" },
474 { "iBridge2,19", "j185ap", 0x22, 0x8012, "Apple T2 iMac20,1 (j185)" },
475 { "iBridge2,20", "j185fap", 0x23, 0x8012, "Apple T2 iMac20,2 (j185f)" },
476 { "iBridge2,21", "j223ap", 0x3B, 0x8012, "Apple T2 MacBookPro16,3 (j223)" },
477 { "iBridge2,22", "j215ap", 0x38, 0x8012, "Apple T2 MacBookPro16,4 (j215)" },
478 /* Apple Displays */
479 { "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" },
480 /* Apple Vision Pro */
481 { "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" },
482 { "RealityDevice17,1", "n301aap", 0x42, 0x8142, "Apple Vision Pro (M5)" },
483 { NULL, NULL, -1, -1, NULL }
484};
485
486#ifndef USE_DUMMY
487static unsigned int crc32_lookup_t1[256] = {
488 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
489 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
490 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
491 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
492 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
493 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
494 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
495 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
496 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
497 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
498 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
499 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
500 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
501 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
502 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
503 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
504 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
505 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
506 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
507 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
508 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
509 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
510 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
511 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
512 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
513 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
514 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
515 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
516 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
517 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
518 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
519 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
520 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
521 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
522 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
523 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
524 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
525 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
526 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
527 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
528 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
529 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
530 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
531 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
532 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
533 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
534 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
535 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
536 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
537 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
538 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
539 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
540 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
541 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
542 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
543 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
544 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
545 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
546 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
547 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
548 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
549 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
550 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
551 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
552};
553
554#define crc32_step(a,b) \
555 a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8))
556
557#ifdef _WIN32
558#pragma pack(1)
559typedef struct {
560 uint16_t vid;
561 uint16_t pid;
562 uint32_t unk;
563 char nonces[255];
564 char serial[255];
565 char manufacturer[255];
566 char product[255];
567} KIS_device_info;
568
569typedef struct {
570 uint8_t data[0x4000];
571 uint32_t size;
572 uint32_t unused;
573 uint64_t address;
574} KIS_upload_chunk;
575#pragma pack()
576#else
577#pragma pack(1)
578typedef struct {
579 uint16_t sequence; // A sequence number
580 uint8_t version; // Protocol version
581 uint8_t portal; // The "portal" to connect to
582 uint8_t argCount; // Number of arguments
583 uint8_t indexLo; // An index
584 uint8_t indexHiRplSizeLo; // High 2 bits of index + low 6 bytes of reply size
585 uint8_t rplSizeHi; // Reply size high bits, number of words the device should send
586 uint32_t reqSize; // Size of the complete request, including the arguments and payload, excluding the header
587 // Followed by arguments and payload data
588} KIS_req_header;
589
590typedef struct {
591 KIS_req_header hdr;
592 uint32_t value;
593} KIS_config_wr32;
594
595typedef struct {
596 uint8_t bLength ; ///< Size of this descriptor in bytes.
597 uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
598 uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
599
600 uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific.
601 uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
602 uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis.
603 uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
604
605 uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
606 uint16_t idProduct ; ///< Product ID (assigned by the manufacturer).
607 uint16_t bcdDevice ; ///< Device release number in binary-coded decimal.
608 uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer.
609 uint8_t iProduct ; ///< Index of string descriptor describing product.
610 uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number.
611
612 uint8_t bNumConfigurations ; ///< Number of possible configurations.
613} usb_device_descriptor;
614
615typedef struct {
616 KIS_req_header hdr;
617 union {
618 struct {
619 uint32_t tag;
620 uint32_t unk1;
621 uint32_t maxUploadSize;
622 uint32_t maxDownloadSize; // maybe???
623 uint64_t rambase;
624 uint32_t nonceOffset;
625 uint32_t pad;
626 uint8_t unkpad[0x20];
627 usb_device_descriptor deviceDescriptor;
628 };
629 uint8_t deviceInfo[0x300];
630 };
631 uint32_t rspsize;
632 uint32_t statuscode;
633} KIS_device_info;
634
635typedef struct {
636 KIS_req_header hdr;
637 uint64_t address;
638 uint32_t size;
639 uint8_t data[0x4000];
640} KIS_upload_chunk;
641
642typedef struct {
643 KIS_req_header hdr;
644 uint32_t size; // Number of bytes read/written
645 uint32_t status;
646} KIS_generic_reply;
647#pragma pack()
648#endif
649
650#pragma pack(1)
651typedef struct {
652 uint16_t cmdcode;
653#define MSG_VERSION_QUERY 0x000
654#define MSG_ECHO 0x801
655#define MSG_DUMP_BUFFER 0x802
656#define MSG_SEND_COMMAND 0x803
657#define MSG_READ_FILE 0x804
658#define MSG_SEND_FILE 0x805
659#define MSG_CRC 0x807
660#define MSG_ACK 0x808
661#define MSG_REJECT 0x809
662 uint16_t magic; // always 0x1234
663 uint32_t size;
664 uint32_t loadaddr; // 0x0 for commands, 0x09000000 for files
665} legacyCMD;
666#pragma pack()
667
668static THREAD_T th_event_handler = THREAD_T_NULL;
669struct collection listeners;
670static mutex_t listener_mutex;
671struct collection devices;
672static mutex_t device_mutex;
673#ifndef _WIN32
674#ifdef HAVE_IOKIT
675static CFRunLoopRef iokit_runloop = NULL;
676#else
677static libusb_context* irecv_hotplug_ctx = NULL;
678#endif
679#endif
680
681static void _irecv_deinit(void)
682{
683#ifndef USE_DUMMY
684#ifndef _WIN32
685#ifndef HAVE_IOKIT
686 if (libirecovery_context != NULL) {
687 libusb_exit(libirecovery_context);
688 libirecovery_context = NULL;
689 }
690#endif
691#endif
692 collection_free(&listeners);
693 mutex_destroy(&listener_mutex);
694#endif
695}
696
697INITIALIZER(_irecv_init)
698{
699 char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL");
700 if (dbglvl) {
701 libirecovery_debug = strtol(dbglvl, NULL, 0);
702 irecv_set_debug_level(libirecovery_debug);
703 }
704#ifndef USE_DUMMY
705#ifndef _WIN32
706#ifndef HAVE_IOKIT
707 libusb_init(&libirecovery_context);
708#endif
709#endif
710 collection_init(&listeners);
711 mutex_init(&listener_mutex);
712#endif
713 atexit(_irecv_deinit);
714}
715
716#ifdef HAVE_IOKIT
717static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
718{
719 IOReturn result;
720 IOUSBDevRequest request;
721 unsigned char descriptor[256];
722
723 request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
724 request.bRequest = kUSBRqGetDescriptor;
725 request.wValue = (kUSBStringDesc << 8); // | desc_index;
726 request.wIndex = 0; // All languages 0x409; // language
727 request.wLength = sizeof(descriptor) - 1;
728 request.pData = descriptor;
729 request.wLenDone = 0;
730
731 result = (*client->handle)->DeviceRequest(client->handle, &request);
732 if (result == kIOReturnNoDevice)
733 return IRECV_E_NO_DEVICE;
734 if (result == kIOReturnNotOpen)
735 return IRECV_E_USB_STATUS;
736 if (result != kIOReturnSuccess)
737 return IRECV_E_UNKNOWN_ERROR;
738
739 if (descriptor[0] >= 4) { // && descriptor[2] == 0x9 && descriptor[3] == 0x4) {
740
741 request.wValue = (kUSBStringDesc << 8) | desc_index;
742 request.wIndex = descriptor[2] + (descriptor[3] << 8);
743 request.wLenDone = 0;
744 result = (*client->handle)->DeviceRequest(client->handle, &request);
745
746 if (result == kIOReturnNoDevice)
747 return IRECV_E_NO_DEVICE;
748 if (result == kIOReturnNotOpen)
749 return IRECV_E_USB_STATUS;
750 if (result != kIOReturnSuccess)
751 return IRECV_E_UNKNOWN_ERROR;
752
753 int i = 2, j = 0;
754 for ( ; i < descriptor[0]; i += 2, j += 1) {
755 buffer[j] = descriptor[i];
756 }
757 buffer[j] = 0;
758
759 return request.wLenDone;
760 }
761 return IRECV_E_UNKNOWN_ERROR;
762}
763#endif
764
765static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
766{
767#ifndef _WIN32
768#ifdef HAVE_IOKIT
769 return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size);
770#else
771 return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size);
772#endif
773#else
774 irecv_error_t ret;
775 unsigned short langid = 0;
776 unsigned char data[256];
777 int di, si;
778 memset(data, 0, sizeof(data));
779 memset(buffer, 0, size);
780
781 ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data)-1, USB_TIMEOUT);
782
783 if (ret < 0) return ret;
784 if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR;
785 if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR;
786
787 for (di = 0, si = 2; si < data[0]; si += 2) {
788 if (di >= (size - 1)) break;
789 if (data[si + 1]) {
790 /* high byte */
791 buffer[di++] = '?';
792 } else {
793 buffer[di++] = data[si];
794 }
795 }
796 buffer[di] = 0;
797
798 return di;
799#endif
800}
801
802static void irecv_load_device_info_from_iboot_string(irecv_client_t client, const char* iboot_string)
803{
804 if (!client || !iboot_string) {
805 return;
806 }
807
808 memset(&client->device_info, '\0', sizeof(struct irecv_device_info));
809
810 client->device_info.serial_string = strdup(iboot_string);
811
812 char* ptr;
813
814 ptr = strstr(iboot_string, "CPID:");
815 if (ptr != NULL) {
816 sscanf(ptr, "CPID:%x", &client->device_info.cpid);
817 client->device_info.have_cpid = 1;
818 } else {
819 // early iOS 1 doesn't identify itself
820 client->device_info.cpid = 0x8900;
821 }
822
823 ptr = strstr(iboot_string, "CPRV:");
824 if (ptr != NULL) {
825 sscanf(ptr, "CPRV:%x", &client->device_info.cprv);
826 client->device_info.have_cprv = 1;
827 }
828
829 ptr = strstr(iboot_string, "CPFM:");
830 if (ptr != NULL) {
831 sscanf(ptr, "CPFM:%x", &client->device_info.cpfm);
832 client->device_info.have_cpfm = 1;
833 }
834
835 ptr = strstr(iboot_string, "SCEP:");
836 if (ptr != NULL) {
837 sscanf(ptr, "SCEP:%x", &client->device_info.scep);
838 client->device_info.have_scep = 1;
839 }
840
841 ptr = strstr(iboot_string, "BDID:");
842 if (ptr != NULL) {
843 uint64_t bdid = 0;
844 sscanf(ptr, "BDID:%" SCNx64, &bdid);
845 client->device_info.bdid = (unsigned int)bdid;
846 client->device_info.have_bdid = 1;
847 }
848
849 ptr = strstr(iboot_string, "ECID:");
850 if (ptr != NULL) {
851 sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid);
852 client->device_info.have_ecid = 1;
853 }
854
855 ptr = strstr(iboot_string, "IBFL:");
856 if (ptr != NULL) {
857 sscanf(ptr, "IBFL:%x", &client->device_info.ibfl);
858 client->device_info.have_ibfl = 1;
859 }
860
861 char tmp[256];
862 tmp[0] = '\0';
863 ptr = strstr(iboot_string, "SRNM:[");
864 if (ptr != NULL) {
865 sscanf(ptr, "SRNM:[%s]", tmp);
866 ptr = strrchr(tmp, ']');
867 if (ptr != NULL) {
868 *ptr = '\0';
869 }
870 client->device_info.srnm = strdup(tmp);
871 }
872
873 tmp[0] = '\0';
874 ptr = strstr(iboot_string, "IMEI:[");
875 if (ptr != NULL) {
876 sscanf(ptr, "IMEI:[%s]", tmp);
877 ptr = strrchr(tmp, ']');
878 if (ptr != NULL) {
879 *ptr = '\0';
880 }
881 client->device_info.imei = strdup(tmp);
882 }
883
884 tmp[0] = '\0';
885 ptr = strstr(iboot_string, "SRTG:[");
886 if (ptr != NULL) {
887 sscanf(ptr, "SRTG:[%s]", tmp);
888 ptr = strrchr(tmp, ']');
889 if (ptr != NULL) {
890 *ptr = '\0';
891 }
892 client->device_info.srtg = strdup(tmp);
893 }
894
895 client->device_info.pid = client->mode;
896 if (client->isKIS) {
897 client->device_info.pid = KIS_PRODUCT_ID;
898 }
899}
900
901static void irecv_copy_nonce_with_tag_from_buffer(const char* tag, unsigned char** nonce, unsigned int* nonce_size, const char *buf)
902{
903 int taglen = strlen(tag);
904 int nlen = 0;
905 const char* nonce_string = NULL;
906 const char* p = buf;
907 char* colon = NULL;
908 do {
909 colon = strchr(p, ':');
910 if (!colon)
911 break;
912 if (colon-taglen < p) {
913 break;
914 }
915 char *space = strchr(colon, ' ');
916 if (strncmp(colon-taglen, tag, taglen) == 0) {
917 p = colon+1;
918 if (!space) {
919 nlen = strlen(p);
920 } else {
921 nlen = space-p;
922 }
923 nonce_string = p;
924 nlen/=2;
925 break;
926 } else {
927 if (!space) {
928 break;
929 } else {
930 p = space+1;
931 }
932 }
933 } while (colon);
934
935 if (nlen == 0) {
936 debug("%s: WARNING: couldn't find tag %s in string %s\n", __func__, tag, buf);
937 return;
938 }
939
940 unsigned char *nn = malloc(nlen);
941 if (!nn) {
942 return;
943 }
944
945 int i = 0;
946 for (i = 0; i < nlen; i++) {
947 int val = 0;
948 if (sscanf(nonce_string+(i*2), "%02X", &val) == 1) {
949 nn[i] = (unsigned char)val;
950 } else {
951 debug("%s: ERROR: unexpected data in nonce result (%2s)\n", __func__, nonce_string+(i*2));
952 break;
953 }
954 }
955
956 if (i != nlen) {
957 debug("%s: ERROR: unable to parse nonce\n", __func__);
958 free(nn);
959 return;
960 }
961
962 *nonce = nn;
963 *nonce_size = nlen;
964}
965
966static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, unsigned char** nonce, unsigned int* nonce_size)
967{
968 if (!client || !tag) {
969 return;
970 }
971
972 char buf[256];
973 int len = 0;
974
975 *nonce = NULL;
976 *nonce_size = 0;
977
978 memset(buf, 0, sizeof(buf));
979 len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*)buf, sizeof(buf)-1);
980 if (len < 0) {
981 debug("%s: got length: %d\n", __func__, len);
982 return;
983 }
984
985 buf[len] = 0;
986
987 irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf);
988}
989
990#ifndef _WIN32
991static irecv_error_t irecv_kis_request_init(KIS_req_header *hdr, uint8_t portal, uint16_t index, size_t argCount, size_t payloadSize, size_t rplWords)
992{
993 if (argCount > UINT8_MAX) {
994 return IRECV_E_INVALID_INPUT;
995 }
996
997 if (index >= (1 << 10)) {
998 return IRECV_E_INVALID_INPUT;
999 }
1000
1001 if (rplWords >= (1 << 14)) {
1002 return IRECV_E_INVALID_INPUT;
1003 }
1004
1005 size_t reqSize = payloadSize + (argCount << 2);
1006 if (reqSize > UINT32_MAX) {
1007 return IRECV_E_INVALID_INPUT;
1008 }
1009
1010 hdr->sequence = 0; // Doesn't matter
1011 hdr->version = 0xA0;
1012 hdr->portal = portal;
1013 hdr->argCount = (uint8_t) argCount;
1014 hdr->indexLo = (uint8_t) (index & 0xFF);
1015 hdr->indexHiRplSizeLo = (uint8_t) (((index >> 8) & 0x3) | ((rplWords << 2) & 0xFC));
1016 hdr->rplSizeHi = (uint8_t) ((rplWords >> 6) & 0xFF);
1017 hdr->reqSize = (uint32_t) reqSize;
1018
1019 return IRECV_E_SUCCESS;
1020}
1021
1022static irecv_error_t irecv_kis_request(irecv_client_t client, KIS_req_header *req, size_t reqSize, KIS_req_header *rpl, size_t *rplSize)
1023{
1024 int endpoint = 0;
1025 switch (req->portal) {
1026 case KIS_PORTAL_CONFIG:
1027 endpoint = 1;
1028 break;
1029 case KIS_PORTAL_RSM:
1030 endpoint = 3;
1031 break;
1032 default:
1033 debug("Don't know which endpoint to use for portal %d\n", req->portal);
1034 return IRECV_E_INVALID_INPUT;
1035 }
1036
1037 int sent = 0;
1038 irecv_error_t err = irecv_usb_bulk_transfer(client, endpoint, (unsigned char *) req, reqSize, &sent, USB_TIMEOUT);
1039 if (err != IRECV_E_SUCCESS) {
1040 debug("[send] irecv_usb_bulk_transfer failed, error %d\n", err);
1041 return err;
1042 }
1043
1044 if ((size_t) sent != reqSize) {
1045 debug("sent != reqSize\n");
1046 return IRECV_E_USB_UPLOAD;
1047 }
1048
1049 int rcvd = 0;
1050 err = irecv_usb_bulk_transfer(client, endpoint | 0x80, (unsigned char *) rpl, *rplSize, &rcvd, USB_TIMEOUT);
1051 if (err != IRECV_E_SUCCESS) {
1052 debug("[rcv] irecv_usb_bulk_transfer failed, error %d\n", err);
1053 return err;
1054 }
1055
1056 *rplSize = rcvd;
1057
1058 return IRECV_E_SUCCESS;
1059}
1060
1061static irecv_error_t irecv_kis_config_write32(irecv_client_t client, uint8_t portal, uint16_t index, uint32_t value)
1062{
1063 KIS_config_wr32 req = {};
1064 KIS_generic_reply rpl = {};
1065 irecv_error_t err = irecv_kis_request_init(&req.hdr, portal, index, 1, 0, 1);
1066 if (err != IRECV_E_SUCCESS) {
1067 debug("Failed to init KIS request, error %d\n", err);
1068 return err;
1069 }
1070
1071 req.value = value;
1072
1073 size_t rplSize = sizeof(rpl);
1074 err = irecv_kis_request(client, &req.hdr, sizeof(req), &rpl.hdr, &rplSize);
1075 if (err != IRECV_E_SUCCESS) {
1076 debug("Failed to send KIS request, error %d\n", err);
1077 return err;
1078 }
1079
1080 if (rpl.size != 4) {
1081 debug("Failed to write config, %d bytes written, status %d\n", rpl.size, rpl.status);
1082 return err;
1083 }
1084
1085 return IRECV_E_SUCCESS;
1086}
1087
1088static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, size_t buf_size)
1089{
1090 off *= 4;
1091
1092 size_t inputSize = sizeof(KIS_device_info) - sizeof(KIS_req_header);
1093
1094 if ((off + 2) > inputSize)
1095 return 0;
1096
1097 uint8_t len = di->deviceInfo[off];
1098 uint8_t type = di->deviceInfo[off + 1];
1099
1100 if (len & 1)
1101 return 0;
1102
1103 if (len/2 >= buf_size)
1104 return 0;
1105
1106 if ((off + 2 + len) > inputSize)
1107 return 0;
1108
1109 if (type != 3)
1110 return 0;
1111
1112 buf[len >> 1] = 0;
1113 for (size_t i = 0; i < len; i += 2) {
1114 buf[i >> 1] = di->deviceInfo[i + off + 2];
1115 }
1116
1117 return len/2;
1118}
1119#endif
1120
1121static irecv_error_t irecv_kis_init(irecv_client_t client)
1122{
1123#ifndef _WIN32
1124 irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL);
1125 if (err != IRECV_E_SUCCESS) {
1126 debug("Failed to write to KIS_INDEX_ENABLE_A, error %d\n", err);
1127 return err;
1128 }
1129
1130 err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_B, KIS_ENABLE_B_VAL);
1131 if (err != IRECV_E_SUCCESS) {
1132 debug("Failed to write to KIS_INDEX_ENABLE_B, error %d\n", err);
1133 return err;
1134 }
1135#endif
1136 client->isKIS = 1;
1137
1138 return IRECV_E_SUCCESS;
1139}
1140
1141static irecv_error_t irecv_kis_load_device_info(irecv_client_t client)
1142{
1143 debug("Loading device info in KIS mode...\n");
1144#ifdef _WIN32
1145 KIS_device_info kisInfo;
1146 DWORD transferred = 0;
1147 int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL);
1148 if (ret) {
1149 debug("Serial: %s\n", kisInfo.serial);
1150 irecv_load_device_info_from_iboot_string(client, kisInfo.serial);
1151 debug("Manufacturer: %s\n", kisInfo.manufacturer);
1152 debug("Product: %s\n", kisInfo.product);
1153 debug("Nonces: %s\n", kisInfo.nonces);
1154 irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, kisInfo.nonces);
1155 irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, kisInfo.nonces);
1156 debug("VID: 0x%04x\n", kisInfo.vid);
1157 debug("PID: 0x%04x\n", kisInfo.pid);
1158 }
1159 client->mode = kisInfo.pid;
1160#else
1161 KIS_req_header req = {};
1162 KIS_device_info di = {};
1163 irecv_error_t err = irecv_kis_request_init(&req, KIS_PORTAL_RSM, KIS_INDEX_GET_INFO, 0, 0, sizeof(di.deviceInfo)/4);
1164 if (err != IRECV_E_SUCCESS) {
1165 debug("Failed to init KIS request, error %d\n", err);
1166 return err;
1167 }
1168
1169 size_t rcvSize = sizeof(di);
1170 err = irecv_kis_request(client, &req, sizeof(req), &di.hdr, &rcvSize);
1171 if (err != IRECV_E_SUCCESS) {
1172 debug("Failed to send KIS request, error %d\n", err);
1173 return err;
1174 }
1175
1176 char buf[0x100];
1177 int len = 0;
1178
1179 len = irecv_kis_read_string(&di, di.deviceDescriptor.iSerialNumber, buf, sizeof(buf));
1180 if (len == 0)
1181 return IRECV_E_INVALID_INPUT;
1182 debug("Serial: %s\n", buf);
1183
1184 irecv_load_device_info_from_iboot_string(client, buf);
1185
1186 len = irecv_kis_read_string(&di, di.deviceDescriptor.iManufacturer, buf, sizeof(buf));
1187 if (len == 0)
1188 return IRECV_E_INVALID_INPUT;
1189 debug("Manufacturer: %s\n", buf);
1190
1191 len = irecv_kis_read_string(&di, di.deviceDescriptor.iProduct, buf, sizeof(buf));
1192 if (len == 0)
1193 return IRECV_E_INVALID_INPUT;
1194 debug("Product: %s\n", buf);
1195
1196 len = irecv_kis_read_string(&di, di.nonceOffset, buf, sizeof(buf));
1197 if (len == 0)
1198 return IRECV_E_INVALID_INPUT;
1199 debug("Nonces: %s\n", buf);
1200
1201 irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, buf);
1202 irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, buf);
1203
1204 debug("VID: 0x%04x\n", di.deviceDescriptor.idVendor);
1205 debug("PID: 0x%04x\n", di.deviceDescriptor.idProduct);
1206
1207 client->mode = di.deviceDescriptor.idProduct;
1208#endif
1209 return IRECV_E_SUCCESS;
1210}
1211
1212#ifdef _WIN32
1213static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
1214static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
1215static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}};
1216static const GUID GUID_DEVINTERFACE_PORTDFU = {0xAF633FF1L, 0x1170, 0x4CA6, {0xAE, 0x9E, 0x08, 0xD0, 0x01, 0x42, 0x1E, 0xAA}};
1217
1218typedef struct usb_control_request {
1219 uint8_t bmRequestType;
1220 uint8_t bRequest;
1221 uint16_t wValue;
1222 uint16_t wIndex;
1223 uint16_t wLength;
1224
1225 char data[];
1226} usb_control_request;
1227
1228static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid)
1229{
1230 int found = 0;
1231 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
1232 irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
1233 memset(_client, 0, sizeof(struct irecv_client_private));
1234
1235 int k;
1236 for (k = 0; !found && guids[k]; k++) {
1237 DWORD i;
1238 SP_DEVICE_INTERFACE_DATA currentInterface;
1239 HDEVINFO usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
1240 memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
1241 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1242 for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
1243 _client->handle = INVALID_HANDLE_VALUE;
1244 DWORD requiredSize = 0;
1245 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
1246 SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
1247 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A) malloc(requiredSize);
1248 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
1249 if (!SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, details, requiredSize, NULL, NULL)) {
1250 free(details);
1251 continue;
1252 }
1253
1254 unsigned int pid = 0;
1255 unsigned int vid = 0;
1256 if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid) != 2) {
1257 debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
1258 free(details);
1259 continue;
1260 }
1261 if (vid != APPLE_VENDOR_ID) {
1262 free(details);
1263 continue;
1264 }
1265
1266 // make sure the current device is actually in the right mode for the given driver interface
1267 if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
1268 || (guids[k] == &GUID_DEVINTERFACE_PORTDFU && pid != IRECV_K_PORT_DFU_MODE)
1269 || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
1270 || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
1271 ) {
1272 free(details);
1273 continue;
1274 }
1275 if (guids[k] == &GUID_DEVINTERFACE_KIS) {
1276 pid = KIS_PRODUCT_ID;
1277 }
1278
1279 _client->handle = CreateFileA(details->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
1280 if (_client->handle == INVALID_HANDLE_VALUE) {
1281 debug("%s: Failed to open device path %s: %d\n", __func__, details->DevicePath, (int)GetLastError());
1282 free(details);
1283 continue;
1284 }
1285 _client->mode = pid;
1286
1287 if (ecid == IRECV_K_WTF_MODE) {
1288 if (_client->mode != IRECV_K_WTF_MODE) {
1289 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1290 CloseHandle(_client->handle);
1291 free(details);
1292 continue;
1293 } else {
1294 ecid = 0;
1295 }
1296 }
1297
1298 if ((ecid != 0) && (_client->mode == IRECV_K_WTF_MODE)) {
1299 /* we can't get ecid in WTF mode */
1300 CloseHandle(_client->handle);
1301 free(details);
1302 continue;
1303 }
1304
1305 char serial_str[256];
1306 serial_str[0] = '\0';
1307
1308 if (_client->mode != KIS_PRODUCT_ID) {
1309 char *p = (char*)details->DevicePath;
1310 while ((p = strstr(p, "\\usb"))) {
1311 if (sscanf(p, "\\usb#vid_05ac&pid_%*04x#%s", serial_str) == 1)
1312 break;
1313 p += 4;
1314 }
1315 free(details);
1316
1317 if (serial_str[0] == '\0') {
1318 CloseHandle(_client->handle);
1319 continue;
1320 }
1321 p = strchr(serial_str, '#');
1322 if (p) {
1323 *p = '\0';
1324 }
1325
1326 unsigned int j;
1327 for (j = 0; j < strlen(serial_str); j++) {
1328 if (serial_str[j] == '_') {
1329 serial_str[j] = ' ';
1330 } else {
1331 serial_str[j] = toupper(serial_str[j]);
1332 }
1333 }
1334
1335 irecv_load_device_info_from_iboot_string(_client, serial_str);
1336 }
1337
1338 if (ecid != 0 && _client->mode != KIS_PRODUCT_ID) {
1339 if (_client->device_info.ecid != ecid) {
1340 CloseHandle(_client->handle);
1341 continue;
1342 }
1343 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid);
1344 }
1345 found = 1;
1346 break;
1347 }
1348 SetupDiDestroyDeviceInfoList(usbDevices);
1349 }
1350
1351 if (!found) {
1352 irecv_close(_client);
1353 return IRECV_E_UNABLE_TO_CONNECT;
1354 }
1355
1356 *client = _client;
1357
1358 return IRECV_E_SUCCESS;
1359}
1360#endif
1361
1362#ifdef HAVE_IOKIT
1363static void iokit_cfdictionary_set_short(CFMutableDictionaryRef dict, const void *key, SInt16 value)
1364{
1365 CFNumberRef numberRef;
1366
1367 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &value);
1368 if (numberRef) {
1369 CFDictionarySetValue(dict, key, numberRef);
1370 CFRelease(numberRef);
1371 }
1372}
1373#endif
1374
1375static int check_context(irecv_client_t client)
1376{
1377 if (client == NULL || client->handle == NULL) {
1378 return IRECV_E_NO_DEVICE;
1379 }
1380
1381 return IRECV_E_SUCCESS;
1382}
1383#endif
1384
1385#ifndef USE_DUMMY
1386#ifdef HAVE_IOKIT
1387static int iokit_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout)
1388{
1389 IOReturn result;
1390 IOUSBDevRequestTO req;
1391
1392 bzero(&req, sizeof(req));
1393 req.bmRequestType = bm_request_type;
1394 req.bRequest = b_request;
1395 req.wValue = OSSwapLittleToHostInt16(w_value);
1396 req.wIndex = OSSwapLittleToHostInt16(w_index);
1397 req.wLength = OSSwapLittleToHostInt16(w_length);
1398 req.pData = data;
1399 req.noDataTimeout = timeout;
1400 req.completionTimeout = timeout;
1401
1402 result = (*client->handle)->DeviceRequestTO(client->handle, &req);
1403 switch (result) {
1404 case kIOReturnSuccess: return req.wLenDone;
1405 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
1406 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
1407 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
1408 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1409 default:
1410 return IRECV_E_UNKNOWN_ERROR;
1411 }
1412}
1413#else
1414#ifdef __APPLE__
1415 void dummy_callback(void) { }
1416#endif
1417#endif
1418#endif
1419
1420int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout)
1421{
1422#ifdef USE_DUMMY
1423 return IRECV_E_UNSUPPORTED;
1424#else
1425#ifndef _WIN32
1426#ifdef HAVE_IOKIT
1427 return iokit_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1428#else
1429 return libusb_control_transfer(client->handle, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1430#endif
1431#else
1432 DWORD count = 0;
1433 BOOL bRet;
1434 OVERLAPPED overlapped;
1435
1436 if (data == NULL)
1437 w_length = 0;
1438
1439 usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + w_length);
1440 packet->bmRequestType = bm_request_type;
1441 packet->bRequest = b_request;
1442 packet->wValue = w_value;
1443 packet->wIndex = w_index;
1444 packet->wLength = w_length;
1445
1446 if (bm_request_type < 0x80 && w_length > 0) {
1447 memcpy(packet->data, data, w_length);
1448 }
1449
1450 memset(&overlapped, 0, sizeof(overlapped));
1451 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1452 DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + w_length, packet, sizeof(usb_control_request) + w_length, NULL, &overlapped);
1453 WaitForSingleObject(overlapped.hEvent, timeout);
1454 bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE);
1455 CloseHandle(overlapped.hEvent);
1456 if (!bRet) {
1457 CancelIo(client->handle);
1458 free(packet);
1459 return -1;
1460 }
1461
1462 count -= sizeof(usb_control_request);
1463 if (count > 0) {
1464 if (bm_request_type >= 0x80) {
1465 memcpy(data, packet->data, count);
1466 }
1467 }
1468 free(packet);
1469
1470 return count;
1471#endif
1472#endif
1473}
1474
1475#ifndef USE_DUMMY
1476#ifdef HAVE_IOKIT
1477static int iokit_usb_bulk_transfer(irecv_client_t client,
1478 unsigned char endpoint,
1479 unsigned char *data,
1480 int length,
1481 int *transferred,
1482 unsigned int timeout)
1483{
1484 IOReturn result;
1485 IOUSBInterfaceInterface300 **intf = client->usbInterface;
1486 UInt32 size = length;
1487 UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
1488 UInt8 numEndpoints;
1489
1490 if (!intf) return IRECV_E_USB_INTERFACE;
1491
1492 result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
1493
1494 if (result != kIOReturnSuccess)
1495 return IRECV_E_USB_INTERFACE;
1496
1497 for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
1498 UInt8 direction = 0;
1499 UInt8 number = 0;
1500 UInt8 transferType = 0;
1501 UInt16 maxPacketSize = 0;
1502 UInt8 interval = 0;
1503
1504 result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
1505 if (result != kIOReturnSuccess)
1506 continue;
1507
1508 if (direction == 3)
1509 direction = isUSBIn;
1510
1511 if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
1512 continue;
1513
1514 // Just because
1515 result = (*intf)->GetPipeStatus(intf, pipeRef);
1516 switch (result) {
1517 case kIOReturnSuccess: break;
1518 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1519 case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
1520 default: return IRECV_E_USB_STATUS;
1521 }
32 1522
33int irecv_write_file(const char* filename, const void* data, size_t size); 1523 // Do the transfer
34int irecv_read_file(const char* filename, char** data, uint32_t* size); 1524 if (isUSBIn) {
1525 result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
1526 if (result != kIOReturnSuccess)
1527 return IRECV_E_PIPE;
1528 *transferred = size;
1529
1530 return IRECV_E_SUCCESS;
1531 }
1532 else {
1533 // IOUSBInterfaceClass::interfaceWritePipe (intf?, pipeRef==1, data, size=0x8000)
1534 result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
1535 if (result != kIOReturnSuccess)
1536 return IRECV_E_PIPE;
1537 *transferred = size;
1538
1539 return IRECV_E_SUCCESS;
1540 }
1541 }
1542
1543 return IRECV_E_USB_INTERFACE;
1544}
1545
1546static int iokit_usb_interrupt_transfer(irecv_client_t client,
1547 unsigned char endpoint,
1548 unsigned char *data,
1549 int length,
1550 int *transferred,
1551 unsigned int timeout)
1552{
1553 IOReturn result;
1554 IOUSBInterfaceInterface300 **intf = client->usbInterface;
1555 UInt32 size = length;
1556 UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
1557 UInt8 numEndpoints;
1558
1559 if (!intf) return IRECV_E_USB_INTERFACE;
1560
1561 result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
1562
1563 if (result != kIOReturnSuccess)
1564 return IRECV_E_USB_INTERFACE;
1565
1566 for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
1567 UInt8 direction = 0;
1568 UInt8 number = 0;
1569 UInt8 transferType = 0;
1570 UInt16 maxPacketSize = 0;
1571 UInt8 interval = 0;
1572
1573 result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
1574 if (result != kIOReturnSuccess)
1575 continue;
1576
1577 if (direction == 3)
1578 direction = isUSBIn;
1579
1580 if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
1581 continue;
1582
1583 // Just because
1584 result = (*intf)->GetPipeStatus(intf, pipeRef);
1585 switch (result) {
1586 case kIOReturnSuccess: break;
1587 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1588 case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
1589 default: return IRECV_E_USB_STATUS;
1590 }
1591
1592 // Do the transfer
1593 if (isUSBIn) {
1594 result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
1595 if (result != kIOReturnSuccess)
1596 return IRECV_E_PIPE;
1597 *transferred = size;
1598
1599 return IRECV_E_SUCCESS;
1600 }
1601 else {
1602 result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
1603 if (result != kIOReturnSuccess)
1604 return IRECV_E_PIPE;
1605 *transferred = size;
1606
1607 return IRECV_E_SUCCESS;
1608 }
1609 }
1610
1611 return IRECV_E_USB_INTERFACE;
1612}
1613#endif
1614#endif
1615
1616int irecv_usb_bulk_transfer(irecv_client_t client,
1617 unsigned char endpoint,
1618 unsigned char *data,
1619 int length,
1620 int *transferred,
1621 unsigned int timeout)
1622{
1623#ifdef USE_DUMMY
1624 return IRECV_E_UNSUPPORTED;
1625#else
1626 int ret;
1627
1628#ifndef _WIN32
1629#ifdef HAVE_IOKIT
1630 return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout);
1631#else
1632 ret = libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout);
1633 if (ret < 0) {
1634 libusb_clear_halt(client->handle, endpoint);
1635 }
1636#endif
1637#else
1638 if (endpoint==0x4) {
1639 ret = DeviceIoControl(client->handle, 0x2201B6, data, length, data, length, (PDWORD) transferred, NULL);
1640 } else {
1641 ret = 0;
1642 }
1643 ret = (ret==0) ? -1 : 0;
1644#endif
1645
1646 return ret;
1647#endif
1648}
1649
1650IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client,
1651 unsigned char endpoint,
1652 unsigned char *data,
1653 int length,
1654 int *transferred,
1655 unsigned int timeout)
1656{
1657#ifdef USE_DUMMY
1658 return IRECV_E_UNSUPPORTED;
1659#else
1660 int ret;
1661
1662#ifndef _WIN32
1663#ifdef HAVE_IOKIT
1664 return iokit_usb_interrupt_transfer(client, endpoint, data, length, transferred, timeout);
1665#else
1666 ret = libusb_interrupt_transfer(client->handle, endpoint, data, length, transferred, timeout);
1667 if (ret < 0) {
1668 libusb_clear_halt(client->handle, endpoint);
1669 }
1670#endif
1671#else
1672 // win32
1673 return IRECV_E_UNSUPPORTED;
1674#endif
1675
1676 return ret;
1677#endif
1678}
1679
1680#ifndef USE_DUMMY
1681#ifdef HAVE_IOKIT
1682static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service)
1683{
1684 IOReturn result;
1685 irecv_client_t client;
1686 SInt32 score;
1687 UInt16 mode;
1688 UInt32 locationID;
1689 IOCFPlugInInterface **plug = NULL;
1690 CFStringRef serialString;
1691
1692 client = (irecv_client_t) calloc( 1, sizeof(struct irecv_client_private));
1693
1694 // Create the plug-in
1695 result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plug, &score);
1696 if (result != kIOReturnSuccess) {
1697 IOObjectRelease(service);
1698 free(client);
1699 return IRECV_E_UNKNOWN_ERROR;
1700 }
1701
1702 // Cache the serial string before discarding the service. The service object
1703 // has a cached copy, so a request to the hardware device is not required.
1704 char serial_str[256];
1705 serial_str[0] = '\0';
1706 serialString = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
1707 if (serialString) {
1708 CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
1709 CFRelease(serialString);
1710 }
1711 irecv_load_device_info_from_iboot_string(client, serial_str);
1712
1713 IOObjectRelease(service);
1714
1715 // Create the device interface
1716 result = (*plug)->QueryInterface(plug, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&(client->handle));
1717 IODestroyPlugInInterface(plug);
1718 if (result != kIOReturnSuccess) {
1719 free(client);
1720 return IRECV_E_UNKNOWN_ERROR;
1721 }
1722
1723 (*client->handle)->GetDeviceProduct(client->handle, &mode);
1724 (*client->handle)->GetLocationID(client->handle, &locationID);
1725 client->mode = mode;
1726 debug("opening device %04x:%04x @ %#010x...\n", kAppleVendorID, client->mode, locationID);
1727
1728 result = (*client->handle)->USBDeviceOpenSeize(client->handle);
1729 if (result != kIOReturnSuccess) {
1730 (*client->handle)->Release(client->handle);
1731 free(client);
1732 return IRECV_E_UNABLE_TO_CONNECT;
1733 }
1734
1735 *pclient = client;
1736 return IRECV_E_SUCCESS;
1737}
35 1738
36irecv_error_t irecv_open(irecv_client_t* pclient) { 1739static io_iterator_t iokit_usb_get_iterator_for_pid(UInt16 pid)
1740{
1741 IOReturn result;
1742 io_iterator_t iterator;
1743 CFMutableDictionaryRef matchingDict;
1744
1745 matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
1746 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
1747 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pid);
1748
1749 result = IOServiceGetMatchingServices(MACH_PORT_NULL, matchingDict, &iterator);
1750 if (result != kIOReturnSuccess)
1751 return IO_OBJECT_NULL;
1752
1753 return iterator;
1754}
1755
1756static irecv_error_t iokit_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1757{
1758 io_service_t service, ret_service;
1759 io_iterator_t iterator;
1760 CFStringRef usbSerial = NULL;
1761 CFStringRef ecidString = NULL;
1762 CFRange range;
1763
1764 UInt16 wtf_pids[] = { IRECV_K_WTF_MODE, 0};
1765 UInt16 all_pids[] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_PORT_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, KIS_PRODUCT_ID, 0 };
1766 UInt16 *pids = all_pids;
1767 int i;
1768
1769 if (pclient == NULL) {
1770 debug("%s: pclient parameter is null\n", __func__);
1771 return IRECV_E_INVALID_INPUT;
1772 }
1773 if (ecid == IRECV_K_WTF_MODE) {
1774 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1775 pids = wtf_pids;
1776 ecid = 0;
1777 }
1778 if (ecid > 0) {
1779 ecidString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%llX"), ecid);
1780 if (ecidString == NULL) {
1781 debug("%s: failed to create ECID string\n", __func__);
1782 return IRECV_E_UNABLE_TO_CONNECT;
1783 }
1784 }
1785
1786 *pclient = NULL;
1787 ret_service = IO_OBJECT_NULL;
1788
1789 for (i = 0; (pids[i] > 0 && ret_service == IO_OBJECT_NULL) ; i++) {
1790
1791 iterator = iokit_usb_get_iterator_for_pid(pids[i]);
1792 if (iterator) {
1793 while ((service = IOIteratorNext(iterator))) {
1794
1795 if (ecid == 0) {
1796 ret_service = service;
1797 break;
1798 }
1799
1800 if (pids[i] == KIS_PRODUCT_ID) {
1801 // In KIS Mode, we have to open the device in order to get
1802 // it's ECID
1803 irecv_error_t err = iokit_usb_open_service(pclient, service);
1804 if (err != IRECV_E_SUCCESS) {
1805 debug("%s: failed to open KIS device\n", __func__);
1806 continue;
1807 }
1808
1809 if (ecidString)
1810 CFRelease(ecidString);
1811
1812 return IRECV_E_SUCCESS;
1813 }
1814
1815 usbSerial = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
1816 if (usbSerial == NULL) {
1817 debug("%s: failed to create USB serial string property\n", __func__);
1818 IOObjectRelease(service);
1819 continue;
1820 }
1821
1822 range = CFStringFind(usbSerial, ecidString, kCFCompareCaseInsensitive);
1823 if (range.location == kCFNotFound) {
1824 IOObjectRelease(service);
1825 } else {
1826 ret_service = service;
1827 break;
1828 }
1829 }
1830 if (usbSerial) {
1831 CFRelease(usbSerial);
1832 usbSerial = NULL;
1833 }
1834 IOObjectRelease(iterator);
1835 }
1836 }
1837
1838 if (ecidString)
1839 CFRelease(ecidString);
1840
1841 if (ret_service == IO_OBJECT_NULL)
1842 return IRECV_E_UNABLE_TO_CONNECT;
1843
1844 return iokit_usb_open_service(pclient, ret_service);
1845}
1846#endif
1847
1848#ifndef _WIN32
1849#ifndef HAVE_IOKIT
1850static irecv_error_t libusb_usb_open_handle_with_descriptor_and_ecid(irecv_client_t *pclient, struct libusb_device_handle *usb_handle, struct libusb_device_descriptor *usb_descriptor, uint64_t ecid)
1851{
1852 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
1853 if (client == NULL) {
1854 libusb_close(usb_handle);
1855 return IRECV_E_OUT_OF_MEMORY;
1856 }
1857
1858 memset(client, '\0', sizeof(struct irecv_client_private));
1859 client->usb_interface = 0;
1860 client->handle = usb_handle;
1861 client->mode = usb_descriptor->idProduct;
1862
1863 if (client->mode != KIS_PRODUCT_ID) {
1864 char serial_str[256];
1865 memset(serial_str, 0, sizeof(serial_str));
1866 irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
1867 irecv_load_device_info_from_iboot_string(client, serial_str);
1868 }
1869
1870 if (ecid != 0 && client->mode != KIS_PRODUCT_ID) {
1871 if (client->device_info.ecid != ecid) {
1872 irecv_close(client);
1873 return IRECV_E_NO_DEVICE; //wrong device
1874 }
1875 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid);
1876 }
1877
1878 *pclient = client;
1879 return IRECV_E_SUCCESS;
1880}
1881
1882static irecv_error_t libusb_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1883{
1884 irecv_error_t ret = IRECV_E_UNABLE_TO_CONNECT;
37 int i = 0; 1885 int i = 0;
38 char serial[256];
39 struct libusb_device* usb_device = NULL; 1886 struct libusb_device* usb_device = NULL;
40 struct libusb_device** usb_device_list = NULL; 1887 struct libusb_device** usb_device_list = NULL;
41 struct libusb_device_handle* usb_handle = NULL;
42 struct libusb_device_descriptor usb_descriptor; 1888 struct libusb_device_descriptor usb_descriptor;
43 1889
44 *pclient = NULL; 1890 *pclient = NULL;
45 libusb_init(&libirecovery_context);
46 if(libirecovery_debug) {
47 irecv_set_debug_level(libirecovery_debug);
48 }
49
50 irecv_error_t error = IRECV_E_SUCCESS;
51 int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list); 1891 int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list);
52 for (i = 0; i < usb_device_count; i++) { 1892 for (i = 0; i < usb_device_count; i++) {
53 usb_device = usb_device_list[i]; 1893 usb_device = usb_device_list[i];
54 libusb_get_device_descriptor(usb_device, &usb_descriptor); 1894 libusb_get_device_descriptor(usb_device, &usb_descriptor);
55 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) { 1895 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) {
56 /* verify this device is in a mode we understand */ 1896 /* verify this device is in a mode we understand */
57 if (usb_descriptor.idProduct == kRecoveryMode1 || 1897 if (usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_1 ||
58 usb_descriptor.idProduct == kRecoveryMode2 || 1898 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_2 ||
59 usb_descriptor.idProduct == kRecoveryMode3 || 1899 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_3 ||
60 usb_descriptor.idProduct == kRecoveryMode4 || 1900 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_4 ||
61 usb_descriptor.idProduct == kDfuMode) { 1901 usb_descriptor.idProduct == IRECV_K_WTF_MODE ||
1902 usb_descriptor.idProduct == IRECV_K_DFU_MODE ||
1903 usb_descriptor.idProduct == IRECV_K_PORT_DFU_MODE ||
1904 usb_descriptor.idProduct == KIS_PRODUCT_ID) {
1905
1906 if (ecid == IRECV_K_WTF_MODE) {
1907 if (usb_descriptor.idProduct != IRECV_K_WTF_MODE) {
1908 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1909 continue;
1910 } else {
1911 ecid = 0;
1912 }
1913 }
1914
1915 if ((ecid != 0) && (usb_descriptor.idProduct == IRECV_K_WTF_MODE)) {
1916 /* we can't get ecid in WTF mode */
1917 continue;
1918 }
62 1919
63 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct); 1920 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct);
64 1921
65 libusb_open(usb_device, &usb_handle); 1922 struct libusb_device_handle* usb_handle = NULL;
66 if (usb_handle == NULL) { 1923 int libusb_error = libusb_open(usb_device, &usb_handle);
67 libusb_free_device_list(usb_device_list, 1); 1924 if (usb_handle == NULL || libusb_error != 0) {
1925 debug("%s: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
1926
68 libusb_close(usb_handle); 1927 libusb_close(usb_handle);
69 libusb_exit(libirecovery_context); 1928 if (ecid != 0) {
1929 continue;
1930 }
1931 libusb_free_device_list(usb_device_list, 1);
70 return IRECV_E_UNABLE_TO_CONNECT; 1932 return IRECV_E_UNABLE_TO_CONNECT;
71 } 1933 }
72 libusb_free_device_list(usb_device_list, 1);
73 1934
74 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client)); 1935 ret = libusb_usb_open_handle_with_descriptor_and_ecid(pclient, usb_handle, &usb_descriptor, ecid);
75 if (client == NULL) { 1936 if (ret == IRECV_E_SUCCESS) {
76 libusb_close(usb_handle); 1937 break;
77 libusb_exit(libirecovery_context);
78 return IRECV_E_OUT_OF_MEMORY;
79 } 1938 }
1939 }
1940 }
1941 }
1942 libusb_free_device_list(usb_device_list, 1);
1943 return ret;
1944}
1945#endif
1946#endif
1947#endif
1948
1949irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1950{
1951#ifdef USE_DUMMY
1952 return IRECV_E_UNSUPPORTED;
1953#else
1954 irecv_error_t error = IRECV_E_UNABLE_TO_CONNECT;
1955
1956 if (libirecovery_debug) {
1957 irecv_set_debug_level(libirecovery_debug);
1958 }
1959#ifndef _WIN32
1960#ifdef HAVE_IOKIT
1961 error = iokit_open_with_ecid(pclient, ecid);
1962#else
1963 error = libusb_open_with_ecid(pclient, ecid);
1964#endif
1965#else
1966 error = win32_open_with_ecid(pclient, ecid);
1967#endif
1968 irecv_client_t client = *pclient;
1969 if (error != IRECV_E_SUCCESS) {
1970 irecv_close(client);
1971 return error;
1972 }
80 1973
81 memset(client, '\0', sizeof(struct irecv_client)); 1974 error = irecv_usb_set_configuration(client, 1);
82 client->interface = 0; 1975 if (error != IRECV_E_SUCCESS) {
83 client->handle = usb_handle; 1976 debug("Failed to set configuration, error %d\n", error);
84 client->mode = usb_descriptor.idProduct; 1977 irecv_close(client);
1978 return error;
1979 }
85 1980
86 error = irecv_set_configuration(client, 1); 1981 if (client->mode == IRECV_K_DFU_MODE || client->mode == IRECV_K_PORT_DFU_MODE || client->mode == IRECV_K_WTF_MODE || client->mode == KIS_PRODUCT_ID) {
87 if (error != IRECV_E_SUCCESS) { 1982 error = irecv_usb_set_interface(client, 0, 0);
88 return error; 1983 } else {
89 } 1984 error = irecv_usb_set_interface(client, 0, 0);
1985 if (error == IRECV_E_SUCCESS && client->mode > IRECV_K_RECOVERY_MODE_2) {
1986 error = irecv_usb_set_interface(client, 1, 1);
1987 }
1988 }
90 1989
91 error = irecv_set_interface(client, 1, 1); 1990 if (error != IRECV_E_SUCCESS) {
92 if (error != IRECV_E_SUCCESS) { 1991 debug("Failed to set interface, error %d\n", error);
93 return error; 1992 irecv_close(client);
94 } 1993 return error;
1994 }
95 1995
96 /* cache usb serial */ 1996 if (client->mode == KIS_PRODUCT_ID) {
97 libusb_get_string_descriptor_ascii(client->handle, usb_descriptor.iSerialNumber, client->serial, 255); 1997 error = irecv_kis_init(client);
1998 if (error != IRECV_E_SUCCESS) {
1999 debug("irecv_kis_init failed, error %d\n", error);
2000 irecv_close(client);
2001 return error;
2002 }
98 2003
99 *pclient = client; 2004 error = irecv_kis_load_device_info(client);
100 return IRECV_E_SUCCESS; 2005 if (error != IRECV_E_SUCCESS) {
101 } 2006 debug("irecv_kis_load_device_info failed, error %d\n", error);
2007 irecv_close(client);
2008 return error;
102 } 2009 }
2010 if (ecid != 0 && client->device_info.ecid != ecid) {
2011 irecv_close(client);
2012 return IRECV_E_NO_DEVICE; //wrong device
2013 }
2014 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)client->device_info.ecid);
2015 } else {
2016 irecv_copy_nonce_with_tag(client, "NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size);
2017 irecv_copy_nonce_with_tag(client, "SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size);
103 } 2018 }
104 2019
105 return IRECV_E_UNABLE_TO_CONNECT; 2020 if (error == IRECV_E_SUCCESS) {
2021 if ((*pclient)->connected_callback != NULL) {
2022 irecv_event_t event;
2023 event.size = 0;
2024 event.data = NULL;
2025 event.progress = 0;
2026 event.type = IRECV_CONNECTED;
2027 (*pclient)->connected_callback(*pclient, &event);
2028 }
2029 }
2030 return error;
2031#endif
106} 2032}
107 2033
108irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) { 2034irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configuration)
109 if (client == NULL || client->handle == NULL) { 2035{
2036#ifdef USE_DUMMY
2037 return IRECV_E_UNSUPPORTED;
2038#else
2039 if (check_context(client) != IRECV_E_SUCCESS)
110 return IRECV_E_NO_DEVICE; 2040 return IRECV_E_NO_DEVICE;
111 }
112 2041
2042#ifndef _WIN32
113 debug("Setting to configuration %d\n", configuration); 2043 debug("Setting to configuration %d\n", configuration);
114 2044
2045#ifdef HAVE_IOKIT
2046 IOReturn result;
2047
2048 result = (*client->handle)->SetConfiguration(client->handle, configuration);
2049 if (result != kIOReturnSuccess) {
2050 debug("error setting configuration: %#x\n", result);
2051 return IRECV_E_USB_CONFIGURATION;
2052 }
2053#else
115 int current = 0; 2054 int current = 0;
116 libusb_get_configuration(client->handle, &current); 2055 libusb_get_configuration(client->handle, &current);
117 if (current != configuration) { 2056 if (current != configuration) {
@@ -119,45 +2058,199 @@ irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration)
119 return IRECV_E_USB_CONFIGURATION; 2058 return IRECV_E_USB_CONFIGURATION;
120 } 2059 }
121 } 2060 }
2061#endif
2062 client->usb_config = configuration;
2063#endif
122 2064
123 client->config = configuration;
124 return IRECV_E_SUCCESS; 2065 return IRECV_E_SUCCESS;
2066#endif
125} 2067}
126 2068
127irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) { 2069#ifndef USE_DUMMY
128 if (client == NULL || client->handle == NULL) { 2070#ifdef HAVE_IOKIT
129 return IRECV_E_NO_DEVICE; 2071static IOReturn iokit_usb_get_interface(IOUSBDeviceInterface320 **device, uint8_t ifc, io_service_t *usbInterfacep)
2072{
2073 IOUSBFindInterfaceRequest request;
2074 uint8_t current_interface;
2075 kern_return_t kresult;
2076 io_iterator_t interface_iterator;
2077
2078 *usbInterfacep = IO_OBJECT_NULL;
2079
2080 request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
2081 request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
2082 request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
2083 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
2084
2085 kresult = (*device)->CreateInterfaceIterator(device, &request, &interface_iterator);
2086 if (kresult)
2087 return kresult;
2088
2089 for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) {
2090 *usbInterfacep = IOIteratorNext(interface_iterator);
2091 if (current_interface != ifc)
2092 (void) IOObjectRelease (*usbInterfacep);
130 } 2093 }
2094 IOObjectRelease(interface_iterator);
131 2095
132 if (client->interface == interface) { 2096 return kIOReturnSuccess;
133 return IRECV_E_SUCCESS; 2097}
2098
2099static irecv_error_t iokit_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface)
2100{
2101 IOReturn result;
2102 io_service_t interface_service = IO_OBJECT_NULL;
2103 IOCFPlugInInterface **plugInInterface = NULL;
2104 SInt32 score;
2105
2106 // Close current interface
2107 if (client->usbInterface) {
2108 result = (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
2109 result = (*client->usbInterface)->Release(client->usbInterface);
2110 client->usbInterface = NULL;
2111 }
2112
2113 result = iokit_usb_get_interface(client->handle, usb_interface, &interface_service);
2114 if (result != kIOReturnSuccess) {
2115 debug("failed to find requested interface: %d\n", usb_interface);
2116 return IRECV_E_USB_INTERFACE;
134 } 2117 }
135 2118
136 debug("Setting to interface %d:%d\n", interface, alt_interface); 2119 result = IOCreatePlugInInterfaceForService(interface_service, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
137 if (libusb_claim_interface(client->handle, interface) < 0) { 2120 IOObjectRelease(interface_service);
2121 if (result != kIOReturnSuccess) {
2122 debug("error creating plug-in interface: %#x\n", result);
138 return IRECV_E_USB_INTERFACE; 2123 return IRECV_E_USB_INTERFACE;
139 } 2124 }
140 2125
141 if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) { 2126 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300), (LPVOID)&client->usbInterface);
2127 IODestroyPlugInInterface(plugInInterface);
2128 if (result != kIOReturnSuccess) {
2129 debug("error creating interface interface: %#x\n", result);
142 return IRECV_E_USB_INTERFACE; 2130 return IRECV_E_USB_INTERFACE;
143 } 2131 }
144 2132
145 client->interface = interface; 2133 result = (*client->usbInterface)->USBInterfaceOpen(client->usbInterface);
146 client->alt_interface = alt_interface; 2134 if (result != kIOReturnSuccess) {
2135 debug("error opening interface: %#x\n", result);
2136 return IRECV_E_USB_INTERFACE;
2137 }
2138
2139 if (usb_interface == 1) {
2140 result = (*client->usbInterface)->SetAlternateInterface(client->usbInterface, usb_alt_interface);
2141 if (result != kIOReturnSuccess) {
2142 debug("error setting alternate interface: %#x\n", result);
2143 return IRECV_E_USB_INTERFACE;
2144 }
2145 }
2146
147 return IRECV_E_SUCCESS; 2147 return IRECV_E_SUCCESS;
148} 2148}
2149#endif
2150#endif
2151
2152irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface)
2153{
2154#ifdef USE_DUMMY
2155 return IRECV_E_UNSUPPORTED;
2156#else
2157 if (check_context(client) != IRECV_E_SUCCESS)
2158 return IRECV_E_NO_DEVICE;
149 2159
150irecv_error_t irecv_reset(irecv_client_t client) { 2160 debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface);
151 if (client == NULL || client->handle == NULL) { 2161#ifndef _WIN32
2162#ifdef HAVE_IOKIT
2163 if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) {
2164 return IRECV_E_USB_INTERFACE;
2165 }
2166#else
2167 if (libusb_claim_interface(client->handle, usb_interface) < 0) {
2168 return IRECV_E_USB_INTERFACE;
2169 }
2170
2171 if (usb_interface == 1) {
2172 if (libusb_set_interface_alt_setting(client->handle, usb_interface, usb_alt_interface) < 0) {
2173 return IRECV_E_USB_INTERFACE;
2174 }
2175 }
2176#endif
2177#else
2178 if (usb_interface == 1) {
2179 if (irecv_usb_control_transfer(client, 0, 0x0B, usb_alt_interface, usb_interface, NULL, 0, USB_TIMEOUT) < 0) {
2180 return IRECV_E_USB_INTERFACE;
2181 }
2182 }
2183#endif
2184 client->usb_interface = usb_interface;
2185 client->usb_alt_interface = usb_alt_interface;
2186
2187 return IRECV_E_SUCCESS;
2188#endif
2189}
2190
2191irecv_error_t irecv_reset(irecv_client_t client)
2192{
2193#ifdef USE_DUMMY
2194 return IRECV_E_UNSUPPORTED;
2195#else
2196 if (check_context(client) != IRECV_E_SUCCESS)
152 return IRECV_E_NO_DEVICE; 2197 return IRECV_E_NO_DEVICE;
2198
2199#ifndef _WIN32
2200#ifdef HAVE_IOKIT
2201 IOReturn result;
2202
2203 result = (*client->handle)->ResetDevice(client->handle);
2204 if (result != kIOReturnSuccess && result != kIOReturnNotResponding) {
2205 debug("error sending device reset: %#x\n", result);
2206 return IRECV_E_UNKNOWN_ERROR;
153 } 2207 }
154 2208
2209 result = (*client->handle)->USBDeviceReEnumerate(client->handle, 0);
2210 if (result != kIOReturnSuccess && result != kIOReturnNotResponding) {
2211 debug("error re-enumerating device: %#x (ignored)\n", result);
2212 }
2213#else
155 libusb_reset_device(client->handle); 2214 libusb_reset_device(client->handle);
2215#endif
2216#else
2217 DWORD count;
2218 DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL);
2219#endif
156 2220
157 return IRECV_E_SUCCESS; 2221 return IRECV_E_SUCCESS;
2222#endif
2223}
2224
2225irecv_error_t irecv_open_with_ecid_and_attempts(irecv_client_t* pclient, uint64_t ecid, int attempts)
2226{
2227#ifdef USE_DUMMY
2228 return IRECV_E_UNSUPPORTED;
2229#else
2230 int i;
2231
2232 for (i = 0; i < attempts; i++) {
2233 if (*pclient) {
2234 irecv_close(*pclient);
2235 *pclient = NULL;
2236 }
2237 if (irecv_open_with_ecid(pclient, ecid) != IRECV_E_SUCCESS) {
2238 debug("Connection failed. Waiting 1 sec before retry.\n");
2239 sleep(1);
2240 } else {
2241 return IRECV_E_SUCCESS;
2242 }
2243 }
2244
2245 return IRECV_E_UNABLE_TO_CONNECT;
2246#endif
158} 2247}
159 2248
160irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) { 2249irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data)
2250{
2251#ifdef USE_DUMMY
2252 return IRECV_E_UNSUPPORTED;
2253#else
161 switch(type) { 2254 switch(type) {
162 case IRECV_RECEIVED: 2255 case IRECV_RECEIVED:
163 client->received_callback = callback; 2256 client->received_callback = callback;
@@ -165,9 +2258,11 @@ irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type
165 2258
166 case IRECV_PROGRESS: 2259 case IRECV_PROGRESS:
167 client->progress_callback = callback; 2260 client->progress_callback = callback;
2261 break;
168 2262
169 case IRECV_CONNECTED: 2263 case IRECV_CONNECTED:
170 client->connected_callback = callback; 2264 client->connected_callback = callback;
2265 break;
171 2266
172 case IRECV_PRECOMMAND: 2267 case IRECV_PRECOMMAND:
173 client->precommand_callback = callback; 2268 client->precommand_callback = callback;
@@ -179,15 +2274,21 @@ irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type
179 2274
180 case IRECV_DISCONNECTED: 2275 case IRECV_DISCONNECTED:
181 client->disconnected_callback = callback; 2276 client->disconnected_callback = callback;
2277 break;
182 2278
183 default: 2279 default:
184 return IRECV_E_UNKNOWN_ERROR; 2280 return IRECV_E_UNKNOWN_ERROR;
185 } 2281 }
186 2282
187 return IRECV_E_SUCCESS; 2283 return IRECV_E_SUCCESS;
2284#endif
188} 2285}
189 2286
190irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) { 2287irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type)
2288{
2289#ifdef USE_DUMMY
2290 return IRECV_E_UNSUPPORTED;
2291#else
191 switch(type) { 2292 switch(type) {
192 case IRECV_RECEIVED: 2293 case IRECV_RECEIVED:
193 client->received_callback = NULL; 2294 client->received_callback = NULL;
@@ -195,9 +2296,11 @@ irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type ty
195 2296
196 case IRECV_PROGRESS: 2297 case IRECV_PROGRESS:
197 client->progress_callback = NULL; 2298 client->progress_callback = NULL;
2299 break;
198 2300
199 case IRECV_CONNECTED: 2301 case IRECV_CONNECTED:
200 client->connected_callback = NULL; 2302 client->connected_callback = NULL;
2303 break;
201 2304
202 case IRECV_PRECOMMAND: 2305 case IRECV_PRECOMMAND:
203 client->precommand_callback = NULL; 2306 client->precommand_callback = NULL;
@@ -209,17 +2312,871 @@ irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type ty
209 2312
210 case IRECV_DISCONNECTED: 2313 case IRECV_DISCONNECTED:
211 client->disconnected_callback = NULL; 2314 client->disconnected_callback = NULL;
2315 break;
212 2316
213 default: 2317 default:
214 return IRECV_E_UNKNOWN_ERROR; 2318 return IRECV_E_UNKNOWN_ERROR;
215 } 2319 }
216 2320
217 return IRECV_E_SUCCESS; 2321 return IRECV_E_SUCCESS;
2322#endif
2323}
2324
2325#ifndef USE_DUMMY
2326struct irecv_device_event_context {
2327 irecv_device_event_cb_t callback;
2328 void *user_data;
2329};
2330
2331struct irecv_usb_device_info {
2332 struct irecv_device_info device_info;
2333 enum irecv_mode mode;
2334 uint32_t location;
2335 int alive;
2336};
2337
2338#ifdef _WIN32
2339struct irecv_win_dev_ctx {
2340 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
2341 uint32_t location;
2342};
2343#else
2344#ifdef HAVE_IOKIT
2345struct irecv_iokit_dev_ctx {
2346 io_service_t device;
2347 IOUSBDeviceInterface **dev;
2348};
2349#endif
2350#endif
2351
2352static int _irecv_is_recovery_device(void *device)
2353{
2354 uint16_t vendor_id = 0;
2355 uint16_t product_id = 0;
2356#ifdef _WIN32
2357 const char *path = (const char*)device;
2358 unsigned int vendor = 0;
2359 unsigned int product = 0;
2360 if (sscanf(path, "\\usb#vid_%04x&pid_%04x#", &vendor, &product) != 2) {
2361 return 0;
2362 }
2363 vendor_id = (uint16_t)vendor;
2364 product_id = (uint16_t)product;
2365#else
2366#ifdef HAVE_IOKIT
2367 kern_return_t kr;
2368 IOUSBDeviceInterface **dev = device;
2369 kr = (*dev)->GetDeviceVendor(dev, &vendor_id);
2370 if (kr != kIOReturnSuccess) {
2371 debug("%s: Failed to get vendor id\n", __func__);
2372 return 0;
2373 }
2374 kr = (*dev)->GetDeviceProduct(dev, &product_id);
2375 if (kr != kIOReturnSuccess) {
2376 debug("%s: Failed to get product id\n", __func__);
2377 return 0;
2378 }
2379#else
2380 libusb_device *device_ = (libusb_device*)device;
2381 struct libusb_device_descriptor devdesc;
2382 int libusb_error;
2383
2384 libusb_error = libusb_get_device_descriptor(device_, &devdesc);
2385 if (libusb_error != 0) {
2386 debug("%s: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2387 return 0;
2388 }
2389 vendor_id = devdesc.idVendor;
2390 product_id = devdesc.idProduct;
2391#endif
2392#endif
2393
2394 if (vendor_id != APPLE_VENDOR_ID) {
2395 return 0;
2396 }
2397
2398 switch (product_id) {
2399 case IRECV_K_DFU_MODE:
2400 case IRECV_K_WTF_MODE:
2401 case IRECV_K_RECOVERY_MODE_1:
2402 case IRECV_K_RECOVERY_MODE_2:
2403 case IRECV_K_RECOVERY_MODE_3:
2404 case IRECV_K_RECOVERY_MODE_4:
2405 case IRECV_K_PORT_DFU_MODE:
2406 case KIS_PRODUCT_ID:
2407 break;
2408 default:
2409 return 0;
2410 }
2411 return 1;
2412}
2413
2414static void* _irecv_handle_device_add(void *userdata)
2415{
2416 struct irecv_client_private client_loc;
2417 char serial_str[256];
2418 uint32_t location = 0;
2419 uint16_t product_id = 0;
2420 irecv_error_t error = 0;
2421 irecv_client_t client = NULL;
2422
2423 memset(serial_str, 0, sizeof(serial_str));
2424#ifdef _WIN32
2425 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata;
2426 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details = win_ctx->details;
2427 LPSTR result = (LPSTR)details->DevicePath;
2428 location = win_ctx->location;
2429
2430 unsigned int pid = 0;
2431
2432 if (strncmp(result, "\\\\?\\kis#", 8) == 0) {
2433 pid = KIS_PRODUCT_ID;
2434 } else {
2435 char *p = result;
2436 while ((p = strstr(p, "\\usb"))) {
2437 if (sscanf(p, "\\usb#vid_05ac&pid_%04x#%s", &pid, serial_str) == 2)
2438 break;
2439 p += 4;
2440 }
2441
2442 if (serial_str[0] == '\0') {
2443 debug("%s: ERROR: failed to parse DevicePath?!\n", __func__);
2444 return NULL;
2445 }
2446
2447 if (!_irecv_is_recovery_device(p)) {
2448 return NULL;
2449 }
2450 }
2451
2452 product_id = (uint16_t)pid;
2453
2454 if (product_id == KIS_PRODUCT_ID) {
2455 client = (irecv_client_t)malloc(sizeof(struct irecv_client_private));
2456 if (client == NULL) {
2457 debug("%s: Failed to allocate memory\n", __func__);
2458 return NULL;
2459 }
2460 memset(client, '\0', sizeof(struct irecv_client_private));
2461 client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
2462 if (client->handle == INVALID_HANDLE_VALUE) {
2463 debug("%s: Failed to open device path %s\n", __func__, result);
2464 free(client);
2465 return NULL;
2466 }
2467 client->mode = pid;
2468 } else {
2469 char* p = strchr(serial_str, '#');
2470 if (p) {
2471 *p = '\0';
2472 }
2473
2474 unsigned int j;
2475 for (j = 0; j < strlen(serial_str); j++) {
2476 if (serial_str[j] == '_') {
2477 serial_str[j] = ' ';
2478 } else {
2479 serial_str[j] = toupper(serial_str[j]);
2480 }
2481 }
2482 }
2483
2484#else /* !_WIN32 */
2485#ifdef HAVE_IOKIT
2486 struct irecv_iokit_dev_ctx* iokit_ctx = (struct irecv_iokit_dev_ctx*)userdata;
2487 io_service_t device = iokit_ctx->device;
2488 IOUSBDeviceInterface **dev = iokit_ctx->dev;
2489
2490 if (!device) {
2491 debug("%s: ERROR: no device?!\n", __func__);
2492 return NULL;
2493 }
2494 if (!dev) {
2495 debug("%s: ERROR: no device interface?!\n", __func__);
2496 return NULL;
2497 }
2498
2499 (*dev)->GetDeviceProduct(dev, &product_id);
2500 if (!product_id) {
2501 debug("%s: ERROR: could not get product id?!\n", __func__);
2502 return NULL;
2503 }
2504 CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
2505 if (locationNum) {
2506 CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
2507 CFRelease(locationNum);
2508 }
2509 if (!location) {
2510 debug("%s: ERROR: could not get locationID?!\n", __func__);
2511 return NULL;
2512 }
2513
2514 if (product_id == KIS_PRODUCT_ID) {
2515 IOObjectRetain(device);
2516 int i = 0;
2517 for (i = 0; i < 10; i++) {
2518 error = iokit_usb_open_service(&client, device);
2519 if (error == IRECV_E_SUCCESS) {
2520 break;
2521 }
2522 debug("%s: Could not open KIS device, retrying...\n", __func__);
2523 usleep(500000);
2524 }
2525 if (error != IRECV_E_SUCCESS) {
2526 debug("%s: ERROR: could not open KIS device!\n", __func__);
2527 return NULL;
2528 }
2529 product_id = client->mode;
2530 } else {
2531 CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
2532 if (serialString) {
2533 CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
2534 CFRelease(serialString);
2535 }
2536 }
2537#else /* !HAVE_IOKIT */
2538 libusb_device *device = (libusb_device*)userdata;
2539 struct libusb_device_descriptor devdesc;
2540 struct libusb_device_handle* usb_handle = NULL;
2541 int libusb_error;
2542
2543 libusb_error = libusb_get_device_descriptor(device, &devdesc);
2544 if (libusb_error != 0) {
2545 debug("%s: ERROR: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2546 return NULL;
2547 }
2548 product_id = devdesc.idProduct;
2549
2550 uint8_t bus = libusb_get_bus_number(device);
2551 uint8_t address = libusb_get_device_address(device);
2552 location = (bus << 16) | address;
2553
2554 libusb_error = libusb_open(device, &usb_handle);
2555 if (usb_handle == NULL || libusb_error != 0) {
2556 debug("%s: ERROR: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
2557 libusb_close(usb_handle);
2558 return 0;
2559 }
2560
2561 if (product_id == KIS_PRODUCT_ID) {
2562 error = libusb_usb_open_handle_with_descriptor_and_ecid(&client, usb_handle, &devdesc, 0);
2563 if (error != IRECV_E_SUCCESS) {
2564 debug("%s: ERROR: could not open KIS device!\n", __func__);
2565 return NULL;
2566 }
2567
2568 product_id = client->mode;
2569 } else {
2570 libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
2571 if (libusb_error < 0) {
2572 debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2573 return 0;
2574 }
2575 libusb_close(usb_handle);
2576 }
2577#endif /* !HAVE_IOKIT */
2578#endif /* !_WIN32 */
2579 memset(&client_loc, '\0', sizeof(client_loc));
2580 if (product_id == KIS_PRODUCT_ID) {
2581 int i = 0;
2582 for (i = 0; i < 10; i++) {
2583 error = irecv_usb_set_configuration(client, 1);
2584 if (error == IRECV_E_SUCCESS) {
2585 break;
2586 }
2587 debug("Failed to set configuration, error %d, retrying...\n", error);
2588 usleep(500000);
2589 }
2590 if (error != IRECV_E_SUCCESS) {
2591 debug("Failed to set configuration, error %d\n", error);
2592 irecv_close(client);
2593 return NULL;
2594 }
2595
2596 for (i = 0; i < 10; i++) {
2597 error = irecv_usb_set_interface(client, 0, 0);
2598 if (error == IRECV_E_SUCCESS) {
2599 break;
2600 }
2601 debug("Failed to set interface, error %d, retrying...\n", error);
2602 usleep(500000);
2603 }
2604 if (error != IRECV_E_SUCCESS) {
2605 debug("Failed to set interface, error %d\n", error);
2606 irecv_close(client);
2607 return NULL;
2608 }
2609
2610 error = irecv_kis_init(client);
2611 if (error != IRECV_E_SUCCESS) {
2612 debug("irecv_kis_init failed, error %d\n", error);
2613 irecv_close(client);
2614 return NULL;
2615 }
2616
2617 error = irecv_kis_load_device_info(client);
2618 if (error != IRECV_E_SUCCESS) {
2619 debug("irecv_kis_load_device_info failed, error %d\n", error);
2620 irecv_close(client);
2621 return NULL;
2622 }
2623 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)client->device_info.ecid);
2624 strncpy(serial_str, client->device_info.serial_string, 255);
2625 product_id = client->mode;
2626 client_loc.isKIS = 1;
2627 }
2628 if (client) {
2629 irecv_close(client);
2630 }
2631
2632 client_loc.mode = product_id;
2633 irecv_load_device_info_from_iboot_string(&client_loc, serial_str);
2634
2635 struct irecv_usb_device_info *usb_dev_info = (struct irecv_usb_device_info*)malloc(sizeof(struct irecv_usb_device_info));
2636 memcpy(&(usb_dev_info->device_info), &(client_loc.device_info), sizeof(struct irecv_device_info));
2637 usb_dev_info->location = location;
2638 usb_dev_info->alive = 1;
2639 usb_dev_info->mode = client_loc.mode;
2640
2641 collection_add(&devices, usb_dev_info);
2642
2643 irecv_device_event_t dev_event;
2644 dev_event.type = IRECV_DEVICE_ADD;
2645 dev_event.mode = client_loc.mode;
2646 dev_event.device_info = &(usb_dev_info->device_info);
2647
2648 mutex_lock(&listener_mutex);
2649 FOREACH(struct irecv_device_event_context* context, &listeners) {
2650 context->callback(&dev_event, context->user_data);
2651 } ENDFOREACH
2652 mutex_unlock(&listener_mutex);
2653
2654 return NULL;
2655}
2656
2657static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo)
2658{
2659 irecv_device_event_t dev_event;
2660 dev_event.type = IRECV_DEVICE_REMOVE;
2661 dev_event.mode = devinfo->mode;
2662 dev_event.device_info = &(devinfo->device_info);
2663 mutex_lock(&listener_mutex);
2664 FOREACH(struct irecv_device_event_context* context, &listeners) {
2665 context->callback(&dev_event, context->user_data);
2666 } ENDFOREACH
2667 mutex_unlock(&listener_mutex);
2668 free(devinfo->device_info.srnm);
2669 devinfo->device_info.srnm = NULL;
2670 free(devinfo->device_info.imei);
2671 devinfo->device_info.imei = NULL;
2672 free(devinfo->device_info.srtg);
2673 devinfo->device_info.srtg = NULL;
2674 free(devinfo->device_info.serial_string);
2675 devinfo->device_info.serial_string = NULL;
2676 devinfo->alive = 0;
2677 collection_remove(&devices, devinfo);
2678 free(devinfo);
2679}
2680
2681#ifndef _WIN32
2682#ifdef HAVE_IOKIT
2683static void iokit_device_added(void *refcon, io_iterator_t iterator)
2684{
2685 kern_return_t kr;
2686 io_service_t device;
2687 IOCFPlugInInterface **plugInInterface = NULL;
2688 IOUSBDeviceInterface **dev = NULL;
2689 HRESULT result;
2690 SInt32 score;
2691
2692 while ((device = IOIteratorNext(iterator))) {
2693 kr = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
2694 if ((kIOReturnSuccess != kr) || !plugInInterface) {
2695 debug("%s: ERROR: Unable to create a plug-in (%08x)\n", __func__, kr);
2696 IOObjectRelease(device);
2697 continue;
2698 }
2699 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&dev);
2700 (*plugInInterface)->Release(plugInInterface);
2701
2702 if (result || !dev) {
2703 debug("%s: ERROR: Couldn't create a device interface (%08x)\n", __func__, (int)result);
2704 IOObjectRelease(device);
2705 continue;
2706 }
2707
2708 if (!_irecv_is_recovery_device(dev)) {
2709 (void) (*dev)->Release(dev);
2710 IOObjectRelease(device);
2711 continue;
2712 }
2713
2714 struct irecv_iokit_dev_ctx idev;
2715 idev.device = device;
2716 idev.dev = dev;
2717 _irecv_handle_device_add(&idev);
2718 (void) (*dev)->Release(dev);
2719 IOObjectRelease(device);
2720 }
2721}
2722
2723static void iokit_device_removed(void *refcon, io_iterator_t iterator)
2724{
2725 io_service_t device;
2726
2727 while ((device = IOIteratorNext(iterator))) {
2728 uint32_t location = 0;
2729 CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
2730 if (locationNum) {
2731 CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
2732 CFRelease(locationNum);
2733 }
2734 IOObjectRelease(device);
2735
2736 if (!location) {
2737 continue;
2738 }
2739
2740 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2741 if (devinfo->location == location) {
2742 _irecv_handle_device_remove(devinfo);
2743 break;
2744 }
2745 } ENDFOREACH
2746 }
2747}
2748#else /* !HAVE_IOKIT */
2749#ifdef HAVE_LIBUSB_HOTPLUG_API
2750static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
2751{
2752 if (!_irecv_is_recovery_device(device)) {
2753 return 0;
2754 }
2755 if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
2756 THREAD_T th_device;
2757 if (thread_new(&th_device, _irecv_handle_device_add, device) != 0) {
2758 debug("%s: FATAL: failed to create thread to handle device add\n", __func__);
2759 return 0;
2760 }
2761 thread_detach(th_device);
2762 } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
2763 uint8_t bus = libusb_get_bus_number(device);
2764 uint8_t address = libusb_get_device_address(device);
2765 uint32_t location = (bus << 16) | address;
2766 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2767 if (devinfo->location == location) {
2768 _irecv_handle_device_remove(devinfo);
2769 break;
2770 }
2771 } ENDFOREACH
2772 }
2773
2774 return 0;
2775}
2776#endif /* HAVE_LIBUSB_HOTPLUG_API */
2777#endif /* !HAVE_IOKIT */
2778#endif /* !_WIN32 */
2779
2780struct _irecv_event_handler_info {
2781 cond_t startup_cond;
2782 mutex_t startup_mutex;
2783};
2784
2785static void *_irecv_event_handler(void* data)
2786{
2787 struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data;
2788#ifdef _WIN32
2789 struct collection newDevices;
2790 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
2791 int running = 1;
2792
2793 collection_init(&newDevices);
2794
2795 mutex_lock(&(info->startup_mutex));
2796 cond_signal(&(info->startup_cond));
2797 mutex_unlock(&(info->startup_mutex));
2798
2799 do {
2800 SP_DEVICE_INTERFACE_DATA currentInterface;
2801 HDEVINFO usbDevices;
2802 DWORD i;
2803 int k;
2804
2805 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2806 devinfo->alive = 0;
2807 } ENDFOREACH
2808
2809 for (k = 0; guids[k]; k++) {
2810 usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
2811 if (!usbDevices) {
2812 debug("%s: ERROR: SetupDiGetClassDevs failed\n", __func__);
2813 // cleanup/free newDevices
2814 FOREACH(struct irecv_win_dev_ctx *win_ctx, &newDevices) {
2815 free(win_ctx->details);
2816 collection_remove(&newDevices, win_ctx);
2817 free(win_ctx);
2818 } ENDFOREACH
2819 collection_free(&newDevices);
2820 return NULL;
2821 }
2822
2823
2824 memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
2825 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
2826 for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
2827 DWORD requiredSize = 0;
2828 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
2829 SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
2830 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A) malloc(requiredSize);
2831 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
2832 SP_DEVINFO_DATA devinfodata;
2833 devinfodata.cbSize = sizeof(SP_DEVINFO_DATA);
2834 if (!SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, details, requiredSize, NULL, &devinfodata)) {
2835 free(details);
2836 continue;
2837 }
2838
2839 DWORD sz = REG_SZ;
2840 char driver[256];
2841 driver[0] = '\0';
2842 if (!SetupDiGetDeviceRegistryPropertyA(usbDevices, &devinfodata, SPDRP_DRIVER, &sz, (PBYTE)driver, sizeof(driver), NULL)) {
2843 debug("%s: ERROR: Failed to get driver key\n", __func__);
2844 free(details);
2845 continue;
2846 }
2847
2848 char *p = strrchr(driver, '\\');
2849 if (!p) {
2850 debug("%s: ERROR: Failed to parse device location\n", __func__);
2851 free(details);
2852 continue;
2853 }
2854 p++;
2855 uint32_t location = 0;
2856 if (!*p || strlen(p) < 4) {
2857 debug("%s: ERROR: Driver location suffix too short\n", __func__);
2858 free(details);
2859 continue;
2860 }
2861 memcpy(&location, p, 4);
2862 int found = 0;
2863
2864 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2865 if (devinfo->location == location) {
2866 devinfo->alive = 1;
2867 found = 1;
2868 break;
2869 }
2870 } ENDFOREACH
2871
2872 unsigned int pid = 0;
2873 unsigned int vid = 0;
2874 if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid)!= 2) {
2875 debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
2876 free(details);
2877 continue;
2878 }
2879 if (vid != APPLE_VENDOR_ID) {
2880 free(details);
2881 continue;
2882 }
2883
2884 // make sure the current device is actually in the right mode for the given driver interface
2885 int skip = 0;
2886 if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
2887 || (guids[k] == &GUID_DEVINTERFACE_PORTDFU && pid != IRECV_K_PORT_DFU_MODE)
2888 || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
2889 || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
2890 ) {
2891 skip = 1;
2892 }
2893
2894 if (!found && !skip) {
2895 // Add device to newDevices list, and deliver the notification later, when removed devices are first handled.
2896 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)malloc(sizeof(struct irecv_win_dev_ctx));
2897 win_ctx->details = details;
2898 win_ctx->location = location;
2899 collection_add(&newDevices, win_ctx);
2900 details = NULL;
2901 }
2902 free(details);
2903 }
2904 SetupDiDestroyDeviceInfoList(usbDevices);
2905 }
2906
2907 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2908 if (!devinfo->alive) {
2909 debug("%s: removed ecid: %016" PRIx64 ", location: %d\n",__func__, (uint64_t)devinfo->device_info.ecid, devinfo->location);
2910 _irecv_handle_device_remove(devinfo);
2911 }
2912 } ENDFOREACH
2913
2914 // handle newly added devices and remove from local list
2915 FOREACH(struct irecv_win_dev_ctx *win_ctx, &newDevices) {
2916 debug("%s: found new: %s, location: %d\n", __func__, win_ctx->details->DevicePath, win_ctx->location);
2917 _irecv_handle_device_add(win_ctx);
2918 free(win_ctx->details);
2919 collection_remove(&newDevices, win_ctx);
2920 free(win_ctx);
2921 } ENDFOREACH
2922
2923 Sleep(500);
2924 mutex_lock(&listener_mutex);
2925 if (collection_count(&listeners) == 0) {
2926 running = 0;
2927 }
2928 mutex_unlock(&listener_mutex);
2929 } while (running);
2930
2931 collection_free(&newDevices);
2932#else /* !_WIN32 */
2933#ifdef HAVE_IOKIT
2934 kern_return_t kr;
2935
2936 IONotificationPortRef notifyPort = IONotificationPortCreate(MACH_PORT_NULL);
2937 CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notifyPort);
2938 iokit_runloop = CFRunLoopGetCurrent();
2939 CFRunLoopAddSource(iokit_runloop, runLoopSource, kCFRunLoopDefaultMode);
2940
2941 uint16_t pids[9] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, IRECV_K_PORT_DFU_MODE, KIS_PRODUCT_ID, 0 };
2942 int i = 0;
2943 while (pids[i] > 0) {
2944 CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
2945 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
2946 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pids[i]);
2947
2948 matchingDict = (CFMutableDictionaryRef)CFRetain(matchingDict);
2949
2950 io_iterator_t devAddedIter;
2951 kr = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, iokit_device_added, NULL, &devAddedIter);
2952 if (kr != kIOReturnSuccess) {
2953 debug("%s: Failed to register device add notification callback\n", __func__);
2954 }
2955 iokit_device_added(NULL, devAddedIter);
2956
2957 io_iterator_t devRemovedIter;
2958 kr = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, iokit_device_removed, NULL, &devRemovedIter);
2959 if (kr != kIOReturnSuccess) {
2960 debug("%s: Failed to register device remove notification callback\n", __func__);
2961 }
2962 iokit_device_removed(NULL, devRemovedIter);
2963
2964 i++;
2965 }
2966
2967 mutex_lock(&(info->startup_mutex));
2968 cond_signal(&(info->startup_cond));
2969 mutex_unlock(&(info->startup_mutex));
2970
2971 CFRunLoopRun();
2972
2973#else /* !HAVE_IOKIT */
2974#ifdef HAVE_LIBUSB_HOTPLUG_API
2975 static libusb_hotplug_callback_handle usb_hotplug_cb_handle;
2976 libusb_hotplug_register_callback(irecv_hotplug_ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_ENUMERATE, APPLE_VENDOR_ID, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, _irecv_usb_hotplug_cb, NULL, &usb_hotplug_cb_handle);
2977 int running = 1;
2978
2979 mutex_lock(&(info->startup_mutex));
2980 cond_signal(&(info->startup_cond));
2981 mutex_unlock(&(info->startup_mutex));
2982
2983 do {
2984 struct timeval tv;
2985 tv.tv_sec = tv.tv_usec = 0;
2986 libusb_handle_events_timeout(irecv_hotplug_ctx, &tv);
2987
2988 mutex_lock(&listener_mutex);
2989 if (collection_count(&listeners) == 0) {
2990 running = 0;
2991 }
2992 mutex_unlock(&listener_mutex);
2993
2994 usleep(100000);
2995 } while (running);
2996 libusb_hotplug_deregister_callback(irecv_hotplug_ctx, usb_hotplug_cb_handle);
2997#else /* !HAVE_LIBUSB_HOTPLUG_API */
2998 int i, cnt;
2999 libusb_device **devs;
3000 int running = 1;
3001
3002 mutex_lock(&(info->startup_mutex));
3003 cond_signal(&(info->startup_cond));
3004 mutex_unlock(&(info->startup_mutex));
3005
3006 do {
3007 cnt = libusb_get_device_list(irecv_hotplug_ctx, &devs);
3008 if (cnt < 0) {
3009 debug("%s: FATAL: Failed to get device list: %s\n", __func__, libusb_error_name(cnt));
3010 return NULL;
3011 }
3012
3013 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3014 devinfo->alive = 0;
3015 } ENDFOREACH
3016
3017 for (i = 0; i < cnt; i++) {
3018 libusb_device *dev = devs[i];
3019 if (!_irecv_is_recovery_device(dev)) {
3020 continue;
3021 }
3022 uint8_t bus = libusb_get_bus_number(dev);
3023 uint8_t address = libusb_get_device_address(dev);
3024 uint32_t location = (bus << 16) | address;
3025 int found = 0;
3026 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3027 if (devinfo->location == location) {
3028 devinfo->alive = 1;
3029 found = 1;
3030 break;
3031 }
3032 } ENDFOREACH
3033 if (!found) {
3034 _irecv_handle_device_add(dev);
3035 }
3036 }
3037
3038 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3039 if (!devinfo->alive) {
3040 _irecv_handle_device_remove(devinfo);
3041 }
3042 } ENDFOREACH
3043
3044 libusb_free_device_list(devs, 1);
3045
3046 mutex_lock(&listener_mutex);
3047 if (collection_count(&listeners) == 0) {
3048 running = 0;
3049 }
3050 mutex_unlock(&listener_mutex);
3051 if (!running)
3052 break;
3053 usleep(500000);
3054 } while (running);
3055#endif /* !HAVE_LIBUSB_HOTPLUG_API */
3056#endif /* !HAVE_IOKIT */
3057#endif /* !_WIN32 */
3058 return NULL;
3059}
3060#endif /* !USE_DUMMY */
3061
3062irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data)
3063{
3064#ifdef USE_DUMMY
3065 return IRECV_E_UNSUPPORTED;
3066#else
3067 if (!context || !callback)
3068 return IRECV_E_INVALID_INPUT;
3069
3070 struct irecv_device_event_context* _context = malloc(sizeof(struct irecv_device_event_context));
3071 if (!_context) {
3072 return IRECV_E_OUT_OF_MEMORY;
3073 }
3074
3075 _context->callback = callback;
3076 _context->user_data = user_data;
3077
3078 mutex_lock(&listener_mutex);
3079 collection_add(&listeners, _context);
3080
3081 if (th_event_handler == THREAD_T_NULL || !thread_alive(th_event_handler)) {
3082 mutex_unlock(&listener_mutex);
3083 struct _irecv_event_handler_info info;
3084 cond_init(&info.startup_cond);
3085 mutex_init(&info.startup_mutex);
3086#ifndef _WIN32
3087#ifndef HAVE_IOKIT
3088 libusb_init(&irecv_hotplug_ctx);
3089#endif
3090#endif
3091 collection_init(&devices);
3092 mutex_init(&device_mutex);
3093 mutex_lock(&info.startup_mutex);
3094 if (thread_new(&th_event_handler, _irecv_event_handler, &info) == 0) {
3095 cond_wait(&info.startup_cond, &info.startup_mutex);
3096 }
3097 mutex_unlock(&info.startup_mutex);
3098 cond_destroy(&info.startup_cond);
3099 mutex_destroy(&info.startup_mutex);
3100 } else {
3101 /* send DEVICE_ADD events to the new listener */
3102 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3103 if (devinfo && devinfo->alive) {
3104 irecv_device_event_t ev;
3105 ev.type = IRECV_DEVICE_ADD;
3106 ev.mode = devinfo->mode;
3107 ev.device_info = &(devinfo->device_info);
3108 _context->callback(&ev, _context->user_data);
3109 }
3110 } ENDFOREACH
3111 mutex_unlock(&listener_mutex);
3112 }
3113
3114 *context = _context;
3115
3116 return IRECV_E_SUCCESS;
3117#endif
218} 3118}
219 3119
220irecv_error_t irecv_close(irecv_client_t client) { 3120irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context)
3121{
3122#ifdef USE_DUMMY
3123 return IRECV_E_UNSUPPORTED;
3124#else
3125 if (!context)
3126 return IRECV_E_INVALID_INPUT;
3127
3128 mutex_lock(&listener_mutex);
3129 collection_remove(&listeners, context);
3130 int num = collection_count(&listeners);
3131 mutex_unlock(&listener_mutex);
3132
3133 if (num == 0 && th_event_handler != THREAD_T_NULL && thread_alive(th_event_handler)) {
3134#ifdef HAVE_IOKIT
3135 if (iokit_runloop) {
3136 while (!CFRunLoopIsWaiting(iokit_runloop)) usleep(420);
3137 CFRunLoopStop(iokit_runloop);
3138 iokit_runloop = NULL;
3139 }
3140#endif
3141 thread_join(th_event_handler);
3142 thread_free(th_event_handler);
3143 th_event_handler = THREAD_T_NULL;
3144 mutex_lock(&device_mutex);
3145 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3146 free(devinfo->device_info.srnm);
3147 devinfo->device_info.srnm = NULL;
3148 free(devinfo->device_info.imei);
3149 devinfo->device_info.imei = NULL;
3150 free(devinfo->device_info.srtg);
3151 devinfo->device_info.srtg = NULL;
3152 free(devinfo->device_info.serial_string);
3153 devinfo->device_info.serial_string = NULL;
3154 free(devinfo);
3155 } ENDFOREACH
3156 collection_free(&devices);
3157 mutex_unlock(&device_mutex);
3158 mutex_destroy(&device_mutex);
3159#ifndef _WIN32
3160#ifndef HAVE_IOKIT
3161 libusb_exit(irecv_hotplug_ctx);
3162 irecv_hotplug_ctx = NULL;
3163#endif
3164#endif
3165 }
3166
3167 free(context);
3168
3169 return IRECV_E_SUCCESS;
3170#endif
3171}
3172
3173static irecv_error_t irecv_cleanup(irecv_client_t client)
3174{
3175#ifdef USE_DUMMY
3176 return IRECV_E_UNSUPPORTED;
3177#else
221 if (client != NULL) { 3178 if (client != NULL) {
222 if(client->disconnected_callback != NULL) { 3179 if (client->disconnected_callback != NULL) {
223 irecv_event_t event; 3180 irecv_event_t event;
224 event.size = 0; 3181 event.size = 0;
225 event.data = NULL; 3182 event.data = NULL;
@@ -227,114 +3184,201 @@ irecv_error_t irecv_close(irecv_client_t client) {
227 event.type = IRECV_DISCONNECTED; 3184 event.type = IRECV_DISCONNECTED;
228 client->disconnected_callback(client, &event); 3185 client->disconnected_callback(client, &event);
229 } 3186 }
230 3187#ifndef _WIN32
3188#ifdef HAVE_IOKIT
3189 if (client->usbInterface) {
3190 (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
3191 (*client->usbInterface)->Release(client->usbInterface);
3192 client->usbInterface = NULL;
3193 }
3194 if (client->handle) {
3195 (*client->handle)->USBDeviceClose(client->handle);
3196 (*client->handle)->Release(client->handle);
3197 client->handle = NULL;
3198 }
3199#else
231 if (client->handle != NULL) { 3200 if (client->handle != NULL) {
232 libusb_release_interface(client->handle, client->interface); 3201 if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE) && (client->isKIS == 0)) {
3202 libusb_release_interface(client->handle, client->usb_interface);
3203 }
233 libusb_close(client->handle); 3204 libusb_close(client->handle);
234 client->handle = NULL; 3205 client->handle = NULL;
235 } 3206 }
3207#endif
3208#else
3209 CloseHandle(client->handle);
3210#endif
3211 free(client->device_info.srnm);
3212 free(client->device_info.imei);
3213 free(client->device_info.srtg);
3214 free(client->device_info.serial_string);
3215 free(client->device_info.ap_nonce);
3216 free(client->device_info.sep_nonce);
3217 }
236 3218
237 if (libirecovery_context != NULL) { 3219 return IRECV_E_SUCCESS;
238 libusb_exit(libirecovery_context); 3220#endif
239 libirecovery_context = NULL; 3221}
240 }
241 3222
3223irecv_error_t irecv_close(irecv_client_t client)
3224{
3225 irecv_error_t ret = IRECV_E_SUCCESS;
3226 if (client) {
3227 ret = irecv_cleanup(client);
242 free(client); 3228 free(client);
243 client = NULL; 3229 client = NULL;
244 } 3230 }
245 3231 return ret;
246 return IRECV_E_SUCCESS;
247} 3232}
248 3233
249void irecv_set_debug_level(int level) { 3234void irecv_set_debug_level(int level)
3235{
250 libirecovery_debug = level; 3236 libirecovery_debug = level;
251 if(libirecovery_context) { 3237#ifndef USE_DUMMY
252 libusb_set_debug(libirecovery_context, libirecovery_debug); 3238#ifndef _WIN32
3239#ifndef HAVE_IOKIT
3240 if (libirecovery_context) {
3241#if LIBUSB_API_VERSION >= 0x01000106
3242 libusb_set_option(libirecovery_context, LIBUSB_OPTION_LOG_LEVEL, libirecovery_debug > 2 ? 1: 0);
3243#else
3244 libusb_set_debug(libirecovery_context, libirecovery_debug > 2 ? 1: 0);
3245#endif
253 } 3246 }
3247#endif
3248#endif
3249#endif
3250}
3251
3252const char* irecv_version()
3253{
3254#ifndef PACKAGE_VERSION
3255#error PACKAGE_VERSION is not defined!
3256#endif
3257 return PACKAGE_VERSION;
254} 3258}
255 3259
256static irecv_error_t irecv_send_command_raw(irecv_client_t client, unsigned char* command) { 3260
3261#ifndef USE_DUMMY
3262static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* command, uint8_t b_request)
3263{
257 unsigned int length = strlen(command); 3264 unsigned int length = strlen(command);
258 if (length >= 0x100) { 3265 if (length >= 0x100) {
259 length = 0xFF; 3266 return IRECV_E_INVALID_INPUT;
260 } 3267 }
261 3268
262 if (length > 0) { 3269 if (length > 0 && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
263 int ret = libusb_control_transfer(client->handle, 0x40, 0, 0, 0, command, length + 1, 100); 3270 int bytes = 0;
264 if ((ret < 0) || (ret != (length + 1))) { 3271 irecv_error_t error = 0;
265 if (ret == LIBUSB_ERROR_PIPE) 3272#ifdef DEBUG
266 return IRECV_E_PIPE; 3273 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
267 if (ret == LIBUSB_ERROR_TIMEOUT) 3274 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
268 return IRECV_E_TIMEOUT; 3275 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
269 return IRECV_E_UNKNOWN_ERROR; 3276 if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
3277#endif
3278 char cmdstr[0x100] = {0};
3279 if (length & 0xf) {
3280 length &= ~0xf;
3281 length += 0x10;
270 } 3282 }
3283 snprintf(cmdstr, sizeof(cmdstr), "%s\n",command);
3284 legacyCMD cmd = {
3285 MSG_SEND_COMMAND,
3286 0x1234, //magic
3287 (uint32_t)length, //zero terminated?
3288 0x0
3289 };
3290 if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3291 if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3292 if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
3293 if ((error = irecv_usb_interrupt_transfer(client, 0x02, (unsigned char*)cmdstr, length, &bytes, USB_TIMEOUT))) return error;
3294 sleep(1); //go easy on this old device
3295 return IRECV_E_SUCCESS;
3296 }
3297
3298 if (length > 0) {
3299 irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT);
271 } 3300 }
272 3301
273 return IRECV_E_SUCCESS; 3302 return IRECV_E_SUCCESS;
274} 3303}
3304#endif
275 3305
276irecv_error_t irecv_send_command(irecv_client_t client, unsigned char* command) { 3306irecv_error_t irecv_send_command_breq(irecv_client_t client, const char* command, uint8_t b_request)
3307{
3308#ifdef USE_DUMMY
3309 return IRECV_E_UNSUPPORTED;
3310#else
277 irecv_error_t error = 0; 3311 irecv_error_t error = 0;
278 3312
279 if (client == NULL || client->handle == NULL) { 3313 if (check_context(client) != IRECV_E_SUCCESS)
280 return IRECV_E_NO_DEVICE; 3314 return IRECV_E_NO_DEVICE;
281 }
282 3315
283 unsigned int length = strlen(command); 3316 unsigned int length = strlen(command);
284 if (length >= 0x100) { 3317 if (length >= 0x100) {
285 length = 0xFF; 3318 return IRECV_E_INVALID_INPUT;
286 } 3319 }
287 3320
288 irecv_event_t event; 3321 irecv_event_t event;
289 if(client->precommand_callback != NULL) { 3322 if (client->precommand_callback != NULL) {
290 event.size = length; 3323 event.size = length;
291 event.data = command; 3324 event.data = command;
292 event.type = IRECV_PRECOMMAND; 3325 event.type = IRECV_PRECOMMAND;
293 if(client->precommand_callback(client, &event)) { 3326 if (client->precommand_callback(client, &event)) {
294 return IRECV_E_SUCCESS; 3327 return IRECV_E_SUCCESS;
295 } 3328 }
296 } 3329 }
297 3330
298 error = irecv_send_command_raw(client, command); 3331 error = irecv_send_command_raw(client, command, b_request);
299 if (error != IRECV_E_SUCCESS) { 3332 if (error != IRECV_E_SUCCESS) {
300 debug("Failed to send command %s\n", command); 3333 debug("Failed to send command %s\n", command);
301 if (error != IRECV_E_PIPE) 3334 if (error != IRECV_E_PIPE)
302 return error; 3335 return error;
303 } 3336 }
304 3337
305 if(client->postcommand_callback != NULL) { 3338 if (client->postcommand_callback != NULL) {
306 event.size = length; 3339 event.size = length;
307 event.data = command; 3340 event.data = command;
308 event.type = IRECV_POSTCOMMAND; 3341 event.type = IRECV_POSTCOMMAND;
309 if(client->postcommand_callback(client, &event)) { 3342 if (client->postcommand_callback(client, &event)) {
310 return IRECV_E_SUCCESS; 3343 return IRECV_E_SUCCESS;
311 } 3344 }
312 } 3345 }
313 3346
314 return IRECV_E_SUCCESS; 3347 return IRECV_E_SUCCESS;
3348#endif
315} 3349}
316 3350
317irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) { 3351irecv_error_t irecv_send_command(irecv_client_t client, const char* command)
318 if (client == NULL || client->handle == NULL) { 3352{
3353 return irecv_send_command_breq(client, command, 0);
3354}
3355
3356irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, unsigned int options)
3357{
3358#ifdef USE_DUMMY
3359 return IRECV_E_UNSUPPORTED;
3360#else
3361 if (check_context(client) != IRECV_E_SUCCESS)
319 return IRECV_E_NO_DEVICE; 3362 return IRECV_E_NO_DEVICE;
320 }
321 3363
322 FILE* file = fopen(filename, "rb"); 3364 FILE* file = fopen(filename, "rb");
323 if (file == NULL) { 3365 if (file == NULL) {
324 return IRECV_E_FILE_NOT_FOUND; 3366 return IRECV_E_FILE_NOT_FOUND;
325 } 3367 }
326 3368
327 fseek(file, 0, SEEK_END); 3369 struct stat fst;
328 long length = ftell(file); 3370 if (fstat(fileno(file), &fst) < 0) {
329 fseek(file, 0, SEEK_SET); 3371 return IRECV_E_UNKNOWN_ERROR;
3372 }
3373 size_t length = fst.st_size;
330 3374
331 unsigned char* buffer = (unsigned char*) malloc(length); 3375 char* buffer = (char*)malloc(length);
332 if (buffer == NULL) { 3376 if (buffer == NULL) {
333 fclose(file); 3377 fclose(file);
334 return IRECV_E_OUT_OF_MEMORY; 3378 return IRECV_E_OUT_OF_MEMORY;
335 } 3379 }
336 3380
337 long bytes = fread(buffer, 1, length, file); 3381 size_t bytes = fread(buffer, 1, length, file);
338 fclose(file); 3382 fclose(file);
339 3383
340 if (bytes != length) { 3384 if (bytes != length) {
@@ -342,64 +3386,283 @@ irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) {
342 return IRECV_E_UNKNOWN_ERROR; 3386 return IRECV_E_UNKNOWN_ERROR;
343 } 3387 }
344 3388
345 irecv_error_t error = irecv_send_buffer(client, buffer, length); 3389 irecv_error_t error = irecv_send_buffer(client, (unsigned char*)buffer, length, options);
346 free(buffer); 3390 free(buffer);
3391
347 return error; 3392 return error;
3393#endif
348} 3394}
349 3395
350irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { 3396#ifndef USE_DUMMY
351 if (client == NULL || client->handle == NULL) { 3397static irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status)
3398{
3399 if (check_context(client) != IRECV_E_SUCCESS) {
352 *status = 0; 3400 *status = 0;
353 return IRECV_E_NO_DEVICE; 3401 return IRECV_E_NO_DEVICE;
354 } 3402 }
355 3403
356 unsigned char buffer[6]; 3404 unsigned char buffer[6];
357 memset(buffer, '\0', 6); 3405 memset(buffer, '\0', sizeof(buffer));
358 if (libusb_control_transfer(client->handle, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) { 3406 if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, sizeof(buffer), USB_TIMEOUT) != sizeof(buffer)) {
359 *status = 0; 3407 *status = 0;
360 return IRECV_E_USB_STATUS; 3408 return IRECV_E_USB_STATUS;
361 } 3409 }
362 3410
363 *status = (unsigned int) buffer[4]; 3411 *status = (unsigned int) buffer[4];
3412
3413 return IRECV_E_SUCCESS;
3414}
3415
3416static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options)
3417{
3418 if (client->mode != IRECV_K_DFU_MODE) {
3419 return IRECV_E_UNSUPPORTED;
3420 }
3421
3422 unsigned long origLen = length;
3423
3424 KIS_upload_chunk *chunk = calloc(1, sizeof(KIS_upload_chunk));
3425 uint64_t address = 0;
3426 while (length) {
3427 unsigned long toUpload = length;
3428 if (toUpload > 0x4000)
3429 toUpload = 0x4000;
3430
3431#ifdef _WIN32
3432 memcpy(chunk->data, buffer, toUpload);
3433 chunk->size = toUpload;
3434 chunk->address = address;
3435#else
3436 irecv_error_t error = irecv_kis_request_init(&chunk->hdr, KIS_PORTAL_RSM, KIS_INDEX_UPLOAD, 3, toUpload, 0);
3437 if (error != IRECV_E_SUCCESS) {
3438 free(chunk);
3439 debug("Failed to init chunk header, error %d\n", error);
3440 return error;
3441 }
3442
3443 chunk->address = address;
3444 chunk->size = toUpload;
3445 memcpy(chunk->data, buffer, toUpload);
3446#endif
3447
3448#ifdef _WIN32
3449 DWORD transferred = 0;
3450 int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL);
3451 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3452#else
3453 KIS_generic_reply reply;
3454 size_t rcvSize = sizeof(reply);
3455 error = irecv_kis_request(client, &chunk->hdr, sizeof(*chunk) - (0x4000 - toUpload), &reply.hdr, &rcvSize);
3456#endif
3457 if (error != IRECV_E_SUCCESS) {
3458 free(chunk);
3459 debug("Failed to upload chunk, error %d\n", error);
3460 return error;
3461 }
3462
3463 address += toUpload;
3464 buffer += toUpload;
3465 length -= toUpload;
3466
3467 if (client->progress_callback != NULL) {
3468 irecv_event_t event;
3469 event.progress = ((double) (origLen - length) / (double) origLen) * 100.0;
3470 event.type = IRECV_PROGRESS;
3471 event.data = (char*)"Uploading";
3472 event.size = origLen - length;
3473 client->progress_callback(client, &event);
3474 } else {
3475 debug("Sent: %lu bytes - %lu of %lu\n", toUpload, origLen - length, origLen);
3476 }
3477 }
3478 free(chunk);
3479
3480 if (options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) {
3481#ifdef _WIN32
3482 DWORD amount = (DWORD)origLen;
3483 DWORD transferred = 0;
3484 int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL);
3485 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3486#else
3487 irecv_error_t error = irecv_kis_config_write32(client, KIS_PORTAL_RSM, KIS_INDEX_BOOT_IMG, origLen);
3488#endif
3489 if (error != IRECV_E_SUCCESS) {
3490 debug("Failed to boot image, error %d\n", error);
3491 return error;
3492 }
3493 }
3494
364 return IRECV_E_SUCCESS; 3495 return IRECV_E_SUCCESS;
365} 3496}
3497#endif
3498
3499irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options)
3500{
3501#ifdef USE_DUMMY
3502 return IRECV_E_UNSUPPORTED;
3503#else
3504 if (client->isKIS)
3505 return irecv_kis_send_buffer(client, buffer, length, options);
366 3506
367irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length) {
368 irecv_error_t error = 0; 3507 irecv_error_t error = 0;
369 int recovery_mode = (client->mode != kDfuMode); 3508 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
3509 int legacy_recovery_mode = 0;
3510 int isiOS2 = 0;
3511
3512 if (recovery_mode && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3513#ifdef DEBUG
3514 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
3515 int bytes = 0;
3516 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
3517 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
3518 if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
3519#endif
3520 legacy_recovery_mode = 1;
3521 }
370 3522
371 if (client == NULL || client->handle == NULL) { 3523 if (recovery_mode && !legacy_recovery_mode) {
372 return IRECV_E_NO_DEVICE; 3524 // we are in recovery mode and we are not dealing with iOS 1.x
3525 if ((client->device_info.cpid == 0x8900 || client->device_info.cpid == 0x8720) && !client->device_info.have_ibfl) {
3526 // iOS 2.x doesn't have IBFL tag, but iOS 3 does
3527 // Also, to avoid false activation of this codepath, restrict it to the only two CPID which can run iOS 2
3528 recovery_mode = 0; // iOS 2 recovery mode works same as DFU mode
3529 isiOS2 = 1;
3530 options |= IRECV_SEND_OPT_DFU_NOTIFY_FINISH;
3531 }
373 } 3532 }
374 3533
375 int packet_size = recovery_mode ? 0x4000: 0x800; 3534 if (check_context(client) != IRECV_E_SUCCESS)
3535 return IRECV_E_NO_DEVICE;
3536
3537 unsigned int h1 = 0xFFFFFFFF;
3538 unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10};
3539 int dfu_crc = 1;
3540 int packet_size = recovery_mode ? 0x8000 : 0x800;
3541 if (legacy_recovery_mode) packet_size = 0x200;
3542 if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) {
3543 packet_size = 0x40;
3544 dfu_crc = 0;
3545 }
376 int last = length % packet_size; 3546 int last = length % packet_size;
377 int packets = length / packet_size; 3547 int packets = length / packet_size;
3548
378 if (last != 0) { 3549 if (last != 0) {
379 packets++; 3550 packets++;
3551 } else {
3552 last = packet_size;
380 } 3553 }
381 3554
382 /* initiate transfer */ 3555 /* initiate transfer */
383 if (recovery_mode) { 3556 if (legacy_recovery_mode) {
384 error = libusb_control_transfer(client->handle, 0x41, 0, 0, 0, NULL, 0, 1000); 3557 int bytes = 0;
385 if (error != IRECV_E_SUCCESS) { 3558 uint32_t loadaddr0x8900 = 0x09000000;
386 return error; 3559 const char *ios1_overwrite_loadaddr = getenv("LIBIRECOVERY_IOS1_OVERWRITE_LOADADDR");
3560 if (ios1_overwrite_loadaddr) {
3561 sscanf(ios1_overwrite_loadaddr, "0x%x",&loadaddr0x8900);
3562 debug("Overwriting loadaddr requested by env var. uploading to 0x%08x\n",loadaddr0x8900);
3563 }
3564 legacyCMD cmd = {
3565 MSG_SEND_FILE,
3566 0x1234, //magic
3567 (uint32_t)length,
3568 loadaddr0x8900
3569 };
3570 if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3571 if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3572 if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
3573 } else if (recovery_mode) {
3574 error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT);
3575 } else {
3576 uint8_t state = 0;
3577 if (irecv_usb_control_transfer(client, 0xa1, 5, 0, 0, (unsigned char*)&state, 1, USB_TIMEOUT) == 1) {
3578 error = IRECV_E_SUCCESS;
3579 } else {
3580 return IRECV_E_USB_UPLOAD;
3581 }
3582 switch (state) {
3583 case 2:
3584 /* DFU IDLE */
3585 break;
3586 case 8:
3587 /* DFU WAIT RESET */
3588 if (!isiOS2) {
3589 debug("Unexpected state %d in non-iOS2 mode!, issuing ABORT\n", state);
3590 irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT);
3591 error = IRECV_E_USB_UPLOAD;
3592 }
3593 break;
3594 case 10:
3595 debug("DFU ERROR, issuing CLRSTATUS\n");
3596 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_TIMEOUT);
3597 error = IRECV_E_USB_UPLOAD;
3598 break;
3599 default:
3600 debug("Unexpected state %d, issuing ABORT\n", state);
3601 irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT);
3602 error = IRECV_E_USB_UPLOAD;
3603 break;
387 } 3604 }
388 } 3605 }
389 3606
3607 if (error != IRECV_E_SUCCESS) {
3608 return error;
3609 }
3610
390 int i = 0; 3611 int i = 0;
391 double progress = 0;
392 unsigned long count = 0; 3612 unsigned long count = 0;
393 unsigned int status = 0; 3613 unsigned int status = 0;
394 int bytes = 0; 3614 int bytes = 0;
395 for (i = 0; i < packets; i++) { 3615 for (i = 0; i < packets; i++) {
396 int size = (i + 1) < packets ? packet_size : last; 3616 int size = (i + 1) < packets ? packet_size : last;
397 3617
398 /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ 3618 if (legacy_recovery_mode) {
399 if (recovery_mode) { 3619 // Use interrupt transfer for legacy devices
400 error = libusb_bulk_transfer(client->handle, 0x04, &buffer[i * packet_size], size, &bytes, 1000); 3620 error = irecv_usb_interrupt_transfer(client, 0x05, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
3621 } else if (recovery_mode) {
3622 // Use bulk transfer for recovery mode
3623 error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
401 } else { 3624 } else {
402 bytes = libusb_control_transfer(client->handle, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000); 3625 // Use control transfer for DFU and WTF mode
3626 if (dfu_crc) {
3627 int j;
3628 for (j = 0; j < size; j++) {
3629 crc32_step(h1, buffer[i*packet_size + j]);
3630 }
3631 }
3632 if (dfu_crc && i+1 == packets) {
3633 int j;
3634 if (size+16 > packet_size) {
3635 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT);
3636 if (bytes != size) {
3637 return IRECV_E_USB_UPLOAD;
3638 }
3639 count += size;
3640 size = 0;
3641 }
3642 for (j = 0; j < 2; j++) {
3643 crc32_step(h1, dfu_xbuf[j*6 + 0]);
3644 crc32_step(h1, dfu_xbuf[j*6 + 1]);
3645 crc32_step(h1, dfu_xbuf[j*6 + 2]);
3646 crc32_step(h1, dfu_xbuf[j*6 + 3]);
3647 crc32_step(h1, dfu_xbuf[j*6 + 4]);
3648 crc32_step(h1, dfu_xbuf[j*6 + 5]);
3649 }
3650
3651 char* newbuf = (char*)malloc(size + 16);
3652 if (size > 0) {
3653 memcpy(newbuf, &buffer[i * packet_size], size);
3654 }
3655 memcpy(newbuf+size, dfu_xbuf, 12);
3656 newbuf[size+12] = h1 & 0xFF;
3657 newbuf[size+13] = (h1 >> 8) & 0xFF;
3658 newbuf[size+14] = (h1 >> 16) & 0xFF;
3659 newbuf[size+15] = (h1 >> 24) & 0xFF;
3660 size += 16;
3661 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, (unsigned char*)newbuf, size, USB_TIMEOUT);
3662 free(newbuf);
3663 } else {
3664 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT);
3665 }
403 } 3666 }
404 3667
405 if (bytes != size) { 3668 if (bytes != size) {
@@ -415,44 +3678,92 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
415 } 3678 }
416 3679
417 if (!recovery_mode && status != 5) { 3680 if (!recovery_mode && status != 5) {
418 return IRECV_E_USB_UPLOAD; 3681 int retry = 0;
3682
3683 while (retry++ < 20) {
3684 irecv_get_status(client, &status);
3685 if (status == 5) {
3686 break;
3687 }
3688 sleep(1);
3689 }
3690
3691 if (status != 5) {
3692 return IRECV_E_USB_UPLOAD;
3693 }
419 } 3694 }
420 3695
421 count += size; 3696 count += size;
422 if(client->progress_callback != NULL) { 3697 if (client->progress_callback != NULL) {
423 irecv_event_t event; 3698 irecv_event_t event;
424 event.progress = ((double) count/ (double) length) * 100.0; 3699 event.progress = ((double) count/ (double) length) * 100.0;
425 event.type = IRECV_PROGRESS; 3700 event.type = IRECV_PROGRESS;
426 event.data = "Uploading"; 3701 event.data = (char*)"Uploading";
427 event.size = count; 3702 event.size = count;
428 client->progress_callback(client, &event); 3703 client->progress_callback(client, &event);
429 } else { 3704 } else {
430 debug("Sent: %d bytes - %d of %d\n", bytes, count, length); 3705 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
431 } 3706 }
432 } 3707 }
433 3708
434 if (!recovery_mode) { 3709 if (recovery_mode && length % 512 == 0) {
435 libusb_control_transfer(client->handle, 0x21, 1, 0, 0, buffer, 0, 1000); 3710 /* send a ZLP */
436 for (i = 0; i < 3; i++) { 3711 bytes = 0;
3712 irecv_usb_bulk_transfer(client, 0x04, buffer, 0, &bytes, USB_TIMEOUT);
3713 }
3714
3715 if ((options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) && !recovery_mode) {
3716 irecv_usb_control_transfer(client, 0x21, 1, packets, 0, (unsigned char*) buffer, 0, USB_TIMEOUT);
3717
3718 for (i = 0; i < 2; i++) {
437 error = irecv_get_status(client, &status); 3719 error = irecv_get_status(client, &status);
438 if (error != IRECV_E_SUCCESS) { 3720 if (error != IRECV_E_SUCCESS) {
439 return error; 3721 return error;
440 } 3722 }
441 } 3723 }
3724
3725 if ((options & IRECV_SEND_OPT_DFU_FORCE_ZLP)) {
3726 /* we send a pseudo ZLP here just in case */
3727 irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT);
3728 }
3729
3730 irecv_reset(client);
3731
3732 if (isiOS2) {
3733 irecv_reconnect(client, 0);
3734 }
3735 }
3736
3737 if (legacy_recovery_mode) {
3738 irecv_reconnect(client, 0);
3739 char cmdstr[0x100] = {0};
3740 snprintf(cmdstr, sizeof(cmdstr), "setenv filesize %d", (int)length);
3741 irecv_send_command(client, cmdstr);
442 } 3742 }
443 3743
444 return IRECV_E_SUCCESS; 3744 return IRECV_E_SUCCESS;
3745#endif
445} 3746}
446 3747
447irecv_error_t irecv_receive(irecv_client_t client) { 3748irecv_error_t irecv_receive(irecv_client_t client)
448 unsigned char buffer[BUFFER_SIZE]; 3749{
3750#ifdef USE_DUMMY
3751 return IRECV_E_UNSUPPORTED;
3752#else
3753 char buffer[BUFFER_SIZE];
449 memset(buffer, '\0', BUFFER_SIZE); 3754 memset(buffer, '\0', BUFFER_SIZE);
450 if (client == NULL || client->handle == NULL) { 3755
3756 if (check_context(client) != IRECV_E_SUCCESS)
451 return IRECV_E_NO_DEVICE; 3757 return IRECV_E_NO_DEVICE;
452 }
453 3758
454 int bytes = 0; 3759 int bytes = 0;
455 while (libusb_bulk_transfer(client->handle, 0x81, buffer, BUFFER_SIZE, &bytes, 100) == 0) { 3760 while (1) {
3761 irecv_usb_set_interface(client, 1, 1);
3762 int r = irecv_usb_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 500);
3763 irecv_usb_set_interface(client, 0, 0);
3764 if (r != 0) {
3765 break;
3766 }
456 if (bytes > 0) { 3767 if (bytes > 0) {
457 if (client->received_callback != NULL) { 3768 if (client->received_callback != NULL) {
458 irecv_event_t event; 3769 irecv_event_t event;
@@ -460,155 +3771,295 @@ irecv_error_t irecv_receive(irecv_client_t client) {
460 event.data = buffer; 3771 event.data = buffer;
461 event.type = IRECV_RECEIVED; 3772 event.type = IRECV_RECEIVED;
462 if (client->received_callback(client, &event) != 0) { 3773 if (client->received_callback(client, &event) != 0) {
463 return IRECV_E_SUCCESS; 3774 break;
464 } 3775 }
465 } 3776 }
466 } else break; 3777 } else break;
467 } 3778 }
468
469 return IRECV_E_SUCCESS; 3779 return IRECV_E_SUCCESS;
3780#endif
470} 3781}
471 3782
472irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { 3783irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value)
3784{
3785#ifdef USE_DUMMY
3786 return IRECV_E_UNSUPPORTED;
3787#else
473 char command[256]; 3788 char command[256];
474 if (client == NULL || client->handle == NULL) { 3789
3790 if (check_context(client) != IRECV_E_SUCCESS)
475 return IRECV_E_NO_DEVICE; 3791 return IRECV_E_NO_DEVICE;
476 }
477 3792
478 *value = NULL; 3793 *value = NULL;
479 3794
480 if(variable == NULL) { 3795 if (variable == NULL) {
481 return IRECV_E_UNKNOWN_ERROR; 3796 return IRECV_E_INVALID_INPUT;
3797 }
3798
3799 if (client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3800 debug("iOS 1 doesn't support getenv\n");
3801 return IRECV_E_UNSUPPORTED;
482 } 3802 }
483 3803
484 memset(command, '\0', sizeof(command)); 3804 memset(command, '\0', sizeof(command));
485 snprintf(command, sizeof(command)-1, "getenv %s", variable); 3805 snprintf(command, sizeof(command)-1, "getenv %s", variable);
486 irecv_error_t error = irecv_send_command_raw(client, command); 3806 irecv_error_t error = irecv_send_command_raw(client, command, 0);
487 if(error == IRECV_E_PIPE) 3807 if (error == IRECV_E_PIPE) {
488 return IRECV_E_SUCCESS; 3808 return IRECV_E_SUCCESS;
489 if(error != IRECV_E_SUCCESS) 3809 }
3810
3811 if (error != IRECV_E_SUCCESS) {
490 return error; 3812 return error;
3813 }
491 3814
492 unsigned char* response = (unsigned char*) malloc(256); 3815 int rsize = 256;
3816 char* response = (char*) malloc(rsize);
493 if (response == NULL) { 3817 if (response == NULL) {
494 return IRECV_E_OUT_OF_MEMORY; 3818 return IRECV_E_OUT_OF_MEMORY;
495 } 3819 }
496 3820
497 memset(response, '\0', 256); 3821 memset(response, '\0', rsize);
498 int ret = libusb_control_transfer(client->handle, 0xC0, 0, 0, 0, response, 255, 500); 3822 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
499 if (ret < 0)
500 return IRECV_E_UNKNOWN_ERROR;
501 3823
502 *value = response; 3824 *value = response;
3825
503 return IRECV_E_SUCCESS; 3826 return IRECV_E_SUCCESS;
3827#endif
504} 3828}
505 3829
506irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) { 3830irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value)
507 if (client == NULL || client->handle == NULL) { 3831{
3832#ifdef USE_DUMMY
3833 return IRECV_E_UNSUPPORTED;
3834#else
3835 if (check_context(client) != IRECV_E_SUCCESS)
508 return IRECV_E_NO_DEVICE; 3836 return IRECV_E_NO_DEVICE;
509 }
510 3837
511 unsigned char* cpid_string = strstr(client->serial, "CPID:"); 3838 *value = 0;
512 if (cpid_string == NULL) { 3839
513 *cpid = 0; 3840 int rsize = 256;
514 return IRECV_E_UNKNOWN_ERROR; 3841 char* response = (char*) malloc(rsize);
3842 if (response == NULL) {
3843 return IRECV_E_OUT_OF_MEMORY;
515 } 3844 }
516 sscanf(cpid_string, "CPID:%d", cpid); 3845
3846 memset(response, '\0', rsize);
3847 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
3848
3849 *value = (unsigned int) *response;
517 3850
518 return IRECV_E_SUCCESS; 3851 return IRECV_E_SUCCESS;
3852#endif
519} 3853}
520 3854
521irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) { 3855irecv_error_t irecv_get_mode(irecv_client_t client, int* mode)
522 if (client == NULL || client->handle == NULL) { 3856{
3857#ifdef USE_DUMMY
3858 return IRECV_E_UNSUPPORTED;
3859#else
3860 if (check_context(client) != IRECV_E_SUCCESS)
523 return IRECV_E_NO_DEVICE; 3861 return IRECV_E_NO_DEVICE;
524 }
525 3862
526 unsigned char* bdid_string = strstr(client->serial, "BDID:"); 3863 *mode = client->mode;
527 if (bdid_string == NULL) {
528 *bdid = 0;
529 return IRECV_E_UNKNOWN_ERROR;
530 }
531 sscanf(bdid_string, "BDID:%d", bdid);
532 3864
533 return IRECV_E_SUCCESS; 3865 return IRECV_E_SUCCESS;
3866#endif
534} 3867}
535 3868
536irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) { 3869const struct irecv_device_info* irecv_get_device_info(irecv_client_t client)
537 if (client == NULL || client->handle == NULL) { 3870{
538 return IRECV_E_NO_DEVICE; 3871#ifdef USE_DUMMY
539 } 3872 return NULL;
540 3873#else
541 unsigned char* ecid_string = strstr(client->serial, "ECID:"); 3874 if (check_context(client) != IRECV_E_SUCCESS)
542 if (ecid_string == NULL) { 3875 return NULL;
543 *ecid = 0;
544 return IRECV_E_UNKNOWN_ERROR;
545 }
546 sscanf(ecid_string, "ECID:%qX", ecid);
547 3876
548 return IRECV_E_SUCCESS; 3877 return &client->device_info;
3878#endif
549} 3879}
550 3880
551irecv_error_t irecv_send_exploit(irecv_client_t client) { 3881#ifndef USE_DUMMY
552 if (client == NULL || client->handle == NULL) { 3882#ifdef HAVE_IOKIT
3883static void *iokit_limera1n_usb_submit_request(void *argv)
3884{
3885 void **args = argv;
3886 IOUSBDeviceInterface320 **dev = args[0];
3887 IOUSBDevRequest *req = args[1];
3888
3889 IOReturn result = (*dev)->DeviceRequest(dev, req);
3890 if (result != kIOReturnSuccess)
3891 debug("%s result: %#x\n", __func__, result);
3892
3893 return NULL;
3894}
3895#endif
3896#endif
3897
3898irecv_error_t irecv_trigger_limera1n_exploit(irecv_client_t client)
3899{
3900#ifdef USE_DUMMY
3901 return IRECV_E_UNSUPPORTED;
3902#else
3903 if (check_context(client) != IRECV_E_SUCCESS)
553 return IRECV_E_NO_DEVICE; 3904 return IRECV_E_NO_DEVICE;
3905
3906#ifdef HAVE_IOKIT
3907 IOReturn result;
3908 IOUSBDevRequestTO req;
3909 bzero(&req, sizeof(req));
3910
3911 req.bmRequestType = 0x21;
3912 req.bRequest = 2;
3913 req.wValue = 0;
3914 req.wIndex = 0;
3915 req.wLength = 0;
3916 req.pData = NULL;
3917 req.noDataTimeout = USB_TIMEOUT;
3918 req.completionTimeout = USB_TIMEOUT;
3919
3920 // The original version uses an async request, but we don't have an async event
3921 // source set up. The hack relies on aborting the transaction before it times out,
3922 // which can be accomplished by sending on another thread.
3923
3924 void *args[2] = { client->handle, &req };
3925 THREAD_T thread;
3926 thread_new(&thread, iokit_limera1n_usb_submit_request, args);
3927
3928 usleep(5 * 1000);
3929 result = (*client->handle)->USBDeviceAbortPipeZero(client->handle);
3930 if (result != kIOReturnSuccess)
3931 debug("USBDeviceAbortPipeZero returned %#x\n", result);
3932
3933 switch (result) {
3934 case kIOReturnSuccess: return req.wLenDone;
3935 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
3936 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
3937 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
3938 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
3939 default:
3940 return IRECV_E_UNKNOWN_ERROR;
554 } 3941 }
3942#else
3943 irecv_usb_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, USB_TIMEOUT);
3944#endif
555 3945
556 libusb_control_transfer(client->handle, 0x21, 2, 0, 0, NULL, 0, 100);
557 return IRECV_E_SUCCESS; 3946 return IRECV_E_SUCCESS;
3947#endif
558} 3948}
559 3949
560irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) { 3950irecv_error_t irecv_execute_script(irecv_client_t client, const char* script)
3951{
3952#ifdef USE_DUMMY
3953 return IRECV_E_UNSUPPORTED;
3954#else
561 irecv_error_t error = IRECV_E_SUCCESS; 3955 irecv_error_t error = IRECV_E_SUCCESS;
562 if (client == NULL || client->handle == NULL) { 3956 if (check_context(client) != IRECV_E_SUCCESS)
563 return IRECV_E_NO_DEVICE; 3957 return IRECV_E_NO_DEVICE;
564 }
565 3958
566 int file_size = 0; 3959 char* body = strdup(script);
567 char* file_data = NULL; 3960 char* line = strtok(body, "\n");
568 if(irecv_read_file(filename, &file_data, &file_size) < 0) {
569 return IRECV_E_FILE_NOT_FOUND;
570 }
571 3961
572 char* line = strtok(file_data, "\n"); 3962 while (line != NULL) {
573 while(line != NULL) { 3963 if (line[0] != '#') {
574 if(line[0] != '#') {
575 error = irecv_send_command(client, line); 3964 error = irecv_send_command(client, line);
576 if(error != IRECV_E_SUCCESS) { 3965 if (error != IRECV_E_SUCCESS) {
577 return error; 3966 break;
578 } 3967 }
579 3968
580 error = irecv_receive(client); 3969 error = irecv_receive(client);
581 if(error != IRECV_E_SUCCESS) { 3970 if (error != IRECV_E_SUCCESS) {
582 return error; 3971 break;
583 } 3972 }
584 } 3973 }
585 line = strtok(NULL, "\n"); 3974 line = strtok(NULL, "\n");
586 } 3975 }
587 3976
3977 free(body);
3978
3979 return error;
3980#endif
3981}
3982
3983irecv_error_t irecv_saveenv(irecv_client_t client)
3984{
3985#ifdef USE_DUMMY
3986 return IRECV_E_UNSUPPORTED;
3987#else
3988 irecv_error_t error = irecv_send_command_raw(client, "saveenv", 0);
3989 if (error != IRECV_E_SUCCESS) {
3990 return error;
3991 }
3992
588 return IRECV_E_SUCCESS; 3993 return IRECV_E_SUCCESS;
3994#endif
589} 3995}
590 3996
591irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) { 3997irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value)
3998{
3999#ifdef USE_DUMMY
4000 return IRECV_E_UNSUPPORTED;
4001#else
592 char command[256]; 4002 char command[256];
593 if (client == NULL || client->handle == NULL) { 4003
4004 if (check_context(client) != IRECV_E_SUCCESS)
594 return IRECV_E_NO_DEVICE; 4005 return IRECV_E_NO_DEVICE;
595 }
596 4006
597 if(variable == NULL || value == NULL) { 4007 if (variable == NULL || value == NULL) {
598 return IRECV_E_UNKNOWN_ERROR; 4008 return IRECV_E_UNKNOWN_ERROR;
599 } 4009 }
600 4010
601 memset(command, '\0', sizeof(command)); 4011 memset(command, '\0', sizeof(command));
602 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value); 4012 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value);
603 irecv_error_t error = irecv_send_command_raw(client, command); 4013 irecv_error_t error = irecv_send_command_raw(client, command, 0);
604 if(error != IRECV_E_SUCCESS) { 4014 if (error != IRECV_E_SUCCESS) {
4015 return error;
4016 }
4017
4018 return IRECV_E_SUCCESS;
4019#endif
4020}
4021
4022irecv_error_t irecv_setenv_np(irecv_client_t client, const char* variable, const char* value)
4023{
4024#ifdef USE_DUMMY
4025 return IRECV_E_UNSUPPORTED;
4026#else
4027 char command[256];
4028
4029 if (check_context(client) != IRECV_E_SUCCESS)
4030 return IRECV_E_NO_DEVICE;
4031
4032 if (variable == NULL || value == NULL) {
4033 return IRECV_E_UNKNOWN_ERROR;
4034 }
4035
4036 memset(command, '\0', sizeof(command));
4037 snprintf(command, sizeof(command)-1, "setenvnp %s %s", variable, value);
4038 irecv_error_t error = irecv_send_command_raw(client, command, 0);
4039 if (error != IRECV_E_SUCCESS) {
4040 return error;
4041 }
4042
4043 return IRECV_E_SUCCESS;
4044#endif
4045}
4046
4047irecv_error_t irecv_reboot(irecv_client_t client)
4048{
4049#ifdef USE_DUMMY
4050 return IRECV_E_UNSUPPORTED;
4051#else
4052 irecv_error_t error = irecv_send_command_raw(client, "reboot", 0);
4053 if (error != IRECV_E_SUCCESS) {
605 return error; 4054 return error;
606 } 4055 }
607 4056
608 return IRECV_E_SUCCESS; 4057 return IRECV_E_SUCCESS;
4058#endif
609} 4059}
610 4060
611const char* irecv_strerror(irecv_error_t error) { 4061const char* irecv_strerror(irecv_error_t error)
4062{
612 switch (error) { 4063 switch (error) {
613 case IRECV_E_SUCCESS: 4064 case IRECV_E_SUCCESS:
614 return "Command completed successfully"; 4065 return "Command completed successfully";
@@ -646,6 +4097,9 @@ const char* irecv_strerror(irecv_error_t error) {
646 case IRECV_E_TIMEOUT: 4097 case IRECV_E_TIMEOUT:
647 return "Timeout talking to device"; 4098 return "Timeout talking to device";
648 4099
4100 case IRECV_E_UNSUPPORTED:
4101 return "Operation unsupported by driver";
4102
649 default: 4103 default:
650 return "Unknown error"; 4104 return "Unknown error";
651 } 4105 }
@@ -653,64 +4107,220 @@ const char* irecv_strerror(irecv_error_t error) {
653 return NULL; 4107 return NULL;
654} 4108}
655 4109
656int irecv_write_file(const char* filename, const void* data, size_t size) { 4110irecv_error_t irecv_reset_counters(irecv_client_t client)
657 size_t bytes = 0; 4111{
658 FILE* file = NULL; 4112#ifdef USE_DUMMY
4113 return IRECV_E_UNSUPPORTED;
4114#else
4115 if (check_context(client) != IRECV_E_SUCCESS)
4116 return IRECV_E_NO_DEVICE;
659 4117
660 debug("Writing data to %s\n", filename); 4118 if ((client->mode == IRECV_K_DFU_MODE) || (client->mode == IRECV_K_PORT_DFU_MODE) || (client->mode == IRECV_K_WTF_MODE)) {
661 file = fopen(filename, "wb"); 4119 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, 0, 0, USB_TIMEOUT);
662 if (file == NULL) {
663 error("read_file: Unable to open file %s\n", filename);
664 return -1;
665 } 4120 }
666 4121
667 bytes = fwrite(data, 1, size, file); 4122 return IRECV_E_SUCCESS;
668 fclose(file); 4123#endif
4124}
669 4125
670 if (bytes != size) { 4126irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length)
671 error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size); 4127{
672 return -1; 4128#ifdef USE_DUMMY
4129 return IRECV_E_UNSUPPORTED;
4130#else
4131 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
4132
4133 if (check_context(client) != IRECV_E_SUCCESS)
4134 return IRECV_E_NO_DEVICE;
4135
4136 int packet_size = recovery_mode ? 0x2000: 0x800;
4137 int last = length % packet_size;
4138 int packets = length / packet_size;
4139 if (last != 0) {
4140 packets++;
4141 } else {
4142 last = packet_size;
673 } 4143 }
674 4144
675 return size; 4145 int i = 0;
4146 int bytes = 0;
4147 unsigned long count = 0;
4148 for (i = 0; i < packets; i++) {
4149 unsigned short size = (i+1) < packets ? packet_size : last;
4150 bytes = irecv_usb_control_transfer(client, 0xA1, 2, 0, 0, (unsigned char*)&buffer[i * packet_size], size, USB_TIMEOUT);
4151
4152 if (bytes != size) {
4153 return IRECV_E_USB_UPLOAD;
4154 }
4155
4156 count += size;
4157 if (client->progress_callback != NULL) {
4158 irecv_event_t event;
4159 event.progress = ((double) count/ (double) length) * 100.0;
4160 event.type = IRECV_PROGRESS;
4161 event.data = (char*)"Downloading";
4162 event.size = count;
4163 client->progress_callback(client, &event);
4164 } else {
4165 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
4166 }
4167 }
4168
4169 return IRECV_E_SUCCESS;
4170#endif
676} 4171}
677 4172
678int irecv_read_file(const char* filename, char** data, uint32_t* size) { 4173irecv_error_t irecv_finish_transfer(irecv_client_t client)
679 size_t bytes = 0; 4174{
680 size_t length = 0; 4175#ifdef USE_DUMMY
681 FILE* file = NULL; 4176 return IRECV_E_UNSUPPORTED;
682 char* buffer = NULL; 4177#else
683 debug("Reading data from %s\n", filename); 4178 int i = 0;
4179 unsigned int status = 0;
684 4180
685 *size = 0; 4181 if (check_context(client) != IRECV_E_SUCCESS)
686 *data = NULL; 4182 return IRECV_E_NO_DEVICE;
687 4183
688 file = fopen(filename, "rb"); 4184 irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT);
689 if (file == NULL) { 4185
690 error("read_file: File %s not found\n", filename); 4186 for (i = 0; i < 3; i++){
691 return -1; 4187 irecv_get_status(client, &status);
692 } 4188 }
693 4189
694 fseek(file, 0, SEEK_END); 4190 irecv_reset(client);
695 length = ftell(file);
696 rewind(file);
697 4191
698 buffer = (char*) malloc(length); 4192 return IRECV_E_SUCCESS;
699 if(buffer == NULL) { 4193#endif
700 error("ERROR: Out of memory\n"); 4194}
701 fclose(file); 4195
702 return -1; 4196irecv_device_t irecv_devices_get_all(void)
4197{
4198 return irecv_devices;
4199}
4200
4201irecv_error_t irecv_devices_get_device_by_client(irecv_client_t client, irecv_device_t* device)
4202{
4203#ifdef USE_DUMMY
4204 return IRECV_E_UNSUPPORTED;
4205#else
4206 int i = 0;
4207
4208 if (!client || !device)
4209 return IRECV_E_INVALID_INPUT;
4210
4211 *device = NULL;
4212
4213 if (client->device_info.cpid == 0) {
4214 return IRECV_E_UNKNOWN_ERROR;
703 } 4215 }
704 bytes = fread(buffer, 1, length, file);
705 fclose(file);
706 4216
707 if(bytes != length) { 4217 unsigned int cpid_match = client->device_info.cpid;
708 error("ERROR: Unable to read entire file\n"); 4218 unsigned int bdid_match = client->device_info.bdid;
709 free(buffer); 4219 if (client->mode == IRECV_K_PORT_DFU_MODE) {
710 return -1; 4220 cpid_match = (client->device_info.bdid >> 8) & 0xFFFF;
4221 bdid_match = (client->device_info.bdid >> 24) & 0xFF;
711 } 4222 }
712 4223
713 *size = length; 4224 for (i = 0; irecv_devices[i].hardware_model != NULL; i++) {
714 *data = buffer; 4225 if (irecv_devices[i].chip_id == cpid_match && irecv_devices[i].board_id == bdid_match) {
715 return 0; 4226 *device = &irecv_devices[i];
4227 return IRECV_E_SUCCESS;
4228 }
4229 }
4230
4231 return IRECV_E_NO_DEVICE;
4232#endif
4233}
4234
4235irecv_error_t irecv_devices_get_device_by_product_type(const char* product_type, irecv_device_t* device)
4236{
4237 int i = 0;
4238
4239 if (!product_type || !device)
4240 return IRECV_E_INVALID_INPUT;
4241
4242 *device = NULL;
4243
4244 for (i = 0; irecv_devices[i].product_type != NULL; i++) {
4245 if (!strcmp(product_type, irecv_devices[i].product_type)) {
4246 *device = &irecv_devices[i];
4247 return IRECV_E_SUCCESS;
4248 }
4249 }
4250
4251 return IRECV_E_NO_DEVICE;
4252}
4253
4254irecv_error_t irecv_devices_get_device_by_hardware_model(const char* hardware_model, irecv_device_t* device)
4255{
4256 int i = 0;
4257
4258 if (!hardware_model || !device)
4259 return IRECV_E_INVALID_INPUT;
4260
4261 *device = NULL;
4262
4263 for (i = 0; irecv_devices[i].hardware_model != NULL; i++) {
4264 if (!strcasecmp(hardware_model, irecv_devices[i].hardware_model)) {
4265 *device = &irecv_devices[i];
4266 return IRECV_E_SUCCESS;
4267 }
4268 }
4269
4270 return IRECV_E_NO_DEVICE;
4271}
4272
4273irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause)
4274{
4275#ifdef USE_DUMMY
4276 return NULL;
4277#else
4278 irecv_error_t error = 0;
4279 irecv_client_t new_client = NULL;
4280 irecv_event_cb_t progress_callback = client->progress_callback;
4281 irecv_event_cb_t received_callback = client->received_callback;
4282 irecv_event_cb_t connected_callback = client->connected_callback;
4283 irecv_event_cb_t precommand_callback = client->precommand_callback;
4284 irecv_event_cb_t postcommand_callback = client->postcommand_callback;
4285 irecv_event_cb_t disconnected_callback = client->disconnected_callback;
4286
4287 uint64_t ecid = client->device_info.ecid;
4288
4289 if (check_context(client) == IRECV_E_SUCCESS) {
4290 irecv_cleanup(client);
4291 }
4292
4293 if (initial_pause > 0) {
4294 debug("Waiting %d seconds for the device to pop up...\n", initial_pause);
4295 sleep(initial_pause);
4296 }
4297
4298 error = irecv_open_with_ecid_and_attempts(&new_client, ecid, 10);
4299 if (error != IRECV_E_SUCCESS) {
4300 return NULL;
4301 }
4302
4303 new_client->progress_callback = progress_callback;
4304 new_client->received_callback = received_callback;
4305 new_client->connected_callback = connected_callback;
4306 new_client->precommand_callback = precommand_callback;
4307 new_client->postcommand_callback = postcommand_callback;
4308 new_client->disconnected_callback = disconnected_callback;
4309
4310 // keep old handle valid
4311 memcpy(client, new_client, sizeof(*client));
4312 free(new_client);
4313 new_client = client;
4314
4315 if (new_client->connected_callback != NULL) {
4316 irecv_event_t event;
4317 event.size = 0;
4318 event.data = NULL;
4319 event.progress = 0;
4320 event.type = IRECV_CONNECTED;
4321 new_client->connected_callback(new_client, &event);
4322 }
4323
4324 return new_client;
4325#endif
716} 4326}