summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml3
-rw-r--r--.github/workflows/build.yml19
-rw-r--r--Makefile.am1
-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
10 files changed, 585 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 @@
+github: nikias
+patreon: nikias
+custom: ["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:
run: |
echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
- name: fetch libplist
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libplist-latest_${{env.target_triplet}}
repo: libimobiledevice/libplist
- name: fetch libimobiledevice-glue
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
@@ -49,7 +49,7 @@ jobs:
run: |
mkdir -p dest
DESTDIR=`pwd`/dest make install
- tar -C dest -cf libirecovery.tar lib usr
+ tar -C dest -cf libirecovery.tar --strip-components 1 .
- name: publish artifact
uses: actions/upload-artifact@v4
with:
@@ -67,14 +67,14 @@ jobs:
fi
shell: bash
- name: fetch libplist
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libplist-latest_macOS
repo: libimobiledevice/libplist
- name: fetch libimobiledevice-glue
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
@@ -109,14 +109,14 @@ jobs:
run: |
mkdir -p dest
DESTDIR=`pwd`/dest make install
- tar -C dest -cf libirecovery.tar usr
+ tar -C dest -cf libirecovery.tar --strip-components 1 .
- name: publish artifact
uses: actions/upload-artifact@v4
with:
name: libirecovery-latest_macOS
path: libirecovery.tar
build-windows:
- runs-on: windows-2019
+ runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
@@ -137,6 +137,7 @@ jobs:
base-devel
git
mingw-w64-${{ matrix.arch }}-gcc
+ mingw-w64-${{ matrix.arch }}-pkg-config
make
libtool
autoconf
@@ -147,14 +148,14 @@ jobs:
echo "dest=$dest" >> $GITHUB_ENV
echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
- name: fetch libplist
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libplist-latest_${{ matrix.arch }}-${{ env.dest }}
repo: libimobiledevice/libplist
- name: fetch libimobiledevice-glue
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
diff --git a/Makefile.am b/Makefile.am
index 6c48faf..80b0eae 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,7 @@ EXTRA_DIST = \
git-version-gen
dist-hook:
+ @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi
echo $(VERSION) > $(distdir)/.tarball-version
DISTCHECK_CONFIGURE_FLAGS = \
diff --git a/NEWS b/NEWS
index cb06ea6..f7e2c2f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,50 @@
+Version 1.3.1
+~~~~~~~~~~~~~
+
+* Device database changes:
+ - Support iPad Pro M5 family devices
+ - Support Apple Vision Pro M5
+ - Support MacBook Pro 14-inch M5
+
+* Bug Fixes:
+ Fix: array initialization compatibility with MSVC
+
+
+Version 1.3.0
+~~~~~~~~~~~~~
+
+* Changes:
+ - Switch to better initializer strategy
+ - Allow building without readline support for the irecovery tool
+ - Added support for iOS 1 and iOS 2 recovery mode (Linux, macOS)
+
+* Device database changes:
+ - Support iPhone 17 family devices
+ - Support Watch 11 / SE3 / Ultra3 models
+ - Support March 2025 iPad and Mac models
+ - Support iPhone 16e
+ - Support November 2024 Mac models
+ - Support iPad mini (A17 Pro)
+
+* Bug Fixes:
+ - IOKit: Fix race condition when trying to delete runloop before it even started
+
+
+Version 1.2.1
+~~~~~~~~~~~~~
+
+* Changes:
+ - Make sure IRECV_DEVICE_REMOVE event has the mode set the device was in
+ - KIS: Add some retry loops around open/set config/set interface operations
+
+* Device database changes:
+ - Support Apple Watch Series 10 and iPhone 16 models
+ - Add iPad Air (M2) and iPad Pro (M4) models
+
+* Bug Fixes:
+ - Windows: Fix crash due to access to uninitialized data
+
+
Version 1.2.0
~~~~~~~~~~~~~
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.
Only the prerequisites differ and they are described in this section.
libirecovery requires [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue).
+Some platforms already provide it as a package.
Check the [Building](https://github.com/libimobiledevice/libimobiledevice-glue?tab=readme-ov-file#building)
-section of the README on how to build it. Note that some platforms might have it as a package.
+section of the README on how to build it.
#### Linux (Debian/Ubuntu based)
@@ -68,12 +69,12 @@ section of the README on how to build it. Note that some platforms might have it
Using MacPorts:
```shell
- sudo port install libtool autoconf automake pkgconfig
+ sudo port install libtool autoconf automake pkgconfig libimobiledevice-glue
```
Using Homebrew:
```shell
- brew install libtool autoconf automake pkg-config
+ brew install libtool autoconf automake pkg-config libimobiledevice-glue
```
#### Windows
@@ -91,7 +92,9 @@ section of the README on how to build it. Note that some platforms might have it
libtool \
autoconf \
automake-wrapper \
- pkg-config
+ pkg-config \
+ mingw-w64-x86_64-libimobiledevice-glue \
+ mingw-w64-x86_64-readline
```
NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly.
@@ -236,8 +239,8 @@ Please make sure your contribution adheres to:
## Links
* Homepage: https://libimobiledevice.org/
-* Repository: https://git.libimobiledevice.org/libirecovery.git
-* Repository (Mirror): https://github.com/libimobiledevice/libirecovery.git
+* Repository: https://github.com/libimobiledevice/libirecovery.git
+* Repository (Mirror): https://git.libimobiledevice.org/libirecovery.git
* Issue Tracker: https://github.com/libimobiledevice/libirecovery/issues
* Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel
* Twitter: https://twitter.com/libimobiledev
@@ -255,4 +258,4 @@ iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc.
This project is an independent software library and has not been authorized,
sponsored, or otherwise approved by Apple Inc.
-README Updated on: 2024-03-23
+README 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
# changes to the signature and the semantic)
# ? :+1 : ? == just internal changes
# CURRENT : REVISION : AGE
-LIBIRECOVERY_SO_VERSION=5:0:0
+LIBIRECOVERY_SO_VERSION=6:2:1
dnl Minimum package versions
LIBUSB_VERSION=1.0.3
@@ -77,35 +77,21 @@ case ${host_os} in
esac
AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
-# Check if the C compiler supports __attribute__((constructor))
-AC_CACHE_CHECK([wether the C compiler supports constructor/destructor attributes],
- ac_cv_attribute_constructor, [
- ac_cv_attribute_constructor=no
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
- [[
- static void __attribute__((constructor)) test_constructor(void) {
- }
- static void __attribute__((destructor)) test_destructor(void) {
- }
- ]], [])],
- [ac_cv_attribute_constructor=yes]
- )]
-)
-if test "$ac_cv_attribute_constructor" = "yes"; then
- AC_DEFINE(HAVE_ATTRIBUTE_CONSTRUCTOR, 1, [Define if the C compiler supports constructor/destructor attributes])
-fi
-
AC_ARG_WITH([tools],
- [AS_HELP_STRING([--with-tools], [Build irecovery tools. (requires readline) [default=yes]])],
+ [AS_HELP_STRING([--with-tools], [Build irecovery tools. [default=yes]])],
[],
[with_tools=yes])
AS_IF([test "x$with_tools" = "xyes"], [
+ have_readline=no
AC_DEFINE(BUILD_TOOLS, 1, [Define if we are building irecovery tools])
- AC_CHECK_HEADERS([readline/readline.h], [],
- [AC_MSG_ERROR([Please install readline development headers])]
- )]
-)
+ AC_CHECK_HEADERS([readline/readline.h],
+ [AC_DEFINE(HAVE_READLINE, 1, [Define if readline is available])
+ have_readline=yes],
+ [AC_MSG_NOTICE([NOTE: Building without readline support. If you want readline support, install its development package.])]
+ )
+ AM_CONDITIONAL(HAVE_READLINE, test "x$have_readline" = "xyes")
+])
AM_CONDITIONAL(BUILD_TOOLS, test "x$with_tools" = "xyes")
AC_ARG_WITH([dummy],
@@ -224,6 +210,7 @@ Configuration for $PACKAGE $VERSION:
Install prefix: .........: $prefix
USB backend: ............: $USB_BACKEND
+ Build tools: ............: $with_tools
Now type 'make' to build $PACKAGE $VERSION,
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 {
unsigned char* sep_nonce;
unsigned int sep_nonce_size;
uint16_t pid;
+ unsigned int have_cpid : 1;
+ unsigned int have_cprv : 1;
+ unsigned int have_cpfm : 1;
+ unsigned int have_scep : 1;
+ unsigned int have_bdid : 1;
+ unsigned int have_ecid : 1;
+ unsigned int have_ibfl : 1;
};
typedef enum {
@@ -132,8 +139,6 @@ enum {
/* library */
IRECV_API void irecv_set_debug_level(int level);
IRECV_API const char* irecv_strerror(irecv_error_t error);
-IRECV_API void irecv_init(void); /* deprecated: libirecovery has constructor now */
-IRECV_API void irecv_exit(void); /* deprecated: libirecovery has destructor now */
IRECV_API const char* irecv_version();
@@ -156,6 +161,7 @@ IRECV_API irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int c
IRECV_API irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface);
IRECV_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);
IRECV_API int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout);
+IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout);
/* events */
typedef 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 @@
#include <libimobiledevice-glue/thread.h>
#ifndef USE_DUMMY
-#ifndef WIN32
+#ifndef _WIN32
#ifndef HAVE_IOKIT
#include <libusb.h>
#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102))
@@ -71,6 +71,33 @@
#include "libirecovery.h"
+// Reference: https://stackoverflow.com/a/2390626/1806760
+// Initializer/finalizer sample for MSVC and GCC/Clang.
+// 2010-2016 Joe Lowe. Released into the public domain.
+
+#ifdef __cplusplus
+ #define INITIALIZER(f) \
+ static void f(void); \
+ struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
+ static void f(void)
+#elif defined(_MSC_VER)
+ #pragma section(".CRT$XCU",read)
+ #define INITIALIZER2_(f,p) \
+ static void f(void); \
+ __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
+ __pragma(comment(linker,"/include:" p #f "_")) \
+ static void f(void)
+ #ifdef _WIN64
+ #define INITIALIZER(f) INITIALIZER2_(f,"")
+ #else
+ #define INITIALIZER(f) INITIALIZER2_(f,"_")
+ #endif
+#else
+ #define INITIALIZER(f) \
+ static void f(void) __attribute__((__constructor__)); \
+ static void f(void)
+#endif
+
struct irecv_client_private {
int debug;
int usb_config;
@@ -80,7 +107,7 @@ struct irecv_client_private {
int isKIS;
struct irecv_device_info device_info;
#ifndef USE_DUMMY
-#ifndef WIN32
+#ifndef _WIN32
#ifndef HAVE_IOKIT
libusb_device_handle* handle;
#else
@@ -122,7 +149,7 @@ struct irecv_client_private {
static int libirecovery_debug = 0;
#ifndef USE_DUMMY
-#ifndef WIN32
+#ifndef _WIN32
#ifndef HAVE_IOKIT
static libusb_context* libirecovery_context = NULL;
#endif
@@ -187,6 +214,15 @@ static struct irecv_device irecv_devices[] = {
{ "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" },
{ "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" },
{ "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" },
+ { "iPhone17,1", "d93ap", 0x0C, 0x8140, "iPhone 16 Pro" },
+ { "iPhone17,2", "d94ap", 0x0E, 0x8140, "iPhone 16 Pro Max" },
+ { "iPhone17,3", "d47ap", 0x08, 0x8140, "iPhone 16" },
+ { "iPhone17,4", "d48ap", 0x0A, 0x8140, "iPhone 16 Plus" },
+ { "iPhone17,5", "v59ap", 0x04, 0x8140, "iPhone 16e" },
+ { "iPhone18,1", "v53ap", 0x0C, 0x8150, "iPhone 17 Pro" },
+ { "iPhone18,2", "v54ap", 0x0E, 0x8150, "iPhone 17 Pro Max" },
+ { "iPhone18,3", "v57ap", 0x08, 0x8150, "iPhone 17" },
+ { "iPhone18,4", "d23ap", 0x0A, 0x8150, "iPhone Air" },
/* iPod */
{ "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" },
{ "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" },
@@ -279,6 +315,26 @@ static struct irecv_device irecv_devices[] = {
{ "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" },
{ "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" },
{ "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" },
+ { "iPad14,8", "j507ap", 0x10, 0x8112, "iPad Air 11-inch (M2, WiFi)" },
+ { "iPad14,9", "j508ap", 0x12, 0x8112, "iPad Air 11-inch (M2, Cellular)" },
+ { "iPad14,10", "j537ap", 0x14, 0x8112, "iPad Air 13-inch (M2, WiFi)" },
+ { "iPad14,11", "j538ap", 0x16, 0x8112, "iPad Air 13-inch (M2, Cellular)" },
+ { "iPad15,3", "j607ap", 0x08, 0x8122, "iPad Air 11-inch (M3, WiFi)" },
+ { "iPad15,4", "j608ap", 0x0A, 0x8122, "iPad Air 11-inch (M3, Cellular)" },
+ { "iPad15,5", "j637ap", 0x0C, 0x8122, "iPad Air 13-inch (M3, WiFi)" },
+ { "iPad15,6", "j638ap", 0x0E, 0x8122, "iPad Air 13-inch (M3, Cellular)" },
+ { "iPad15,7", "j481ap", 0x10, 0x8120, "iPad (A16, WiFi)" },
+ { "iPad15,8", "j482ap", 0x12, 0x8120, "iPad (A16, Cellular)" },
+ { "iPad16,1", "j410ap", 0x08, 0x8130, "iPad mini (A17 Pro, WiFi)" },
+ { "iPad16,2", "j411ap", 0x0A, 0x8130, "iPad mini (A17 Pro, Cellular)" },
+ { "iPad16,3", "j717ap", 0x08, 0x8132, "iPad Pro 11-inch (M4, WiFi)" },
+ { "iPad16,4", "j718ap", 0x0A, 0x8132, "iPad Pro 11-inch (M4, Cellular)" },
+ { "iPad16,5", "j720ap", 0x0C, 0x8132, "iPad Pro 13-inch (M4, WiFi)" },
+ { "iPad16,6", "j721ap", 0x0E, 0x8132, "iPad Pro 13-inch (M4, Cellular)" },
+ { "iPad17,1", "j817ap", 0x08, 0x8142, "iPad Pro 11-inch (M5, WiFi)" },
+ { "iPad17,2", "j818ap", 0x0A, 0x8142, "iPad Pro 11-inch (M5, Cellular)" },
+ { "iPad17,3", "j820ap", 0x0C, 0x8142, "iPad Pro 13-inch (M5, WiFi)" },
+ { "iPad17,4", "j821ap", 0x0E, 0x8142, "iPad Pro 13-inch (M5, Cellular)" },
/* Apple TV */
{ "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" },
{ "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" },
@@ -337,6 +393,19 @@ static struct irecv_device irecv_devices[] = {
{ "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" },
{ "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" },
{ "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" },
+ { "Watch7,8", "n217sap", 0x10, 0x8310, "Apple Watch Series 10 (42mm)" },
+ { "Watch7,9", "n217bap", 0x12, 0x8310, "Apple Watch Series 10 (46mm)" },
+ { "Watch7,10", "n218sap", 0x14, 0x8310, "Apple Watch Series 10 (42mm Cellular)" },
+ { "Watch7,11", "n218bap", 0x16, 0x8310, "Apple Watch Series 10 (46mm Cellular)" },
+ { "Watch7,12", "n230ap", 0x22, 0x8310, "Apple Watch Ultra 3" },
+ { "Watch7,13", "n243sap", 0x28, 0x8310, "Apple Watch SE 3 (40mm)" },
+ { "Watch7,14", "n243bap", 0x2A, 0x8310, "Apple Watch SE 3 (44mm)" },
+ { "Watch7,15", "n244sap", 0x2C, 0x8310, "Apple Watch SE 3 (40mm Cellular)" },
+ { "Watch7,16", "n244bap", 0x2E, 0x8310, "Apple Watch SE 3 (44mm Cellular)" },
+ { "Watch7,17", "n227sap", 0x18, 0x8310, "Apple Watch Series 11 (42mm)" },
+ { "Watch7,18", "n227bap", 0x1A, 0x8310, "Apple Watch Series 11 (46mm)" },
+ { "Watch7,19", "n228sap", 0x1C, 0x8310, "Apple Watch Series 11 (42mm Cellular)" },
+ { "Watch7,20", "n228bap", 0x1E, 0x8310, "Apple Watch Series 11 (46mm Cellular)" },
/* Apple Silicon Macs */
{ "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" },
{ "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" },
@@ -373,6 +442,20 @@ static struct irecv_device irecv_devices[] = {
{ "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
{ "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" },
{ "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-inch, M3, 2024)" },
+ { "Mac15,14", "j575dap", 0x44, 0x6032, "Mac Studio (M3 Ultra, 2025)" },
+ { "Mac16,1", "j604ap", 0x22, 0x8132, "MacBook Pro (14-inch, M4, Nov 2024)" },
+ { "Mac16,2", "j623ap", 0x24, 0x8132, "iMac 24-inch (M4, Two Ports, 2024)" },
+ { "Mac16,3", "j624ap", 0x26, 0x8132, "iMac 24-inch (M4, Four Ports, 2024)" },
+ { "Mac16,5", "j616cap", 0x06, 0x6041, "MacBook Pro (16-inch, M4 Max, Nov 2024)" },
+ { "Mac16,6", "j614cap", 0x04, 0x6041, "MacBook Pro (14-inch, M4 Max, Nov 2024)" },
+ { "Mac16,7", "j616sap", 0x06, 0x6040, "MacBook Pro (16-inch, M4 Pro, Nov 2024)" },
+ { "Mac16,8", "j614sap", 0x04, 0x6040, "MacBook Pro (14-inch, M4 Pro, Nov 2024)" },
+ { "Mac16,9", "j575cap", 0x02, 0x6041, "Mac Studio (M4 Max, 2025)" },
+ { "Mac16,10", "j773gap", 0x2A, 0x8132, "Mac mini (M4, 2024)" },
+ { "Mac16,11", "j773sap", 0x02, 0x6040, "Mac mini (M4 Pro, 2024)" },
+ { "Mac16,12", "j713ap", 0x2C, 0x8132, "MacBook Air (13-inch, M4, 2025)" },
+ { "Mac16,13", "j715ap", 0x2E, 0x8132, "MacBook Air (15-inch, M4, 2025)" },
+ { "Mac17,2", "j704ap", 0x22, 0x8142, "MacBook Pro (14-inch, M5, 2025)" },
/* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */
{ "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" },
/* Apple T2 Coprocessor */
@@ -396,6 +479,7 @@ static struct irecv_device irecv_devices[] = {
{ "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" },
/* Apple Vision Pro */
{ "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" },
+ { "RealityDevice17,1", "n301aap", 0x42, 0x8142, "Apple Vision Pro (M5)" },
{ NULL, NULL, -1, -1, NULL }
};
@@ -470,7 +554,7 @@ static unsigned int crc32_lookup_t1[256] = {
#define crc32_step(a,b) \
a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8))
-#ifdef WIN32
+#ifdef _WIN32
#pragma pack(1)
typedef struct {
uint16_t vid;
@@ -563,12 +647,30 @@ typedef struct {
#pragma pack()
#endif
+#pragma pack(1)
+typedef struct {
+ uint16_t cmdcode;
+#define MSG_VERSION_QUERY 0x000
+#define MSG_ECHO 0x801
+#define MSG_DUMP_BUFFER 0x802
+#define MSG_SEND_COMMAND 0x803
+#define MSG_READ_FILE 0x804
+#define MSG_SEND_FILE 0x805
+#define MSG_CRC 0x807
+#define MSG_ACK 0x808
+#define MSG_REJECT 0x809
+ uint16_t magic; // always 0x1234
+ uint32_t size;
+ uint32_t loadaddr; // 0x0 for commands, 0x09000000 for files
+} legacyCMD;
+#pragma pack()
+
static THREAD_T th_event_handler = THREAD_T_NULL;
struct collection listeners;
static mutex_t listener_mutex;
struct collection devices;
static mutex_t device_mutex;
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
static CFRunLoopRef iokit_runloop = NULL;
#else
@@ -576,28 +678,10 @@ static libusb_context* irecv_hotplug_ctx = NULL;
#endif
#endif
-static void _irecv_init(void)
-{
- char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL");
- if (dbglvl) {
- libirecovery_debug = strtol(dbglvl, NULL, 0);
- irecv_set_debug_level(libirecovery_debug);
- }
-#ifndef USE_DUMMY
-#ifndef WIN32
-#ifndef HAVE_IOKIT
- libusb_init(&libirecovery_context);
-#endif
-#endif
- collection_init(&listeners);
- mutex_init(&listener_mutex);
-#endif
-}
-
static void _irecv_deinit(void)
{
#ifndef USE_DUMMY
-#ifndef WIN32
+#ifndef _WIN32
#ifndef HAVE_IOKIT
if (libirecovery_context != NULL) {
libusb_exit(libirecovery_context);
@@ -610,43 +694,24 @@ static void _irecv_deinit(void)
#endif
}
-static thread_once_t init_once = THREAD_ONCE_INIT;
-static thread_once_t deinit_once = THREAD_ONCE_INIT;
-
-#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR
- #if defined(__llvm__) || defined(__GNUC__)
- #define HAVE_ATTRIBUTE_CONSTRUCTOR
- #endif
-#endif
-
-#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR
-static void __attribute__((constructor)) libirecovery_initialize(void)
-{
- thread_once(&init_once, _irecv_init);
-}
-
-static void __attribute__((destructor)) libirecovery_deinitialize(void)
-{
- thread_once(&deinit_once, _irecv_deinit);
-}
-#elif defined(WIN32)
-BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
+INITIALIZER(_irecv_init)
{
- switch (dwReason) {
- case DLL_PROCESS_ATTACH:
- thread_once(&init_once, _irecv_init);
- break;
- case DLL_PROCESS_DETACH:
- thread_once(&deinit_once, _irecv_deinit);
- break;
- default:
- break;
+ char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL");
+ if (dbglvl) {
+ libirecovery_debug = strtol(dbglvl, NULL, 0);
+ irecv_set_debug_level(libirecovery_debug);
}
- return 1;
-}
-#else
-#warning No compiler support for constructor/destructor attributes, some features might not be available.
+#ifndef USE_DUMMY
+#ifndef _WIN32
+#ifndef HAVE_IOKIT
+ libusb_init(&libirecovery_context);
+#endif
+#endif
+ collection_init(&listeners);
+ mutex_init(&listener_mutex);
#endif
+ atexit(_irecv_deinit);
+}
#ifdef HAVE_IOKIT
static 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
static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
{
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size);
#else
@@ -710,10 +775,10 @@ static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc
unsigned short langid = 0;
unsigned char data[256];
int di, si;
- memset(data, 0, 256);
+ memset(data, 0, sizeof(data));
memset(buffer, 0, size);
- ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, 255, USB_TIMEOUT);
+ ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data)-1, USB_TIMEOUT);
if (ret < 0) return ret;
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
ptr = strstr(iboot_string, "CPID:");
if (ptr != NULL) {
sscanf(ptr, "CPID:%x", &client->device_info.cpid);
+ client->device_info.have_cpid = 1;
+ } else {
+ // early iOS 1 doesn't identify itself
+ client->device_info.cpid = 0x8900;
}
ptr = strstr(iboot_string, "CPRV:");
if (ptr != NULL) {
sscanf(ptr, "CPRV:%x", &client->device_info.cprv);
+ client->device_info.have_cprv = 1;
}
ptr = strstr(iboot_string, "CPFM:");
if (ptr != NULL) {
sscanf(ptr, "CPFM:%x", &client->device_info.cpfm);
+ client->device_info.have_cpfm = 1;
}
ptr = strstr(iboot_string, "SCEP:");
if (ptr != NULL) {
sscanf(ptr, "SCEP:%x", &client->device_info.scep);
+ client->device_info.have_scep = 1;
}
ptr = strstr(iboot_string, "BDID:");
@@ -771,16 +843,19 @@ static void irecv_load_device_info_from_iboot_string(irecv_client_t client, cons
uint64_t bdid = 0;
sscanf(ptr, "BDID:%" SCNx64, &bdid);
client->device_info.bdid = (unsigned int)bdid;
+ client->device_info.have_bdid = 1;
}
ptr = strstr(iboot_string, "ECID:");
if (ptr != NULL) {
sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid);
+ client->device_info.have_ecid = 1;
}
ptr = strstr(iboot_string, "IBFL:");
if (ptr != NULL) {
sscanf(ptr, "IBFL:%x", &client->device_info.ibfl);
+ client->device_info.have_ibfl = 1;
}
char tmp[256];
@@ -900,8 +975,8 @@ static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, un
*nonce = NULL;
*nonce_size = 0;
- memset(buf, 0, 256);
- len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*) buf, 255);
+ memset(buf, 0, sizeof(buf));
+ len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*)buf, sizeof(buf)-1);
if (len < 0) {
debug("%s: got length: %d\n", __func__, len);
return;
@@ -912,7 +987,7 @@ static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, un
irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf);
}
-#ifndef WIN32
+#ifndef _WIN32
static 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)
{
if (argCount > UINT8_MAX) {
@@ -1045,7 +1120,7 @@ static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, siz
static irecv_error_t irecv_kis_init(irecv_client_t client)
{
-#ifndef WIN32
+#ifndef _WIN32
irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL);
if (err != IRECV_E_SUCCESS) {
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)
static irecv_error_t irecv_kis_load_device_info(irecv_client_t client)
{
debug("Loading device info in KIS mode...\n");
-#ifdef WIN32
+#ifdef _WIN32
KIS_device_info kisInfo;
DWORD transferred = 0;
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)
return IRECV_E_SUCCESS;
}
-#ifdef WIN32
+#ifdef _WIN32
static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
static 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)
}
#endif
-void irecv_init(void)
-{
-#ifndef USE_DUMMY
- thread_once(&init_once, _irecv_init);
-#endif
-}
-
-void irecv_exit(void)
-{
-#ifndef USE_DUMMY
- thread_once(&deinit_once, _irecv_deinit);
-#endif
-}
-
#ifndef USE_DUMMY
#ifdef HAVE_IOKIT
static 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
#ifdef USE_DUMMY
return IRECV_E_UNSUPPORTED;
#else
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
return iokit_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
#else
@@ -1481,6 +1542,74 @@ static int iokit_usb_bulk_transfer(irecv_client_t client,
return IRECV_E_USB_INTERFACE;
}
+
+static int iokit_usb_interrupt_transfer(irecv_client_t client,
+ unsigned char endpoint,
+ unsigned char *data,
+ int length,
+ int *transferred,
+ unsigned int timeout)
+{
+ IOReturn result;
+ IOUSBInterfaceInterface300 **intf = client->usbInterface;
+ UInt32 size = length;
+ UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
+ UInt8 numEndpoints;
+
+ if (!intf) return IRECV_E_USB_INTERFACE;
+
+ result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
+
+ if (result != kIOReturnSuccess)
+ return IRECV_E_USB_INTERFACE;
+
+ for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
+ UInt8 direction = 0;
+ UInt8 number = 0;
+ UInt8 transferType = 0;
+ UInt16 maxPacketSize = 0;
+ UInt8 interval = 0;
+
+ result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
+ if (result != kIOReturnSuccess)
+ continue;
+
+ if (direction == 3)
+ direction = isUSBIn;
+
+ if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
+ continue;
+
+ // Just because
+ result = (*intf)->GetPipeStatus(intf, pipeRef);
+ switch (result) {
+ case kIOReturnSuccess: break;
+ case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
+ case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
+ default: return IRECV_E_USB_STATUS;
+ }
+
+ // Do the transfer
+ if (isUSBIn) {
+ result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
+ if (result != kIOReturnSuccess)
+ return IRECV_E_PIPE;
+ *transferred = size;
+
+ return IRECV_E_SUCCESS;
+ }
+ else {
+ result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
+ if (result != kIOReturnSuccess)
+ return IRECV_E_PIPE;
+ *transferred = size;
+
+ return IRECV_E_SUCCESS;
+ }
+ }
+
+ return IRECV_E_USB_INTERFACE;
+}
#endif
#endif
@@ -1496,7 +1625,7 @@ int irecv_usb_bulk_transfer(irecv_client_t client,
#else
int ret;
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout);
#else
@@ -1518,6 +1647,36 @@ int irecv_usb_bulk_transfer(irecv_client_t client,
#endif
}
+IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client,
+ unsigned char endpoint,
+ unsigned char *data,
+ int length,
+ int *transferred,
+ unsigned int timeout)
+{
+#ifdef USE_DUMMY
+ return IRECV_E_UNSUPPORTED;
+#else
+ int ret;
+
+#ifndef _WIN32
+#ifdef HAVE_IOKIT
+ return iokit_usb_interrupt_transfer(client, endpoint, data, length, transferred, timeout);
+#else
+ ret = libusb_interrupt_transfer(client->handle, endpoint, data, length, transferred, timeout);
+ if (ret < 0) {
+ libusb_clear_halt(client->handle, endpoint);
+ }
+#endif
+#else
+ // win32
+ return IRECV_E_UNSUPPORTED;
+#endif
+
+ return ret;
+#endif
+}
+
#ifndef USE_DUMMY
#ifdef HAVE_IOKIT
static 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
}
#endif
-#ifndef WIN32
+#ifndef _WIN32
#ifndef HAVE_IOKIT
static 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)
{
@@ -1703,8 +1862,8 @@ static irecv_error_t libusb_usb_open_handle_with_descriptor_and_ecid(irecv_clien
if (client->mode != KIS_PRODUCT_ID) {
char serial_str[256];
- memset(serial_str, 0, 256);
- irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, 255);
+ memset(serial_str, 0, sizeof(serial_str));
+ irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
irecv_load_device_info_from_iboot_string(client, serial_str);
}
@@ -1797,7 +1956,7 @@ irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
if (libirecovery_debug) {
irecv_set_debug_level(libirecovery_debug);
}
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
error = iokit_open_with_ecid(pclient, ecid);
#else
@@ -1880,7 +2039,7 @@ irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configurati
if (check_context(client) != IRECV_E_SUCCESS)
return IRECV_E_NO_DEVICE;
-#ifndef WIN32
+#ifndef _WIN32
debug("Setting to configuration %d\n", configuration);
#ifdef HAVE_IOKIT
@@ -1999,7 +2158,7 @@ irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface,
return IRECV_E_NO_DEVICE;
debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface);
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) {
return IRECV_E_USB_INTERFACE;
@@ -2037,7 +2196,7 @@ irecv_error_t irecv_reset(irecv_client_t client)
if (check_context(client) != IRECV_E_SUCCESS)
return IRECV_E_NO_DEVICE;
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
IOReturn result;
@@ -2176,7 +2335,7 @@ struct irecv_usb_device_info {
int alive;
};
-#ifdef WIN32
+#ifdef _WIN32
struct irecv_win_dev_ctx {
PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
uint32_t location;
@@ -2194,7 +2353,7 @@ static int _irecv_is_recovery_device(void *device)
{
uint16_t vendor_id = 0;
uint16_t product_id = 0;
-#ifdef WIN32
+#ifdef _WIN32
const char *path = (const char*)device;
unsigned int vendor = 0;
unsigned int product = 0;
@@ -2261,8 +2420,8 @@ static void* _irecv_handle_device_add(void *userdata)
irecv_error_t error = 0;
irecv_client_t client = NULL;
- memset(serial_str, 0, 256);
-#ifdef WIN32
+ memset(serial_str, 0, sizeof(serial_str));
+#ifdef _WIN32
struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata;
PSP_DEVICE_INTERFACE_DETAIL_DATA_A details = win_ctx->details;
LPSTR result = (LPSTR)details->DevicePath;
@@ -2294,6 +2453,11 @@ static void* _irecv_handle_device_add(void *userdata)
if (product_id == KIS_PRODUCT_ID) {
client = (irecv_client_t)malloc(sizeof(struct irecv_client_private));
+ if (client == NULL) {
+ debug("%s: Failed to allocate memory\n", __func__);
+ return NULL;
+ }
+ memset(client, '\0', sizeof(struct irecv_client_private));
client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (client->handle == INVALID_HANDLE_VALUE) {
debug("%s: Failed to open device path %s\n", __func__, result);
@@ -2317,7 +2481,7 @@ static void* _irecv_handle_device_add(void *userdata)
}
}
-#else /* !WIN32 */
+#else /* !_WIN32 */
#ifdef HAVE_IOKIT
struct irecv_iokit_dev_ctx* iokit_ctx = (struct irecv_iokit_dev_ctx*)userdata;
io_service_t device = iokit_ctx->device;
@@ -2349,13 +2513,19 @@ static void* _irecv_handle_device_add(void *userdata)
if (product_id == KIS_PRODUCT_ID) {
IOObjectRetain(device);
-
- error = iokit_usb_open_service(&client, device);
+ int i = 0;
+ for (i = 0; i < 10; i++) {
+ error = iokit_usb_open_service(&client, device);
+ if (error == IRECV_E_SUCCESS) {
+ break;
+ }
+ debug("%s: Could not open KIS device, retrying...\n", __func__);
+ usleep(500000);
+ }
if (error != IRECV_E_SUCCESS) {
debug("%s: ERROR: could not open KIS device!\n", __func__);
return NULL;
}
-
product_id = client->mode;
} else {
CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
@@ -2397,7 +2567,7 @@ static void* _irecv_handle_device_add(void *userdata)
product_id = client->mode;
} else {
- libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, 255);
+ libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
if (libusb_error < 0) {
debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error));
return 0;
@@ -2405,17 +2575,32 @@ static void* _irecv_handle_device_add(void *userdata)
libusb_close(usb_handle);
}
#endif /* !HAVE_IOKIT */
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
memset(&client_loc, '\0', sizeof(client_loc));
if (product_id == KIS_PRODUCT_ID) {
- error = irecv_usb_set_configuration(client, 1);
+ int i = 0;
+ for (i = 0; i < 10; i++) {
+ error = irecv_usb_set_configuration(client, 1);
+ if (error == IRECV_E_SUCCESS) {
+ break;
+ }
+ debug("Failed to set configuration, error %d, retrying...\n", error);
+ usleep(500000);
+ }
if (error != IRECV_E_SUCCESS) {
debug("Failed to set configuration, error %d\n", error);
irecv_close(client);
return NULL;
}
- error = irecv_usb_set_interface(client, 0, 0);
+ for (i = 0; i < 10; i++) {
+ error = irecv_usb_set_interface(client, 0, 0);
+ if (error == IRECV_E_SUCCESS) {
+ break;
+ }
+ debug("Failed to set interface, error %d, retrying...\n", error);
+ usleep(500000);
+ }
if (error != IRECV_E_SUCCESS) {
debug("Failed to set interface, error %d\n", error);
irecv_close(client);
@@ -2473,7 +2658,7 @@ static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo)
{
irecv_device_event_t dev_event;
dev_event.type = IRECV_DEVICE_REMOVE;
- dev_event.mode = 0;
+ dev_event.mode = devinfo->mode;
dev_event.device_info = &(devinfo->device_info);
mutex_lock(&listener_mutex);
FOREACH(struct irecv_device_event_context* context, &listeners) {
@@ -2493,7 +2678,7 @@ static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo)
free(devinfo);
}
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
static void iokit_device_added(void *refcon, io_iterator_t iterator)
{
@@ -2590,7 +2775,7 @@ static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, lib
}
#endif /* HAVE_LIBUSB_HOTPLUG_API */
#endif /* !HAVE_IOKIT */
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
struct _irecv_event_handler_info {
cond_t startup_cond;
@@ -2600,7 +2785,7 @@ struct _irecv_event_handler_info {
static void *_irecv_event_handler(void* data)
{
struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data;
-#ifdef WIN32
+#ifdef _WIN32
struct collection newDevices;
const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
int running = 1;
@@ -2744,7 +2929,7 @@ static void *_irecv_event_handler(void* data)
} while (running);
collection_free(&newDevices);
-#else /* !WIN32 */
+#else /* !_WIN32 */
#ifdef HAVE_IOKIT
kern_return_t kr;
@@ -2869,7 +3054,7 @@ static void *_irecv_event_handler(void* data)
} while (running);
#endif /* !HAVE_LIBUSB_HOTPLUG_API */
#endif /* !HAVE_IOKIT */
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
return NULL;
}
#endif /* !USE_DUMMY */
@@ -2898,7 +3083,7 @@ irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context
struct _irecv_event_handler_info info;
cond_init(&info.startup_cond);
mutex_init(&info.startup_mutex);
-#ifndef WIN32
+#ifndef _WIN32
#ifndef HAVE_IOKIT
libusb_init(&irecv_hotplug_ctx);
#endif
@@ -2948,6 +3133,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex
if (num == 0 && th_event_handler != THREAD_T_NULL && thread_alive(th_event_handler)) {
#ifdef HAVE_IOKIT
if (iokit_runloop) {
+ while (!CFRunLoopIsWaiting(iokit_runloop)) usleep(420);
CFRunLoopStop(iokit_runloop);
iokit_runloop = NULL;
}
@@ -2970,7 +3156,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex
collection_free(&devices);
mutex_unlock(&device_mutex);
mutex_destroy(&device_mutex);
-#ifndef WIN32
+#ifndef _WIN32
#ifndef HAVE_IOKIT
libusb_exit(irecv_hotplug_ctx);
irecv_hotplug_ctx = NULL;
@@ -2984,7 +3170,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex
#endif
}
-irecv_error_t irecv_close(irecv_client_t client)
+static irecv_error_t irecv_cleanup(irecv_client_t client)
{
#ifdef USE_DUMMY
return IRECV_E_UNSUPPORTED;
@@ -2998,7 +3184,7 @@ irecv_error_t irecv_close(irecv_client_t client)
event.type = IRECV_DISCONNECTED;
client->disconnected_callback(client, &event);
}
-#ifndef WIN32
+#ifndef _WIN32
#ifdef HAVE_IOKIT
if (client->usbInterface) {
(*client->usbInterface)->USBInterfaceClose(client->usbInterface);
@@ -3028,20 +3214,28 @@ irecv_error_t irecv_close(irecv_client_t client)
free(client->device_info.serial_string);
free(client->device_info.ap_nonce);
free(client->device_info.sep_nonce);
-
- free(client);
- client = NULL;
}
return IRECV_E_SUCCESS;
#endif
}
+irecv_error_t irecv_close(irecv_client_t client)
+{
+ irecv_error_t ret = IRECV_E_SUCCESS;
+ if (client) {
+ ret = irecv_cleanup(client);
+ free(client);
+ client = NULL;
+ }
+ return ret;
+}
+
void irecv_set_debug_level(int level)
{
libirecovery_debug = level;
#ifndef USE_DUMMY
-#ifndef WIN32
+#ifndef _WIN32
#ifndef HAVE_IOKIT
if (libirecovery_context) {
#if LIBUSB_API_VERSION >= 0x01000106
@@ -3072,6 +3266,35 @@ static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* c
return IRECV_E_INVALID_INPUT;
}
+ if (length > 0 && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
+ int bytes = 0;
+ irecv_error_t error = 0;
+#ifdef DEBUG
+ uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
+ if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
+ if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
+#endif
+ char cmdstr[0x100] = {0};
+ if (length & 0xf) {
+ length &= ~0xf;
+ length += 0x10;
+ }
+ snprintf(cmdstr, sizeof(cmdstr), "%s\n",command);
+ legacyCMD cmd = {
+ MSG_SEND_COMMAND,
+ 0x1234, //magic
+ (uint32_t)length, //zero terminated?
+ 0x0
+ };
+ if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
+ if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x02, (unsigned char*)cmdstr, length, &bytes, USB_TIMEOUT))) return error;
+ sleep(1); //go easy on this old device
+ return IRECV_E_SUCCESS;
+ }
+
if (length > 0) {
irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT);
}
@@ -3179,8 +3402,8 @@ static irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* statu
}
unsigned char buffer[6];
- memset(buffer, '\0', 6);
- if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, USB_TIMEOUT) != 6) {
+ memset(buffer, '\0', sizeof(buffer));
+ if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, sizeof(buffer), USB_TIMEOUT) != sizeof(buffer)) {
*status = 0;
return IRECV_E_USB_STATUS;
}
@@ -3205,7 +3428,7 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char*
if (toUpload > 0x4000)
toUpload = 0x4000;
-#ifdef WIN32
+#ifdef _WIN32
memcpy(chunk->data, buffer, toUpload);
chunk->size = toUpload;
chunk->address = address;
@@ -3222,7 +3445,7 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char*
memcpy(chunk->data, buffer, toUpload);
#endif
-#ifdef WIN32
+#ifdef _WIN32
DWORD transferred = 0;
int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL);
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*
free(chunk);
if (options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) {
-#ifdef WIN32
+#ifdef _WIN32
DWORD amount = (DWORD)origLen;
DWORD transferred = 0;
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
irecv_error_t error = 0;
int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
+ int legacy_recovery_mode = 0;
+ int isiOS2 = 0;
+
+ if (recovery_mode && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
+#ifdef DEBUG
+ uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
+ int bytes = 0;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
+ if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
+#endif
+ legacy_recovery_mode = 1;
+ }
+
+ if (recovery_mode && !legacy_recovery_mode) {
+ // we are in recovery mode and we are not dealing with iOS 1.x
+ if ((client->device_info.cpid == 0x8900 || client->device_info.cpid == 0x8720) && !client->device_info.have_ibfl) {
+ // iOS 2.x doesn't have IBFL tag, but iOS 3 does
+ // Also, to avoid false activation of this codepath, restrict it to the only two CPID which can run iOS 2
+ recovery_mode = 0; // iOS 2 recovery mode works same as DFU mode
+ isiOS2 = 1;
+ options |= IRECV_SEND_OPT_DFU_NOTIFY_FINISH;
+ }
+ }
if (check_context(client) != IRECV_E_SUCCESS)
return IRECV_E_NO_DEVICE;
@@ -3291,6 +3538,7 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10};
int dfu_crc = 1;
int packet_size = recovery_mode ? 0x8000 : 0x800;
+ if (legacy_recovery_mode) packet_size = 0x200;
if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) {
packet_size = 0x40;
dfu_crc = 0;
@@ -3305,7 +3553,24 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
}
/* initiate transfer */
- if (recovery_mode) {
+ if (legacy_recovery_mode) {
+ int bytes = 0;
+ uint32_t loadaddr0x8900 = 0x09000000;
+ const char *ios1_overwrite_loadaddr = getenv("LIBIRECOVERY_IOS1_OVERWRITE_LOADADDR");
+ if (ios1_overwrite_loadaddr) {
+ sscanf(ios1_overwrite_loadaddr, "0x%x",&loadaddr0x8900);
+ debug("Overwriting loadaddr requested by env var. uploading to 0x%08x\n",loadaddr0x8900);
+ }
+ legacyCMD cmd = {
+ MSG_SEND_FILE,
+ 0x1234, //magic
+ (uint32_t)length,
+ loadaddr0x8900
+ };
+ if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
+ if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
+ } else if (recovery_mode) {
error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT);
} else {
uint8_t state = 0;
@@ -3318,6 +3583,14 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
case 2:
/* DFU IDLE */
break;
+ case 8:
+ /* DFU WAIT RESET */
+ if (!isiOS2) {
+ debug("Unexpected state %d in non-iOS2 mode!, issuing ABORT\n", state);
+ irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT);
+ error = IRECV_E_USB_UPLOAD;
+ }
+ break;
case 10:
debug("DFU ERROR, issuing CLRSTATUS\n");
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
for (i = 0; i < packets; i++) {
int size = (i + 1) < packets ? packet_size : last;
- /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */
- if (recovery_mode) {
+ if (legacy_recovery_mode) {
+ // Use interrupt transfer for legacy devices
+ error = irecv_usb_interrupt_transfer(client, 0x05, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
+ } else if (recovery_mode) {
+ // Use bulk transfer for recovery mode
error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
} else {
+ // Use control transfer for DFU and WTF mode
if (dfu_crc) {
int j;
for (j = 0; j < size; j++) {
@@ -3451,6 +3728,17 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
}
irecv_reset(client);
+
+ if (isiOS2) {
+ irecv_reconnect(client, 0);
+ }
+ }
+
+ if (legacy_recovery_mode) {
+ irecv_reconnect(client, 0);
+ char cmdstr[0x100] = {0};
+ snprintf(cmdstr, sizeof(cmdstr), "setenv filesize %d", (int)length);
+ irecv_send_command(client, cmdstr);
}
return IRECV_E_SUCCESS;
@@ -3508,6 +3796,11 @@ irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** v
return IRECV_E_INVALID_INPUT;
}
+ if (client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
+ debug("iOS 1 doesn't support getenv\n");
+ return IRECV_E_UNSUPPORTED;
+ }
+
memset(command, '\0', sizeof(command));
snprintf(command, sizeof(command)-1, "getenv %s", variable);
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
return error;
}
- char* response = (char*) malloc(256);
+ int rsize = 256;
+ char* response = (char*) malloc(rsize);
if (response == NULL) {
return IRECV_E_OUT_OF_MEMORY;
}
- memset(response, '\0', 256);
- irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT);
+ memset(response, '\0', rsize);
+ irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
*value = response;
@@ -3543,13 +3837,14 @@ irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value)
*value = 0;
- char* response = (char*) malloc(256);
+ int rsize = 256;
+ char* response = (char*) malloc(rsize);
if (response == NULL) {
return IRECV_E_OUT_OF_MEMORY;
}
- memset(response, '\0', 256);
- irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT);
+ memset(response, '\0', rsize);
+ irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
*value = (unsigned int) *response;
@@ -3992,7 +4287,7 @@ irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause)
uint64_t ecid = client->device_info.ecid;
if (check_context(client) == IRECV_E_SUCCESS) {
- irecv_close(client);
+ irecv_cleanup(client);
}
if (initial_pause > 0) {
@@ -4012,6 +4307,11 @@ irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause)
new_client->postcommand_callback = postcommand_callback;
new_client->disconnected_callback = disconnected_callback;
+ // keep old handle valid
+ memcpy(client, new_client, sizeof(*client));
+ free(new_client);
+ new_client = client;
+
if (new_client->connected_callback != NULL) {
irecv_event_t event;
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
AM_CPPFLAGS = -I$(top_srcdir)/include
AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS)
-AM_LDFLAGS = $(libusb_LIBS) -lreadline
+AM_LDFLAGS = $(libusb_LIBS)
bin_PROGRAMS = irecovery
irecovery_SOURCES = irecovery.c
irecovery_CFLAGS = $(AM_CFLAGS)
irecovery_LDFLAGS = $(AM_LDFLAGS)
+if HAVE_READLINE
+irecovery_LDFLAGS += -lreadline
+endif
irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la
endif
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 @@
#include <string.h>
#include <getopt.h>
#include <inttypes.h>
+#include <ctype.h>
#include <libirecovery.h>
+#ifdef HAVE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
+#else
+#ifndef _WIN32
+#include <termios.h>
+#endif
+#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
-#ifndef sleep
+#include <conio.h>
#define sleep(n) Sleep(1000 * n)
#endif
-#endif
#define FILE_HISTORY_PATH ".irecovery"
#define debug(...) if (verbose) fprintf(stderr, __VA_ARGS__)
@@ -271,14 +277,48 @@ static void parse_command(irecv_client_t client, unsigned char* command, unsigne
static void load_command_history()
{
+#ifdef HAVE_READLINE
read_history(FILE_HISTORY_PATH);
+#endif
}
-static void append_command_to_history(char* cmd)
+static void append_command_to_history(const char* cmd)
{
+#ifdef HAVE_READLINE
add_history(cmd);
write_history(FILE_HISTORY_PATH);
+#endif
+}
+
+#ifndef HAVE_READLINE
+#ifdef _WIN32
+#define BS_CC '\b'
+#else
+#define BS_CC 0x7f
+#define getch getchar
+#endif
+static void get_input(char *buf, int maxlen)
+{
+ int len = 0;
+ int c;
+
+ while ((c = getch())) {
+ if ((c == '\r') || (c == '\n')) {
+ break;
+ }
+ if (isprint(c)) {
+ if (len < maxlen-1)
+ buf[len++] = c;
+ } else if (c == BS_CC) {
+ if (len > 0) {
+ fputs("\b \b", stdout);
+ len--;
+ }
+ }
+ }
+ buf[len] = 0;
}
+#endif
static void init_shell(irecv_client_t client)
{
@@ -294,8 +334,15 @@ static void init_shell(irecv_client_t client)
debug("%s\n", irecv_strerror(error));
break;
}
-
+#ifdef HAVE_READLINE
char* cmd = readline("> ");
+#else
+ char cmdbuf[4096];
+ const char* cmd = &cmdbuf[0];
+ printf("> ");
+ fflush(stdout);
+ get_input(cmdbuf, sizeof(cmdbuf));
+#endif
if (cmd && *cmd) {
if (_is_breq_command(cmd)) {
error = irecv_send_command_breq(client, cmd, 1);
@@ -307,8 +354,10 @@ static void init_shell(irecv_client_t client)
}
append_command_to_history(cmd);
- free(cmd);
}
+#ifdef HAVE_READLINE
+ free(cmd);
+#endif
}
}
@@ -544,7 +593,11 @@ int main(int argc, char* argv[])
return 0;
case 'V':
- printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
+ printf("%s %s", TOOL_NAME, PACKAGE_VERSION);
+#ifdef HAVE_READLINE
+ printf(" (readline)");
+#endif
+ printf("\n");
return 0;
default: