summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml3
-rw-r--r--.github/workflows/build.yml19
-rw-r--r--NEWS47
-rw-r--r--README.md17
-rw-r--r--configure.ac35
-rw-r--r--include/libirecovery.h10
-rw-r--r--src/libirecovery.c562
-rw-r--r--tools/Makefile.am5
-rw-r--r--tools/irecovery.c67
9 files changed, 584 insertions, 181 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..e995b30
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
1github: nikias
2patreon: nikias
3custom: ["https://www.paypal.me/NikiasBassen"]
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9d36f2a..600e9c6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,14 +17,14 @@ jobs:
17 run: | 17 run: |
18 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV 18 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
19 - name: fetch libplist 19 - name: fetch libplist
20 uses: dawidd6/action-download-artifact@v3 20 uses: dawidd6/action-download-artifact@v6
21 with: 21 with:
22 github_token: ${{secrets.GITHUB_TOKEN}} 22 github_token: ${{secrets.GITHUB_TOKEN}}
23 workflow: build.yml 23 workflow: build.yml
24 name: libplist-latest_${{env.target_triplet}} 24 name: libplist-latest_${{env.target_triplet}}
25 repo: libimobiledevice/libplist 25 repo: libimobiledevice/libplist
26 - name: fetch libimobiledevice-glue 26 - name: fetch libimobiledevice-glue
27 uses: dawidd6/action-download-artifact@v3 27 uses: dawidd6/action-download-artifact@v6
28 with: 28 with:
29 github_token: ${{secrets.GITHUB_TOKEN}} 29 github_token: ${{secrets.GITHUB_TOKEN}}
30 workflow: build.yml 30 workflow: build.yml
@@ -49,7 +49,7 @@ jobs:
49 run: | 49 run: |
50 mkdir -p dest 50 mkdir -p dest
51 DESTDIR=`pwd`/dest make install 51 DESTDIR=`pwd`/dest make install
52 tar -C dest -cf libirecovery.tar lib usr 52 tar -C dest -cf libirecovery.tar --strip-components 1 .
53 - name: publish artifact 53 - name: publish artifact
54 uses: actions/upload-artifact@v4 54 uses: actions/upload-artifact@v4
55 with: 55 with:
@@ -67,14 +67,14 @@ jobs:
67 fi 67 fi
68 shell: bash 68 shell: bash
69 - name: fetch libplist 69 - name: fetch libplist
70 uses: dawidd6/action-download-artifact@v3 70 uses: dawidd6/action-download-artifact@v6
71 with: 71 with:
72 github_token: ${{secrets.GITHUB_TOKEN}} 72 github_token: ${{secrets.GITHUB_TOKEN}}
73 workflow: build.yml 73 workflow: build.yml
74 name: libplist-latest_macOS 74 name: libplist-latest_macOS
75 repo: libimobiledevice/libplist 75 repo: libimobiledevice/libplist
76 - name: fetch libimobiledevice-glue 76 - name: fetch libimobiledevice-glue
77 uses: dawidd6/action-download-artifact@v3 77 uses: dawidd6/action-download-artifact@v6
78 with: 78 with:
79 github_token: ${{secrets.GITHUB_TOKEN}} 79 github_token: ${{secrets.GITHUB_TOKEN}}
80 workflow: build.yml 80 workflow: build.yml
@@ -109,14 +109,14 @@ jobs:
109 run: | 109 run: |
110 mkdir -p dest 110 mkdir -p dest
111 DESTDIR=`pwd`/dest make install 111 DESTDIR=`pwd`/dest make install
112 tar -C dest -cf libirecovery.tar usr 112 tar -C dest -cf libirecovery.tar --strip-components 1 .
113 - name: publish artifact 113 - name: publish artifact
114 uses: actions/upload-artifact@v4 114 uses: actions/upload-artifact@v4
115 with: 115 with:
116 name: libirecovery-latest_macOS 116 name: libirecovery-latest_macOS
117 path: libirecovery.tar 117 path: libirecovery.tar
118 build-windows: 118 build-windows:
119 runs-on: windows-2019 119 runs-on: windows-latest
120 defaults: 120 defaults:
121 run: 121 run:
122 shell: msys2 {0} 122 shell: msys2 {0}
@@ -137,6 +137,7 @@ jobs:
137 base-devel 137 base-devel
138 git 138 git
139 mingw-w64-${{ matrix.arch }}-gcc 139 mingw-w64-${{ matrix.arch }}-gcc
140 mingw-w64-${{ matrix.arch }}-pkg-config
140 make 141 make
141 libtool 142 libtool
142 autoconf 143 autoconf
@@ -147,14 +148,14 @@ jobs:
147 echo "dest=$dest" >> $GITHUB_ENV 148 echo "dest=$dest" >> $GITHUB_ENV
148 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV 149 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
149 - name: fetch libplist 150 - name: fetch libplist
150 uses: dawidd6/action-download-artifact@v3 151 uses: dawidd6/action-download-artifact@v6
151 with: 152 with:
152 github_token: ${{secrets.GITHUB_TOKEN}} 153 github_token: ${{secrets.GITHUB_TOKEN}}
153 workflow: build.yml 154 workflow: build.yml
154 name: libplist-latest_${{ matrix.arch }}-${{ env.dest }} 155 name: libplist-latest_${{ matrix.arch }}-${{ env.dest }}
155 repo: libimobiledevice/libplist 156 repo: libimobiledevice/libplist
156 - name: fetch libimobiledevice-glue 157 - name: fetch libimobiledevice-glue
157 uses: dawidd6/action-download-artifact@v3 158 uses: dawidd6/action-download-artifact@v6
158 with: 159 with:
159 github_token: ${{secrets.GITHUB_TOKEN}} 160 github_token: ${{secrets.GITHUB_TOKEN}}
160 workflow: build.yml 161 workflow: build.yml
diff --git a/NEWS b/NEWS
index cb06ea6..f7e2c2f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,50 @@
1Version 1.3.1
2~~~~~~~~~~~~~
3
4* Device database changes:
5 - Support iPad Pro M5 family devices
6 - Support Apple Vision Pro M5
7 - Support MacBook Pro 14-inch M5
8
9* Bug Fixes:
10 Fix: array initialization compatibility with MSVC
11
12
13Version 1.3.0
14~~~~~~~~~~~~~
15
16* Changes:
17 - Switch to better initializer strategy
18 - Allow building without readline support for the irecovery tool
19 - Added support for iOS 1 and iOS 2 recovery mode (Linux, macOS)
20
21* Device database changes:
22 - Support iPhone 17 family devices
23 - Support Watch 11 / SE3 / Ultra3 models
24 - Support March 2025 iPad and Mac models
25 - Support iPhone 16e
26 - Support November 2024 Mac models
27 - Support iPad mini (A17 Pro)
28
29* Bug Fixes:
30 - IOKit: Fix race condition when trying to delete runloop before it even started
31
32
33Version 1.2.1
34~~~~~~~~~~~~~
35
36* Changes:
37 - Make sure IRECV_DEVICE_REMOVE event has the mode set the device was in
38 - KIS: Add some retry loops around open/set config/set interface operations
39
40* Device database changes:
41 - Support Apple Watch Series 10 and iPhone 16 models
42 - Add iPad Air (M2) and iPad Pro (M4) models
43
44* Bug Fixes:
45 - Windows: Fix crash due to access to uninitialized data
46
47
1Version 1.2.0 48Version 1.2.0
2~~~~~~~~~~~~~ 49~~~~~~~~~~~~~
3 50
diff --git a/README.md b/README.md
index ac49f5d..be5eb02 100644
--- a/README.md
+++ b/README.md
@@ -39,8 +39,9 @@ have common build steps across different platforms.
39Only the prerequisites differ and they are described in this section. 39Only the prerequisites differ and they are described in this section.
40 40
41libirecovery requires [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue). 41libirecovery requires [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue).
42Some platforms already provide it as a package.
42Check the [Building](https://github.com/libimobiledevice/libimobiledevice-glue?tab=readme-ov-file#building) 43Check the [Building](https://github.com/libimobiledevice/libimobiledevice-glue?tab=readme-ov-file#building)
43section of the README on how to build it. Note that some platforms might have it as a package. 44section of the README on how to build it.
44 45
45#### Linux (Debian/Ubuntu based) 46#### Linux (Debian/Ubuntu based)
46 47
@@ -68,12 +69,12 @@ section of the README on how to build it. Note that some platforms might have it
68 69
69 Using MacPorts: 70 Using MacPorts:
70 ```shell 71 ```shell
71 sudo port install libtool autoconf automake pkgconfig 72 sudo port install libtool autoconf automake pkgconfig libimobiledevice-glue
72 ``` 73 ```
73 74
74 Using Homebrew: 75 Using Homebrew:
75 ```shell 76 ```shell
76 brew install libtool autoconf automake pkg-config 77 brew install libtool autoconf automake pkg-config libimobiledevice-glue
77 ``` 78 ```
78 79
79#### Windows 80#### Windows
@@ -91,7 +92,9 @@ section of the README on how to build it. Note that some platforms might have it
91 libtool \ 92 libtool \
92 autoconf \ 93 autoconf \
93 automake-wrapper \ 94 automake-wrapper \
94 pkg-config 95 pkg-config \
96 mingw-w64-x86_64-libimobiledevice-glue \
97 mingw-w64-x86_64-readline
95 ``` 98 ```
96 NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly. 99 NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly.
97 100
@@ -236,8 +239,8 @@ Please make sure your contribution adheres to:
236## Links 239## Links
237 240
238* Homepage: https://libimobiledevice.org/ 241* Homepage: https://libimobiledevice.org/
239* Repository: https://git.libimobiledevice.org/libirecovery.git 242* Repository: https://github.com/libimobiledevice/libirecovery.git
240* Repository (Mirror): https://github.com/libimobiledevice/libirecovery.git 243* Repository (Mirror): https://git.libimobiledevice.org/libirecovery.git
241* Issue Tracker: https://github.com/libimobiledevice/libirecovery/issues 244* Issue Tracker: https://github.com/libimobiledevice/libirecovery/issues
242* Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel 245* Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel
243* Twitter: https://twitter.com/libimobiledev 246* Twitter: https://twitter.com/libimobiledev
@@ -255,4 +258,4 @@ iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc.
255This project is an independent software library and has not been authorized, 258This project is an independent software library and has not been authorized,
256sponsored, or otherwise approved by Apple Inc. 259sponsored, or otherwise approved by Apple Inc.
257 260
258README Updated on: 2024-03-23 261README Updated on: 2025-09-10
diff --git a/configure.ac b/configure.ac
index 94bb793..52a28b5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,7 +15,7 @@ dnl libtool versioning
15# changes to the signature and the semantic) 15# changes to the signature and the semantic)
16# ? :+1 : ? == just internal changes 16# ? :+1 : ? == just internal changes
17# CURRENT : REVISION : AGE 17# CURRENT : REVISION : AGE
18LIBIRECOVERY_SO_VERSION=5:0:0 18LIBIRECOVERY_SO_VERSION=6:2:1
19 19
20dnl Minimum package versions 20dnl Minimum package versions
21LIBUSB_VERSION=1.0.3 21LIBUSB_VERSION=1.0.3
@@ -77,35 +77,21 @@ case ${host_os} in
77esac 77esac
78AM_CONDITIONAL(WIN32, test x$win32 = xtrue) 78AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
79 79
80# Check if the C compiler supports __attribute__((constructor))
81AC_CACHE_CHECK([wether the C compiler supports constructor/destructor attributes],
82 ac_cv_attribute_constructor, [
83 ac_cv_attribute_constructor=no
84 AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
85 [[
86 static void __attribute__((constructor)) test_constructor(void) {
87 }
88 static void __attribute__((destructor)) test_destructor(void) {
89 }
90 ]], [])],
91 [ac_cv_attribute_constructor=yes]
92 )]
93)
94if test "$ac_cv_attribute_constructor" = "yes"; then
95 AC_DEFINE(HAVE_ATTRIBUTE_CONSTRUCTOR, 1, [Define if the C compiler supports constructor/destructor attributes])
96fi
97
98AC_ARG_WITH([tools], 80AC_ARG_WITH([tools],
99 [AS_HELP_STRING([--with-tools], [Build irecovery tools. (requires readline) [default=yes]])], 81 [AS_HELP_STRING([--with-tools], [Build irecovery tools. [default=yes]])],
100 [], 82 [],
101 [with_tools=yes]) 83 [with_tools=yes])
102 84
103AS_IF([test "x$with_tools" = "xyes"], [ 85AS_IF([test "x$with_tools" = "xyes"], [
86 have_readline=no
104 AC_DEFINE(BUILD_TOOLS, 1, [Define if we are building irecovery tools]) 87 AC_DEFINE(BUILD_TOOLS, 1, [Define if we are building irecovery tools])
105 AC_CHECK_HEADERS([readline/readline.h], [], 88 AC_CHECK_HEADERS([readline/readline.h],
106 [AC_MSG_ERROR([Please install readline development headers])] 89 [AC_DEFINE(HAVE_READLINE, 1, [Define if readline is available])
107 )] 90 have_readline=yes],
108) 91 [AC_MSG_NOTICE([NOTE: Building without readline support. If you want readline support, install its development package.])]
92 )
93 AM_CONDITIONAL(HAVE_READLINE, test "x$have_readline" = "xyes")
94])
109AM_CONDITIONAL(BUILD_TOOLS, test "x$with_tools" = "xyes") 95AM_CONDITIONAL(BUILD_TOOLS, test "x$with_tools" = "xyes")
110 96
111AC_ARG_WITH([dummy], 97AC_ARG_WITH([dummy],
@@ -224,6 +210,7 @@ Configuration for $PACKAGE $VERSION:
224 210
225 Install prefix: .........: $prefix 211 Install prefix: .........: $prefix
226 USB backend: ............: $USB_BACKEND 212 USB backend: ............: $USB_BACKEND
213 Build tools: ............: $with_tools
227 214
228 Now type 'make' to build $PACKAGE $VERSION, 215 Now type 'make' to build $PACKAGE $VERSION,
229 and then 'make install' for installation. 216 and then 'make install' for installation.
diff --git a/include/libirecovery.h b/include/libirecovery.h
index ec9255b..e37b060 100644
--- a/include/libirecovery.h
+++ b/include/libirecovery.h
@@ -106,6 +106,13 @@ struct irecv_device_info {
106 unsigned char* sep_nonce; 106 unsigned char* sep_nonce;
107 unsigned int sep_nonce_size; 107 unsigned int sep_nonce_size;
108 uint16_t pid; 108 uint16_t pid;
109 unsigned int have_cpid : 1;
110 unsigned int have_cprv : 1;
111 unsigned int have_cpfm : 1;
112 unsigned int have_scep : 1;
113 unsigned int have_bdid : 1;
114 unsigned int have_ecid : 1;
115 unsigned int have_ibfl : 1;
109}; 116};
110 117
111typedef enum { 118typedef enum {
@@ -132,8 +139,6 @@ enum {
132/* library */ 139/* library */
133IRECV_API void irecv_set_debug_level(int level); 140IRECV_API void irecv_set_debug_level(int level);
134IRECV_API const char* irecv_strerror(irecv_error_t error); 141IRECV_API const char* irecv_strerror(irecv_error_t error);
135IRECV_API void irecv_init(void); /* deprecated: libirecovery has constructor now */
136IRECV_API void irecv_exit(void); /* deprecated: libirecovery has destructor now */
137 142
138IRECV_API const char* irecv_version(); 143IRECV_API const char* irecv_version();
139 144
@@ -156,6 +161,7 @@ IRECV_API irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int c
156IRECV_API irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface); 161IRECV_API irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface);
157IRECV_API int 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); 162IRECV_API int 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);
158IRECV_API int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout); 163IRECV_API int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout);
164IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout);
159 165
160/* events */ 166/* events */
161typedef void(*irecv_device_event_cb_t)(const irecv_device_event_t* event, void *user_data); 167typedef void(*irecv_device_event_cb_t)(const irecv_device_event_t* event, void *user_data);
diff --git a/src/libirecovery.c b/src/libirecovery.c
index a60782c..bf9a0d6 100644
--- a/src/libirecovery.c
+++ b/src/libirecovery.c
@@ -36,7 +36,7 @@
36#include <libimobiledevice-glue/thread.h> 36#include <libimobiledevice-glue/thread.h>
37 37
38#ifndef USE_DUMMY 38#ifndef USE_DUMMY
39#ifndef WIN32 39#ifndef _WIN32
40#ifndef HAVE_IOKIT 40#ifndef HAVE_IOKIT
41#include <libusb.h> 41#include <libusb.h>
42#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102)) 42#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102))
@@ -71,6 +71,33 @@
71 71
72#include "libirecovery.h" 72#include "libirecovery.h"
73 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
74struct irecv_client_private { 101struct irecv_client_private {
75 int debug; 102 int debug;
76 int usb_config; 103 int usb_config;
@@ -80,7 +107,7 @@ struct irecv_client_private {
80 int isKIS; 107 int isKIS;
81 struct irecv_device_info device_info; 108 struct irecv_device_info device_info;
82#ifndef USE_DUMMY 109#ifndef USE_DUMMY
83#ifndef WIN32 110#ifndef _WIN32
84#ifndef HAVE_IOKIT 111#ifndef HAVE_IOKIT
85 libusb_device_handle* handle; 112 libusb_device_handle* handle;
86#else 113#else
@@ -122,7 +149,7 @@ struct irecv_client_private {
122 149
123static int libirecovery_debug = 0; 150static int libirecovery_debug = 0;
124#ifndef USE_DUMMY 151#ifndef USE_DUMMY
125#ifndef WIN32 152#ifndef _WIN32
126#ifndef HAVE_IOKIT 153#ifndef HAVE_IOKIT
127static libusb_context* libirecovery_context = NULL; 154static libusb_context* libirecovery_context = NULL;
128#endif 155#endif
@@ -187,6 +214,15 @@ static struct irecv_device irecv_devices[] = {
187 { "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" }, 214 { "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" },
188 { "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" }, 215 { "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" },
189 { "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" }, 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" },
190 /* iPod */ 226 /* iPod */
191 { "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" }, 227 { "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" },
192 { "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" }, 228 { "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" },
@@ -279,6 +315,26 @@ static struct irecv_device irecv_devices[] = {
279 { "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" }, 315 { "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" },
280 { "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" }, 316 { "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" },
281 { "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" }, 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)" },
282 /* Apple TV */ 338 /* Apple TV */
283 { "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" }, 339 { "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" },
284 { "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" }, 340 { "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" },
@@ -337,6 +393,19 @@ static struct irecv_device irecv_devices[] = {
337 { "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" }, 393 { "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" },
338 { "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" }, 394 { "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" },
339 { "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" }, 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)" },
340 /* Apple Silicon Macs */ 409 /* Apple Silicon Macs */
341 { "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" }, 410 { "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" },
342 { "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" }, 411 { "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" },
@@ -373,6 +442,20 @@ static struct irecv_device irecv_devices[] = {
373 { "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" }, 442 { "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
374 { "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" }, 443 { "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" },
375 { "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-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)" },
376 /* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */ 459 /* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */
377 { "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" }, 460 { "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" },
378 /* Apple T2 Coprocessor */ 461 /* Apple T2 Coprocessor */
@@ -396,6 +479,7 @@ static struct irecv_device irecv_devices[] = {
396 { "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" }, 479 { "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" },
397 /* Apple Vision Pro */ 480 /* Apple Vision Pro */
398 { "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" }, 481 { "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" },
482 { "RealityDevice17,1", "n301aap", 0x42, 0x8142, "Apple Vision Pro (M5)" },
399 { NULL, NULL, -1, -1, NULL } 483 { NULL, NULL, -1, -1, NULL }
400}; 484};
401 485
@@ -470,7 +554,7 @@ static unsigned int crc32_lookup_t1[256] = {
470#define crc32_step(a,b) \ 554#define crc32_step(a,b) \
471 a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8)) 555 a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8))
472 556
473#ifdef WIN32 557#ifdef _WIN32
474#pragma pack(1) 558#pragma pack(1)
475typedef struct { 559typedef struct {
476 uint16_t vid; 560 uint16_t vid;
@@ -563,12 +647,30 @@ typedef struct {
563#pragma pack() 647#pragma pack()
564#endif 648#endif
565 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
566static THREAD_T th_event_handler = THREAD_T_NULL; 668static THREAD_T th_event_handler = THREAD_T_NULL;
567struct collection listeners; 669struct collection listeners;
568static mutex_t listener_mutex; 670static mutex_t listener_mutex;
569struct collection devices; 671struct collection devices;
570static mutex_t device_mutex; 672static mutex_t device_mutex;
571#ifndef WIN32 673#ifndef _WIN32
572#ifdef HAVE_IOKIT 674#ifdef HAVE_IOKIT
573static CFRunLoopRef iokit_runloop = NULL; 675static CFRunLoopRef iokit_runloop = NULL;
574#else 676#else
@@ -576,28 +678,10 @@ static libusb_context* irecv_hotplug_ctx = NULL;
576#endif 678#endif
577#endif 679#endif
578 680
579static void _irecv_init(void)
580{
581 char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL");
582 if (dbglvl) {
583 libirecovery_debug = strtol(dbglvl, NULL, 0);
584 irecv_set_debug_level(libirecovery_debug);
585 }
586#ifndef USE_DUMMY
587#ifndef WIN32
588#ifndef HAVE_IOKIT
589 libusb_init(&libirecovery_context);
590#endif
591#endif
592 collection_init(&listeners);
593 mutex_init(&listener_mutex);
594#endif
595}
596
597static void _irecv_deinit(void) 681static void _irecv_deinit(void)
598{ 682{
599#ifndef USE_DUMMY 683#ifndef USE_DUMMY
600#ifndef WIN32 684#ifndef _WIN32
601#ifndef HAVE_IOKIT 685#ifndef HAVE_IOKIT
602 if (libirecovery_context != NULL) { 686 if (libirecovery_context != NULL) {
603 libusb_exit(libirecovery_context); 687 libusb_exit(libirecovery_context);
@@ -610,43 +694,24 @@ static void _irecv_deinit(void)
610#endif 694#endif
611} 695}
612 696
613static thread_once_t init_once = THREAD_ONCE_INIT; 697INITIALIZER(_irecv_init)
614static thread_once_t deinit_once = THREAD_ONCE_INIT;
615
616#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR
617 #if defined(__llvm__) || defined(__GNUC__)
618 #define HAVE_ATTRIBUTE_CONSTRUCTOR
619 #endif
620#endif
621
622#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR
623static void __attribute__((constructor)) libirecovery_initialize(void)
624{
625 thread_once(&init_once, _irecv_init);
626}
627
628static void __attribute__((destructor)) libirecovery_deinitialize(void)
629{
630 thread_once(&deinit_once, _irecv_deinit);
631}
632#elif defined(WIN32)
633BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
634{ 698{
635 switch (dwReason) { 699 char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL");
636 case DLL_PROCESS_ATTACH: 700 if (dbglvl) {
637 thread_once(&init_once, _irecv_init); 701 libirecovery_debug = strtol(dbglvl, NULL, 0);
638 break; 702 irecv_set_debug_level(libirecovery_debug);
639 case DLL_PROCESS_DETACH:
640 thread_once(&deinit_once, _irecv_deinit);
641 break;
642 default:
643 break;
644 } 703 }
645 return 1; 704#ifndef USE_DUMMY
646} 705#ifndef _WIN32
647#else 706#ifndef HAVE_IOKIT
648#warning No compiler support for constructor/destructor attributes, some features might not be available. 707 libusb_init(&libirecovery_context);
708#endif
709#endif
710 collection_init(&listeners);
711 mutex_init(&listener_mutex);
649#endif 712#endif
713 atexit(_irecv_deinit);
714}
650 715
651#ifdef HAVE_IOKIT 716#ifdef HAVE_IOKIT
652static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) 717static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
@@ -699,7 +764,7 @@ static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc
699 764
700static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) 765static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
701{ 766{
702#ifndef WIN32 767#ifndef _WIN32
703#ifdef HAVE_IOKIT 768#ifdef HAVE_IOKIT
704 return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size); 769 return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size);
705#else 770#else
@@ -710,10 +775,10 @@ static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc
710 unsigned short langid = 0; 775 unsigned short langid = 0;
711 unsigned char data[256]; 776 unsigned char data[256];
712 int di, si; 777 int di, si;
713 memset(data, 0, 256); 778 memset(data, 0, sizeof(data));
714 memset(buffer, 0, size); 779 memset(buffer, 0, size);
715 780
716 ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, 255, USB_TIMEOUT); 781 ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data)-1, USB_TIMEOUT);
717 782
718 if (ret < 0) return ret; 783 if (ret < 0) return ret;
719 if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR; 784 if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR;
@@ -749,21 +814,28 @@ static void irecv_load_device_info_from_iboot_string(irecv_client_t client, cons
749 ptr = strstr(iboot_string, "CPID:"); 814 ptr = strstr(iboot_string, "CPID:");
750 if (ptr != NULL) { 815 if (ptr != NULL) {
751 sscanf(ptr, "CPID:%x", &client->device_info.cpid); 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;
752 } 821 }
753 822
754 ptr = strstr(iboot_string, "CPRV:"); 823 ptr = strstr(iboot_string, "CPRV:");
755 if (ptr != NULL) { 824 if (ptr != NULL) {
756 sscanf(ptr, "CPRV:%x", &client->device_info.cprv); 825 sscanf(ptr, "CPRV:%x", &client->device_info.cprv);
826 client->device_info.have_cprv = 1;
757 } 827 }
758 828
759 ptr = strstr(iboot_string, "CPFM:"); 829 ptr = strstr(iboot_string, "CPFM:");
760 if (ptr != NULL) { 830 if (ptr != NULL) {
761 sscanf(ptr, "CPFM:%x", &client->device_info.cpfm); 831 sscanf(ptr, "CPFM:%x", &client->device_info.cpfm);
832 client->device_info.have_cpfm = 1;
762 } 833 }
763 834
764 ptr = strstr(iboot_string, "SCEP:"); 835 ptr = strstr(iboot_string, "SCEP:");
765 if (ptr != NULL) { 836 if (ptr != NULL) {
766 sscanf(ptr, "SCEP:%x", &client->device_info.scep); 837 sscanf(ptr, "SCEP:%x", &client->device_info.scep);
838 client->device_info.have_scep = 1;
767 } 839 }
768 840
769 ptr = strstr(iboot_string, "BDID:"); 841 ptr = strstr(iboot_string, "BDID:");
@@ -771,16 +843,19 @@ static void irecv_load_device_info_from_iboot_string(irecv_client_t client, cons
771 uint64_t bdid = 0; 843 uint64_t bdid = 0;
772 sscanf(ptr, "BDID:%" SCNx64, &bdid); 844 sscanf(ptr, "BDID:%" SCNx64, &bdid);
773 client->device_info.bdid = (unsigned int)bdid; 845 client->device_info.bdid = (unsigned int)bdid;
846 client->device_info.have_bdid = 1;
774 } 847 }
775 848
776 ptr = strstr(iboot_string, "ECID:"); 849 ptr = strstr(iboot_string, "ECID:");
777 if (ptr != NULL) { 850 if (ptr != NULL) {
778 sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid); 851 sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid);
852 client->device_info.have_ecid = 1;
779 } 853 }
780 854
781 ptr = strstr(iboot_string, "IBFL:"); 855 ptr = strstr(iboot_string, "IBFL:");
782 if (ptr != NULL) { 856 if (ptr != NULL) {
783 sscanf(ptr, "IBFL:%x", &client->device_info.ibfl); 857 sscanf(ptr, "IBFL:%x", &client->device_info.ibfl);
858 client->device_info.have_ibfl = 1;
784 } 859 }
785 860
786 char tmp[256]; 861 char tmp[256];
@@ -900,8 +975,8 @@ static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, un
900 *nonce = NULL; 975 *nonce = NULL;
901 *nonce_size = 0; 976 *nonce_size = 0;
902 977
903 memset(buf, 0, 256); 978 memset(buf, 0, sizeof(buf));
904 len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*) buf, 255); 979 len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*)buf, sizeof(buf)-1);
905 if (len < 0) { 980 if (len < 0) {
906 debug("%s: got length: %d\n", __func__, len); 981 debug("%s: got length: %d\n", __func__, len);
907 return; 982 return;
@@ -912,7 +987,7 @@ static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, un
912 irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf); 987 irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf);
913} 988}
914 989
915#ifndef WIN32 990#ifndef _WIN32
916static 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) 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)
917{ 992{
918 if (argCount > UINT8_MAX) { 993 if (argCount > UINT8_MAX) {
@@ -1045,7 +1120,7 @@ static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, siz
1045 1120
1046static irecv_error_t irecv_kis_init(irecv_client_t client) 1121static irecv_error_t irecv_kis_init(irecv_client_t client)
1047{ 1122{
1048#ifndef WIN32 1123#ifndef _WIN32
1049 irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL); 1124 irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL);
1050 if (err != IRECV_E_SUCCESS) { 1125 if (err != IRECV_E_SUCCESS) {
1051 debug("Failed to write to KIS_INDEX_ENABLE_A, error %d\n", err); 1126 debug("Failed to write to KIS_INDEX_ENABLE_A, error %d\n", err);
@@ -1066,7 +1141,7 @@ static irecv_error_t irecv_kis_init(irecv_client_t client)
1066static irecv_error_t irecv_kis_load_device_info(irecv_client_t client) 1141static irecv_error_t irecv_kis_load_device_info(irecv_client_t client)
1067{ 1142{
1068 debug("Loading device info in KIS mode...\n"); 1143 debug("Loading device info in KIS mode...\n");
1069#ifdef WIN32 1144#ifdef _WIN32
1070 KIS_device_info kisInfo; 1145 KIS_device_info kisInfo;
1071 DWORD transferred = 0; 1146 DWORD transferred = 0;
1072 int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL); 1147 int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL);
@@ -1134,7 +1209,7 @@ static irecv_error_t irecv_kis_load_device_info(irecv_client_t client)
1134 return IRECV_E_SUCCESS; 1209 return IRECV_E_SUCCESS;
1135} 1210}
1136 1211
1137#ifdef WIN32 1212#ifdef _WIN32
1138static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}}; 1213static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
1139static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; 1214static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
1140static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}}; 1215static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}};
@@ -1307,20 +1382,6 @@ static int check_context(irecv_client_t client)
1307} 1382}
1308#endif 1383#endif
1309 1384
1310void irecv_init(void)
1311{
1312#ifndef USE_DUMMY
1313 thread_once(&init_once, _irecv_init);
1314#endif
1315}
1316
1317void irecv_exit(void)
1318{
1319#ifndef USE_DUMMY
1320 thread_once(&deinit_once, _irecv_deinit);
1321#endif
1322}
1323
1324#ifndef USE_DUMMY 1385#ifndef USE_DUMMY
1325#ifdef HAVE_IOKIT 1386#ifdef HAVE_IOKIT
1326static 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) 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)
@@ -1361,7 +1422,7 @@ int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, u
1361#ifdef USE_DUMMY 1422#ifdef USE_DUMMY
1362 return IRECV_E_UNSUPPORTED; 1423 return IRECV_E_UNSUPPORTED;
1363#else 1424#else
1364#ifndef WIN32 1425#ifndef _WIN32
1365#ifdef HAVE_IOKIT 1426#ifdef HAVE_IOKIT
1366 return iokit_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, timeout); 1427 return iokit_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1367#else 1428#else
@@ -1481,6 +1542,74 @@ static int iokit_usb_bulk_transfer(irecv_client_t client,
1481 1542
1482 return IRECV_E_USB_INTERFACE; 1543 return IRECV_E_USB_INTERFACE;
1483} 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}
1484#endif 1613#endif
1485#endif 1614#endif
1486 1615
@@ -1496,7 +1625,7 @@ int irecv_usb_bulk_transfer(irecv_client_t client,
1496#else 1625#else
1497 int ret; 1626 int ret;
1498 1627
1499#ifndef WIN32 1628#ifndef _WIN32
1500#ifdef HAVE_IOKIT 1629#ifdef HAVE_IOKIT
1501 return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout); 1630 return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout);
1502#else 1631#else
@@ -1518,6 +1647,36 @@ int irecv_usb_bulk_transfer(irecv_client_t client,
1518#endif 1647#endif
1519} 1648}
1520 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
1521#ifndef USE_DUMMY 1680#ifndef USE_DUMMY
1522#ifdef HAVE_IOKIT 1681#ifdef HAVE_IOKIT
1523static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service) 1682static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service)
@@ -1686,7 +1845,7 @@ static irecv_error_t iokit_open_with_ecid(irecv_client_t* pclient, uint64_t ecid
1686} 1845}
1687#endif 1846#endif
1688 1847
1689#ifndef WIN32 1848#ifndef _WIN32
1690#ifndef HAVE_IOKIT 1849#ifndef HAVE_IOKIT
1691static 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) 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)
1692{ 1851{
@@ -1703,8 +1862,8 @@ static irecv_error_t libusb_usb_open_handle_with_descriptor_and_ecid(irecv_clien
1703 1862
1704 if (client->mode != KIS_PRODUCT_ID) { 1863 if (client->mode != KIS_PRODUCT_ID) {
1705 char serial_str[256]; 1864 char serial_str[256];
1706 memset(serial_str, 0, 256); 1865 memset(serial_str, 0, sizeof(serial_str));
1707 irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, 255); 1866 irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
1708 irecv_load_device_info_from_iboot_string(client, serial_str); 1867 irecv_load_device_info_from_iboot_string(client, serial_str);
1709 } 1868 }
1710 1869
@@ -1797,7 +1956,7 @@ irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1797 if (libirecovery_debug) { 1956 if (libirecovery_debug) {
1798 irecv_set_debug_level(libirecovery_debug); 1957 irecv_set_debug_level(libirecovery_debug);
1799 } 1958 }
1800#ifndef WIN32 1959#ifndef _WIN32
1801#ifdef HAVE_IOKIT 1960#ifdef HAVE_IOKIT
1802 error = iokit_open_with_ecid(pclient, ecid); 1961 error = iokit_open_with_ecid(pclient, ecid);
1803#else 1962#else
@@ -1880,7 +2039,7 @@ irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configurati
1880 if (check_context(client) != IRECV_E_SUCCESS) 2039 if (check_context(client) != IRECV_E_SUCCESS)
1881 return IRECV_E_NO_DEVICE; 2040 return IRECV_E_NO_DEVICE;
1882 2041
1883#ifndef WIN32 2042#ifndef _WIN32
1884 debug("Setting to configuration %d\n", configuration); 2043 debug("Setting to configuration %d\n", configuration);
1885 2044
1886#ifdef HAVE_IOKIT 2045#ifdef HAVE_IOKIT
@@ -1999,7 +2158,7 @@ irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface,
1999 return IRECV_E_NO_DEVICE; 2158 return IRECV_E_NO_DEVICE;
2000 2159
2001 debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface); 2160 debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface);
2002#ifndef WIN32 2161#ifndef _WIN32
2003#ifdef HAVE_IOKIT 2162#ifdef HAVE_IOKIT
2004 if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) { 2163 if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) {
2005 return IRECV_E_USB_INTERFACE; 2164 return IRECV_E_USB_INTERFACE;
@@ -2037,7 +2196,7 @@ irecv_error_t irecv_reset(irecv_client_t client)
2037 if (check_context(client) != IRECV_E_SUCCESS) 2196 if (check_context(client) != IRECV_E_SUCCESS)
2038 return IRECV_E_NO_DEVICE; 2197 return IRECV_E_NO_DEVICE;
2039 2198
2040#ifndef WIN32 2199#ifndef _WIN32
2041#ifdef HAVE_IOKIT 2200#ifdef HAVE_IOKIT
2042 IOReturn result; 2201 IOReturn result;
2043 2202
@@ -2176,7 +2335,7 @@ struct irecv_usb_device_info {
2176 int alive; 2335 int alive;
2177}; 2336};
2178 2337
2179#ifdef WIN32 2338#ifdef _WIN32
2180struct irecv_win_dev_ctx { 2339struct irecv_win_dev_ctx {
2181 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details; 2340 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
2182 uint32_t location; 2341 uint32_t location;
@@ -2194,7 +2353,7 @@ static int _irecv_is_recovery_device(void *device)
2194{ 2353{
2195 uint16_t vendor_id = 0; 2354 uint16_t vendor_id = 0;
2196 uint16_t product_id = 0; 2355 uint16_t product_id = 0;
2197#ifdef WIN32 2356#ifdef _WIN32
2198 const char *path = (const char*)device; 2357 const char *path = (const char*)device;
2199 unsigned int vendor = 0; 2358 unsigned int vendor = 0;
2200 unsigned int product = 0; 2359 unsigned int product = 0;
@@ -2261,8 +2420,8 @@ static void* _irecv_handle_device_add(void *userdata)
2261 irecv_error_t error = 0; 2420 irecv_error_t error = 0;
2262 irecv_client_t client = NULL; 2421 irecv_client_t client = NULL;
2263 2422
2264 memset(serial_str, 0, 256); 2423 memset(serial_str, 0, sizeof(serial_str));
2265#ifdef WIN32 2424#ifdef _WIN32
2266 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata; 2425 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata;
2267 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details = win_ctx->details; 2426 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details = win_ctx->details;
2268 LPSTR result = (LPSTR)details->DevicePath; 2427 LPSTR result = (LPSTR)details->DevicePath;
@@ -2294,6 +2453,11 @@ static void* _irecv_handle_device_add(void *userdata)
2294 2453
2295 if (product_id == KIS_PRODUCT_ID) { 2454 if (product_id == KIS_PRODUCT_ID) {
2296 client = (irecv_client_t)malloc(sizeof(struct irecv_client_private)); 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));
2297 client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 2461 client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
2298 if (client->handle == INVALID_HANDLE_VALUE) { 2462 if (client->handle == INVALID_HANDLE_VALUE) {
2299 debug("%s: Failed to open device path %s\n", __func__, result); 2463 debug("%s: Failed to open device path %s\n", __func__, result);
@@ -2317,7 +2481,7 @@ static void* _irecv_handle_device_add(void *userdata)
2317 } 2481 }
2318 } 2482 }
2319 2483
2320#else /* !WIN32 */ 2484#else /* !_WIN32 */
2321#ifdef HAVE_IOKIT 2485#ifdef HAVE_IOKIT
2322 struct irecv_iokit_dev_ctx* iokit_ctx = (struct irecv_iokit_dev_ctx*)userdata; 2486 struct irecv_iokit_dev_ctx* iokit_ctx = (struct irecv_iokit_dev_ctx*)userdata;
2323 io_service_t device = iokit_ctx->device; 2487 io_service_t device = iokit_ctx->device;
@@ -2349,13 +2513,19 @@ static void* _irecv_handle_device_add(void *userdata)
2349 2513
2350 if (product_id == KIS_PRODUCT_ID) { 2514 if (product_id == KIS_PRODUCT_ID) {
2351 IOObjectRetain(device); 2515 IOObjectRetain(device);
2352 2516 int i = 0;
2353 error = iokit_usb_open_service(&client, device); 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 }
2354 if (error != IRECV_E_SUCCESS) { 2525 if (error != IRECV_E_SUCCESS) {
2355 debug("%s: ERROR: could not open KIS device!\n", __func__); 2526 debug("%s: ERROR: could not open KIS device!\n", __func__);
2356 return NULL; 2527 return NULL;
2357 } 2528 }
2358
2359 product_id = client->mode; 2529 product_id = client->mode;
2360 } else { 2530 } else {
2361 CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0); 2531 CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
@@ -2397,7 +2567,7 @@ static void* _irecv_handle_device_add(void *userdata)
2397 2567
2398 product_id = client->mode; 2568 product_id = client->mode;
2399 } else { 2569 } else {
2400 libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, 255); 2570 libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
2401 if (libusb_error < 0) { 2571 if (libusb_error < 0) {
2402 debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error)); 2572 debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2403 return 0; 2573 return 0;
@@ -2405,17 +2575,32 @@ static void* _irecv_handle_device_add(void *userdata)
2405 libusb_close(usb_handle); 2575 libusb_close(usb_handle);
2406 } 2576 }
2407#endif /* !HAVE_IOKIT */ 2577#endif /* !HAVE_IOKIT */
2408#endif /* !WIN32 */ 2578#endif /* !_WIN32 */
2409 memset(&client_loc, '\0', sizeof(client_loc)); 2579 memset(&client_loc, '\0', sizeof(client_loc));
2410 if (product_id == KIS_PRODUCT_ID) { 2580 if (product_id == KIS_PRODUCT_ID) {
2411 error = irecv_usb_set_configuration(client, 1); 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 }
2412 if (error != IRECV_E_SUCCESS) { 2590 if (error != IRECV_E_SUCCESS) {
2413 debug("Failed to set configuration, error %d\n", error); 2591 debug("Failed to set configuration, error %d\n", error);
2414 irecv_close(client); 2592 irecv_close(client);
2415 return NULL; 2593 return NULL;
2416 } 2594 }
2417 2595
2418 error = irecv_usb_set_interface(client, 0, 0); 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 }
2419 if (error != IRECV_E_SUCCESS) { 2604 if (error != IRECV_E_SUCCESS) {
2420 debug("Failed to set interface, error %d\n", error); 2605 debug("Failed to set interface, error %d\n", error);
2421 irecv_close(client); 2606 irecv_close(client);
@@ -2473,7 +2658,7 @@ static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo)
2473{ 2658{
2474 irecv_device_event_t dev_event; 2659 irecv_device_event_t dev_event;
2475 dev_event.type = IRECV_DEVICE_REMOVE; 2660 dev_event.type = IRECV_DEVICE_REMOVE;
2476 dev_event.mode = 0; 2661 dev_event.mode = devinfo->mode;
2477 dev_event.device_info = &(devinfo->device_info); 2662 dev_event.device_info = &(devinfo->device_info);
2478 mutex_lock(&listener_mutex); 2663 mutex_lock(&listener_mutex);
2479 FOREACH(struct irecv_device_event_context* context, &listeners) { 2664 FOREACH(struct irecv_device_event_context* context, &listeners) {
@@ -2493,7 +2678,7 @@ static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo)
2493 free(devinfo); 2678 free(devinfo);
2494} 2679}
2495 2680
2496#ifndef WIN32 2681#ifndef _WIN32
2497#ifdef HAVE_IOKIT 2682#ifdef HAVE_IOKIT
2498static void iokit_device_added(void *refcon, io_iterator_t iterator) 2683static void iokit_device_added(void *refcon, io_iterator_t iterator)
2499{ 2684{
@@ -2590,7 +2775,7 @@ static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, lib
2590} 2775}
2591#endif /* HAVE_LIBUSB_HOTPLUG_API */ 2776#endif /* HAVE_LIBUSB_HOTPLUG_API */
2592#endif /* !HAVE_IOKIT */ 2777#endif /* !HAVE_IOKIT */
2593#endif /* !WIN32 */ 2778#endif /* !_WIN32 */
2594 2779
2595struct _irecv_event_handler_info { 2780struct _irecv_event_handler_info {
2596 cond_t startup_cond; 2781 cond_t startup_cond;
@@ -2600,7 +2785,7 @@ struct _irecv_event_handler_info {
2600static void *_irecv_event_handler(void* data) 2785static void *_irecv_event_handler(void* data)
2601{ 2786{
2602 struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data; 2787 struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data;
2603#ifdef WIN32 2788#ifdef _WIN32
2604 struct collection newDevices; 2789 struct collection newDevices;
2605 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL }; 2790 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
2606 int running = 1; 2791 int running = 1;
@@ -2744,7 +2929,7 @@ static void *_irecv_event_handler(void* data)
2744 } while (running); 2929 } while (running);
2745 2930
2746 collection_free(&newDevices); 2931 collection_free(&newDevices);
2747#else /* !WIN32 */ 2932#else /* !_WIN32 */
2748#ifdef HAVE_IOKIT 2933#ifdef HAVE_IOKIT
2749 kern_return_t kr; 2934 kern_return_t kr;
2750 2935
@@ -2869,7 +3054,7 @@ static void *_irecv_event_handler(void* data)
2869 } while (running); 3054 } while (running);
2870#endif /* !HAVE_LIBUSB_HOTPLUG_API */ 3055#endif /* !HAVE_LIBUSB_HOTPLUG_API */
2871#endif /* !HAVE_IOKIT */ 3056#endif /* !HAVE_IOKIT */
2872#endif /* !WIN32 */ 3057#endif /* !_WIN32 */
2873 return NULL; 3058 return NULL;
2874} 3059}
2875#endif /* !USE_DUMMY */ 3060#endif /* !USE_DUMMY */
@@ -2898,7 +3083,7 @@ irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context
2898 struct _irecv_event_handler_info info; 3083 struct _irecv_event_handler_info info;
2899 cond_init(&info.startup_cond); 3084 cond_init(&info.startup_cond);
2900 mutex_init(&info.startup_mutex); 3085 mutex_init(&info.startup_mutex);
2901#ifndef WIN32 3086#ifndef _WIN32
2902#ifndef HAVE_IOKIT 3087#ifndef HAVE_IOKIT
2903 libusb_init(&irecv_hotplug_ctx); 3088 libusb_init(&irecv_hotplug_ctx);
2904#endif 3089#endif
@@ -2948,6 +3133,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex
2948 if (num == 0 && th_event_handler != THREAD_T_NULL && thread_alive(th_event_handler)) { 3133 if (num == 0 && th_event_handler != THREAD_T_NULL && thread_alive(th_event_handler)) {
2949#ifdef HAVE_IOKIT 3134#ifdef HAVE_IOKIT
2950 if (iokit_runloop) { 3135 if (iokit_runloop) {
3136 while (!CFRunLoopIsWaiting(iokit_runloop)) usleep(420);
2951 CFRunLoopStop(iokit_runloop); 3137 CFRunLoopStop(iokit_runloop);
2952 iokit_runloop = NULL; 3138 iokit_runloop = NULL;
2953 } 3139 }
@@ -2970,7 +3156,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex
2970 collection_free(&devices); 3156 collection_free(&devices);
2971 mutex_unlock(&device_mutex); 3157 mutex_unlock(&device_mutex);
2972 mutex_destroy(&device_mutex); 3158 mutex_destroy(&device_mutex);
2973#ifndef WIN32 3159#ifndef _WIN32
2974#ifndef HAVE_IOKIT 3160#ifndef HAVE_IOKIT
2975 libusb_exit(irecv_hotplug_ctx); 3161 libusb_exit(irecv_hotplug_ctx);
2976 irecv_hotplug_ctx = NULL; 3162 irecv_hotplug_ctx = NULL;
@@ -2984,7 +3170,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex
2984#endif 3170#endif
2985} 3171}
2986 3172
2987irecv_error_t irecv_close(irecv_client_t client) 3173static irecv_error_t irecv_cleanup(irecv_client_t client)
2988{ 3174{
2989#ifdef USE_DUMMY 3175#ifdef USE_DUMMY
2990 return IRECV_E_UNSUPPORTED; 3176 return IRECV_E_UNSUPPORTED;
@@ -2998,7 +3184,7 @@ irecv_error_t irecv_close(irecv_client_t client)
2998 event.type = IRECV_DISCONNECTED; 3184 event.type = IRECV_DISCONNECTED;
2999 client->disconnected_callback(client, &event); 3185 client->disconnected_callback(client, &event);
3000 } 3186 }
3001#ifndef WIN32 3187#ifndef _WIN32
3002#ifdef HAVE_IOKIT 3188#ifdef HAVE_IOKIT
3003 if (client->usbInterface) { 3189 if (client->usbInterface) {
3004 (*client->usbInterface)->USBInterfaceClose(client->usbInterface); 3190 (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
@@ -3028,20 +3214,28 @@ irecv_error_t irecv_close(irecv_client_t client)
3028 free(client->device_info.serial_string); 3214 free(client->device_info.serial_string);
3029 free(client->device_info.ap_nonce); 3215 free(client->device_info.ap_nonce);
3030 free(client->device_info.sep_nonce); 3216 free(client->device_info.sep_nonce);
3031
3032 free(client);
3033 client = NULL;
3034 } 3217 }
3035 3218
3036 return IRECV_E_SUCCESS; 3219 return IRECV_E_SUCCESS;
3037#endif 3220#endif
3038} 3221}
3039 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);
3228 free(client);
3229 client = NULL;
3230 }
3231 return ret;
3232}
3233
3040void irecv_set_debug_level(int level) 3234void irecv_set_debug_level(int level)
3041{ 3235{
3042 libirecovery_debug = level; 3236 libirecovery_debug = level;
3043#ifndef USE_DUMMY 3237#ifndef USE_DUMMY
3044#ifndef WIN32 3238#ifndef _WIN32
3045#ifndef HAVE_IOKIT 3239#ifndef HAVE_IOKIT
3046 if (libirecovery_context) { 3240 if (libirecovery_context) {
3047#if LIBUSB_API_VERSION >= 0x01000106 3241#if LIBUSB_API_VERSION >= 0x01000106
@@ -3072,6 +3266,35 @@ static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* c
3072 return IRECV_E_INVALID_INPUT; 3266 return IRECV_E_INVALID_INPUT;
3073 } 3267 }
3074 3268
3269 if (length > 0 && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3270 int bytes = 0;
3271 irecv_error_t error = 0;
3272#ifdef DEBUG
3273 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
3274 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
3275 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return 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;
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
3075 if (length > 0) { 3298 if (length > 0) {
3076 irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT); 3299 irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT);
3077 } 3300 }
@@ -3179,8 +3402,8 @@ static irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* statu
3179 } 3402 }
3180 3403
3181 unsigned char buffer[6]; 3404 unsigned char buffer[6];
3182 memset(buffer, '\0', 6); 3405 memset(buffer, '\0', sizeof(buffer));
3183 if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, USB_TIMEOUT) != 6) { 3406 if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, sizeof(buffer), USB_TIMEOUT) != sizeof(buffer)) {
3184 *status = 0; 3407 *status = 0;
3185 return IRECV_E_USB_STATUS; 3408 return IRECV_E_USB_STATUS;
3186 } 3409 }
@@ -3205,7 +3428,7 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char*
3205 if (toUpload > 0x4000) 3428 if (toUpload > 0x4000)
3206 toUpload = 0x4000; 3429 toUpload = 0x4000;
3207 3430
3208#ifdef WIN32 3431#ifdef _WIN32
3209 memcpy(chunk->data, buffer, toUpload); 3432 memcpy(chunk->data, buffer, toUpload);
3210 chunk->size = toUpload; 3433 chunk->size = toUpload;
3211 chunk->address = address; 3434 chunk->address = address;
@@ -3222,7 +3445,7 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char*
3222 memcpy(chunk->data, buffer, toUpload); 3445 memcpy(chunk->data, buffer, toUpload);
3223#endif 3446#endif
3224 3447
3225#ifdef WIN32 3448#ifdef _WIN32
3226 DWORD transferred = 0; 3449 DWORD transferred = 0;
3227 int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL); 3450 int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL);
3228 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD; 3451 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
@@ -3255,7 +3478,7 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char*
3255 free(chunk); 3478 free(chunk);
3256 3479
3257 if (options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) { 3480 if (options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) {
3258#ifdef WIN32 3481#ifdef _WIN32
3259 DWORD amount = (DWORD)origLen; 3482 DWORD amount = (DWORD)origLen;
3260 DWORD transferred = 0; 3483 DWORD transferred = 0;
3261 int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL); 3484 int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL);
@@ -3283,6 +3506,30 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3283 3506
3284 irecv_error_t error = 0; 3507 irecv_error_t error = 0;
3285 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)); 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 }
3522
3523 if (recovery_mode && !legacy_recovery_mode) {
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 }
3532 }
3286 3533
3287 if (check_context(client) != IRECV_E_SUCCESS) 3534 if (check_context(client) != IRECV_E_SUCCESS)
3288 return IRECV_E_NO_DEVICE; 3535 return IRECV_E_NO_DEVICE;
@@ -3291,6 +3538,7 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3291 unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10}; 3538 unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10};
3292 int dfu_crc = 1; 3539 int dfu_crc = 1;
3293 int packet_size = recovery_mode ? 0x8000 : 0x800; 3540 int packet_size = recovery_mode ? 0x8000 : 0x800;
3541 if (legacy_recovery_mode) packet_size = 0x200;
3294 if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) { 3542 if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) {
3295 packet_size = 0x40; 3543 packet_size = 0x40;
3296 dfu_crc = 0; 3544 dfu_crc = 0;
@@ -3305,7 +3553,24 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3305 } 3553 }
3306 3554
3307 /* initiate transfer */ 3555 /* initiate transfer */
3308 if (recovery_mode) { 3556 if (legacy_recovery_mode) {
3557 int bytes = 0;
3558 uint32_t loadaddr0x8900 = 0x09000000;
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) {
3309 error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT); 3574 error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT);
3310 } else { 3575 } else {
3311 uint8_t state = 0; 3576 uint8_t state = 0;
@@ -3318,6 +3583,14 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3318 case 2: 3583 case 2:
3319 /* DFU IDLE */ 3584 /* DFU IDLE */
3320 break; 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;
3321 case 10: 3594 case 10:
3322 debug("DFU ERROR, issuing CLRSTATUS\n"); 3595 debug("DFU ERROR, issuing CLRSTATUS\n");
3323 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_TIMEOUT); 3596 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_TIMEOUT);
@@ -3342,10 +3615,14 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3342 for (i = 0; i < packets; i++) { 3615 for (i = 0; i < packets; i++) {
3343 int size = (i + 1) < packets ? packet_size : last; 3616 int size = (i + 1) < packets ? packet_size : last;
3344 3617
3345 /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ 3618 if (legacy_recovery_mode) {
3346 if (recovery_mode) { 3619 // Use interrupt transfer for legacy devices
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
3347 error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT); 3623 error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
3348 } else { 3624 } else {
3625 // Use control transfer for DFU and WTF mode
3349 if (dfu_crc) { 3626 if (dfu_crc) {
3350 int j; 3627 int j;
3351 for (j = 0; j < size; j++) { 3628 for (j = 0; j < size; j++) {
@@ -3451,6 +3728,17 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
3451 } 3728 }
3452 3729
3453 irecv_reset(client); 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);
3454 } 3742 }
3455 3743
3456 return IRECV_E_SUCCESS; 3744 return IRECV_E_SUCCESS;
@@ -3508,6 +3796,11 @@ irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** v
3508 return IRECV_E_INVALID_INPUT; 3796 return IRECV_E_INVALID_INPUT;
3509 } 3797 }
3510 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;
3802 }
3803
3511 memset(command, '\0', sizeof(command)); 3804 memset(command, '\0', sizeof(command));
3512 snprintf(command, sizeof(command)-1, "getenv %s", variable); 3805 snprintf(command, sizeof(command)-1, "getenv %s", variable);
3513 irecv_error_t error = irecv_send_command_raw(client, command, 0); 3806 irecv_error_t error = irecv_send_command_raw(client, command, 0);
@@ -3519,13 +3812,14 @@ irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** v
3519 return error; 3812 return error;
3520 } 3813 }
3521 3814
3522 char* response = (char*) malloc(256); 3815 int rsize = 256;
3816 char* response = (char*) malloc(rsize);
3523 if (response == NULL) { 3817 if (response == NULL) {
3524 return IRECV_E_OUT_OF_MEMORY; 3818 return IRECV_E_OUT_OF_MEMORY;
3525 } 3819 }
3526 3820
3527 memset(response, '\0', 256); 3821 memset(response, '\0', rsize);
3528 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT); 3822 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
3529 3823
3530 *value = response; 3824 *value = response;
3531 3825
@@ -3543,13 +3837,14 @@ irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value)
3543 3837
3544 *value = 0; 3838 *value = 0;
3545 3839
3546 char* response = (char*) malloc(256); 3840 int rsize = 256;
3841 char* response = (char*) malloc(rsize);
3547 if (response == NULL) { 3842 if (response == NULL) {
3548 return IRECV_E_OUT_OF_MEMORY; 3843 return IRECV_E_OUT_OF_MEMORY;
3549 } 3844 }
3550 3845
3551 memset(response, '\0', 256); 3846 memset(response, '\0', rsize);
3552 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT); 3847 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
3553 3848
3554 *value = (unsigned int) *response; 3849 *value = (unsigned int) *response;
3555 3850
@@ -3992,7 +4287,7 @@ irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause)
3992 uint64_t ecid = client->device_info.ecid; 4287 uint64_t ecid = client->device_info.ecid;
3993 4288
3994 if (check_context(client) == IRECV_E_SUCCESS) { 4289 if (check_context(client) == IRECV_E_SUCCESS) {
3995 irecv_close(client); 4290 irecv_cleanup(client);
3996 } 4291 }
3997 4292
3998 if (initial_pause > 0) { 4293 if (initial_pause > 0) {
@@ -4012,6 +4307,11 @@ irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause)
4012 new_client->postcommand_callback = postcommand_callback; 4307 new_client->postcommand_callback = postcommand_callback;
4013 new_client->disconnected_callback = disconnected_callback; 4308 new_client->disconnected_callback = disconnected_callback;
4014 4309
4310 // keep old handle valid
4311 memcpy(client, new_client, sizeof(*client));
4312 free(new_client);
4313 new_client = client;
4314
4015 if (new_client->connected_callback != NULL) { 4315 if (new_client->connected_callback != NULL) {
4016 irecv_event_t event; 4316 irecv_event_t event;
4017 event.size = 0; 4317 event.size = 0;
diff --git a/tools/Makefile.am b/tools/Makefile.am
index ebb085c..15e2eb9 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -2,12 +2,15 @@ if BUILD_TOOLS
2AM_CPPFLAGS = -I$(top_srcdir)/include 2AM_CPPFLAGS = -I$(top_srcdir)/include
3 3
4AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS) 4AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS)
5AM_LDFLAGS = $(libusb_LIBS) -lreadline 5AM_LDFLAGS = $(libusb_LIBS)
6 6
7bin_PROGRAMS = irecovery 7bin_PROGRAMS = irecovery
8 8
9irecovery_SOURCES = irecovery.c 9irecovery_SOURCES = irecovery.c
10irecovery_CFLAGS = $(AM_CFLAGS) 10irecovery_CFLAGS = $(AM_CFLAGS)
11irecovery_LDFLAGS = $(AM_LDFLAGS) 11irecovery_LDFLAGS = $(AM_LDFLAGS)
12if HAVE_READLINE
13irecovery_LDFLAGS += -lreadline
14endif
12irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la 15irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la
13endif 16endif
diff --git a/tools/irecovery.c b/tools/irecovery.c
index 61d053a..b293324 100644
--- a/tools/irecovery.c
+++ b/tools/irecovery.c
@@ -31,16 +31,22 @@
31#include <string.h> 31#include <string.h>
32#include <getopt.h> 32#include <getopt.h>
33#include <inttypes.h> 33#include <inttypes.h>
34#include <ctype.h>
34#include <libirecovery.h> 35#include <libirecovery.h>
36#ifdef HAVE_READLINE
35#include <readline/readline.h> 37#include <readline/readline.h>
36#include <readline/history.h> 38#include <readline/history.h>
39#else
40#ifndef _WIN32
41#include <termios.h>
42#endif
43#endif
37 44
38#ifdef WIN32 45#ifdef _WIN32
39#include <windows.h> 46#include <windows.h>
40#ifndef sleep 47#include <conio.h>
41#define sleep(n) Sleep(1000 * n) 48#define sleep(n) Sleep(1000 * n)
42#endif 49#endif
43#endif
44 50
45#define FILE_HISTORY_PATH ".irecovery" 51#define FILE_HISTORY_PATH ".irecovery"
46#define debug(...) if (verbose) fprintf(stderr, __VA_ARGS__) 52#define debug(...) if (verbose) fprintf(stderr, __VA_ARGS__)
@@ -271,14 +277,48 @@ static void parse_command(irecv_client_t client, unsigned char* command, unsigne
271 277
272static void load_command_history() 278static void load_command_history()
273{ 279{
280#ifdef HAVE_READLINE
274 read_history(FILE_HISTORY_PATH); 281 read_history(FILE_HISTORY_PATH);
282#endif
275} 283}
276 284
277static void append_command_to_history(char* cmd) 285static void append_command_to_history(const char* cmd)
278{ 286{
287#ifdef HAVE_READLINE
279 add_history(cmd); 288 add_history(cmd);
280 write_history(FILE_HISTORY_PATH); 289 write_history(FILE_HISTORY_PATH);
290#endif
291}
292
293#ifndef HAVE_READLINE
294#ifdef _WIN32
295#define BS_CC '\b'
296#else
297#define BS_CC 0x7f
298#define getch getchar
299#endif
300static void get_input(char *buf, int maxlen)
301{
302 int len = 0;
303 int c;
304
305 while ((c = getch())) {
306 if ((c == '\r') || (c == '\n')) {
307 break;
308 }
309 if (isprint(c)) {
310 if (len < maxlen-1)
311 buf[len++] = c;
312 } else if (c == BS_CC) {
313 if (len > 0) {
314 fputs("\b \b", stdout);
315 len--;
316 }
317 }
318 }
319 buf[len] = 0;
281} 320}
321#endif
282 322
283static void init_shell(irecv_client_t client) 323static void init_shell(irecv_client_t client)
284{ 324{
@@ -294,8 +334,15 @@ static void init_shell(irecv_client_t client)
294 debug("%s\n", irecv_strerror(error)); 334 debug("%s\n", irecv_strerror(error));
295 break; 335 break;
296 } 336 }
297 337#ifdef HAVE_READLINE
298 char* cmd = readline("> "); 338 char* cmd = readline("> ");
339#else
340 char cmdbuf[4096];
341 const char* cmd = &cmdbuf[0];
342 printf("> ");
343 fflush(stdout);
344 get_input(cmdbuf, sizeof(cmdbuf));
345#endif
299 if (cmd && *cmd) { 346 if (cmd && *cmd) {
300 if (_is_breq_command(cmd)) { 347 if (_is_breq_command(cmd)) {
301 error = irecv_send_command_breq(client, cmd, 1); 348 error = irecv_send_command_breq(client, cmd, 1);
@@ -307,8 +354,10 @@ static void init_shell(irecv_client_t client)
307 } 354 }
308 355
309 append_command_to_history(cmd); 356 append_command_to_history(cmd);
310 free(cmd);
311 } 357 }
358#ifdef HAVE_READLINE
359 free(cmd);
360#endif
312 } 361 }
313} 362}
314 363
@@ -544,7 +593,11 @@ int main(int argc, char* argv[])
544 return 0; 593 return 0;
545 594
546 case 'V': 595 case 'V':
547 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); 596 printf("%s %s", TOOL_NAME, PACKAGE_VERSION);
597#ifdef HAVE_READLINE
598 printf(" (readline)");
599#endif
600 printf("\n");
548 return 0; 601 return 0;
549 602
550 default: 603 default: