summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2019-09-04 23:56:37 +0200
committerGravatar Nikias Bassen2019-09-04 23:56:37 +0200
commit38befc71844a6108a78e1fe5a76946450d49ca76 (patch)
tree3a886d468803b94f8b3c8fca1fc2d2802414cedc
parent2a38fe45f62cbd5c554293b7d82dbbb74ec8aeca (diff)
downloadlibirecovery-38befc71844a6108a78e1fe5a76946450d49ca76.tar.gz
libirecovery-38befc71844a6108a78e1fe5a76946450d49ca76.tar.bz2
Add device add/remove event subscription API
-rw-r--r--configure.ac96
-rw-r--r--include/libirecovery.h23
-rw-r--r--src/Makefile.am18
-rw-r--r--src/libirecovery.c764
-rw-r--r--tools/irecovery.c4
5 files changed, 809 insertions, 96 deletions
diff --git a/configure.ac b/configure.ac
index 1f10bb7..6481d74 100644
--- a/configure.ac
+++ b/configure.ac
@@ -28,36 +28,59 @@ AC_PROG_CXX
AM_PROG_CC_C_O
AC_PROG_LIBTOOL
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_REALLOC
+AC_CHECK_FUNCS([strdup strerror strndup])
+
# Checks for libraries.
AC_CHECK_HEADERS([readline/readline.h], [],
[AC_MSG_ERROR([Please install readline development headers])]
)
# Check additional platform flags
-case "$host_os" in
+AC_MSG_CHECKING([for platform-specific build settings])
+case ${host_os} in
darwin*)
+ AC_MSG_RESULT([${host_os}])
AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [
AC_CHECK_HEADER(IOKit/usb/IOUSBLib.h, [
- LIBIRECOVERYLDFLAGS="-framework IOKit -framework CoreFoundation"
+ GLOBAL_LDFLAGS+=" -framework IOKit -framework CoreFoundation"
have_iokit=yes
], [])
], [])
+ AX_PTHREAD([], [AC_MSG_ERROR([pthread is required to build $PACKAGE])])
;;
mingw32*)
- LDFLAGS+=" -static-libgcc"
- LIBIRECOVERYLDFLAGS=" -lkernel32 -lmsvcrt -lsetupapi"
+ AC_MSG_RESULT([${host_os}])
+ GLOBAL_LDFLAGS+=" -static-libgcc -lkernel32 -lmsvcrt -lsetupapi"
+ win32=true
;;
cygwin*)
+ AC_MSG_RESULT([${host_os}])
CC=gcc-3
CFLAGS+=" -mno-cygwin"
- LDFLAGS+=" -static-libgcc"
- LIBIRECOVERYLDFLAGS=" -lkernel32 -lmsvcrt -lsetupapi"
+ GLOBAL_LDFLAGS+=" -static-libgcc -lkernel32 -lmsvcrt -lsetupapi"
+ win32=true
;;
*)
- LIBIRECOVERYLDFLAGS=
+ AC_MSG_RESULT([${host_os}])
+ AX_PTHREAD([], [AC_MSG_ERROR([pthread is required to build $PACKAGE])])
;;
esac
-AC_SUBST(LIBIRECOVERYLDFLAGS)
+AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
AC_ARG_WITH([dummy],
[AS_HELP_STRING([--with-dummy], [Use no USB driver at all [default=no]. This is only useful if you just want to query the device list by product type or hardware model. All other operations are no-ops or will return IRECV_E_UNSUPPORTED.])],
@@ -69,61 +92,30 @@ AS_IF([test "x$have_iokit" = "xyes"], [
[AS_HELP_STRING([--with-iokit], [Use IOKit instead of libusb on OS X [default=yes]])],
[],
[with_iokit=yes])
- ], []
+ ]
)
AS_IF([test "x$with_dummy" = "xyes"], [
AC_DEFINE(USE_DUMMY, 1, [Define if we are using dummy USB driver])
USB_BACKEND="dummy"
], [
- AS_IF([test "x$with_iokit" = "xyes" && test "x$have_iokit" = "xyes"] , [
- AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit])
- USB_BACKEND="IOKit"
- ],
- [
+ AS_IF([test "x$with_iokit" = "xyes" && test "x$have_iokit" = "xyes"], [
+ AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit])
+ USB_BACKEND="IOKit"
+ ], [
+ AS_IF([test "x$win32" = "xtrue"], [
+ USB_BACKEND="win32 native (setupapi)"
+ ], [
PKG_CHECK_MODULES(libusb, libusb-1.0 >= $LIBUSB_VERSION)
USB_BACKEND="libusb `$PKG_CONFIG --modversion libusb-1.0`"
- LIBUSB_REQUIRED="libusb-1.0 >= $LIBUSB_VERSION"
- AC_SUBST(LIBUSB_REQUIRED)
- ]
- )
+ ])
+ ])
])
-# Checks for header files.
-AC_HEADER_STDC
-AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
-
-# Checks for typedefs, structures, and compiler characteristics.
-AC_C_CONST
-AC_TYPE_SIZE_T
-AC_TYPE_SSIZE_T
-AC_TYPE_UINT16_T
-AC_TYPE_UINT32_T
-AC_TYPE_UINT8_T
-
-# Checks for library functions.
-AC_FUNC_MALLOC
-AC_FUNC_REALLOC
-AC_CHECK_FUNCS([strcasecmp strdup strerror strndup])
-
-# Check for operating system
-AC_MSG_CHECKING([whether to enable WIN32 build settings])
-case ${host_os} in
- *mingw32*|*cygwin*)
- win32=true
- AC_MSG_RESULT([yes])
- AC_CHECK_TOOL([WINDRES], [windres], AC_MSG_ERROR([windres not found]))
- AC_SUBST(WINDRES)
- ;;
- *)
- win32=false
- AC_MSG_RESULT([no])
- ;;
-esac
-AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
-
-AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fvisibility=hidden")
+AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fvisibility=hidden $PTHREAD_CFLAGS")
+GLOBAL_LDFLAGS+=" $PTHREAD_LIBS"
AC_SUBST(GLOBAL_CFLAGS)
+AC_SUBST(GLOBAL_LDFLAGS)
case "$GLOBAL_CFLAGS" in
*-fvisibility=hidden*)
diff --git a/include/libirecovery.h b/include/libirecovery.h
index 73fe6f0..201be0d 100644
--- a/include/libirecovery.h
+++ b/include/libirecovery.h
@@ -2,8 +2,8 @@
* libirecovery.h
* Communication to iBoot/iBSS on Apple iOS devices via USB
*
+ * Copyright (c) 2012-2019 Nikias Bassen <nikias@gmx.li>
* Copyright (c) 2012-2013 Martin Szulecki <m.szulecki@libimobiledevice.org>
- * Copyright (c) 2012-2013 Nikias Bassen
* Copyright (c) 2010 Chronic-Dev Team
* Copyright (c) 2010 Joshua Hill
*
@@ -27,7 +27,7 @@ extern "C" {
#include <stdint.h>
-enum {
+enum irecv_mode {
IRECV_K_RECOVERY_MODE_1 = 0x1280,
IRECV_K_RECOVERY_MODE_2 = 0x1281,
IRECV_K_RECOVERY_MODE_3 = 0x1282,
@@ -93,14 +93,25 @@ struct irecv_device_info {
unsigned int sep_nonce_size;
};
+typedef enum {
+ IRECV_DEVICE_ADD = 1,
+ IRECV_DEVICE_REMOVE = 2
+} irecv_device_event_type;
+
+typedef struct {
+ irecv_device_event_type type;
+ enum irecv_mode mode;
+ struct irecv_device_info *device_info;
+} irecv_device_event_t;
+
typedef struct irecv_client_private irecv_client_private;
typedef irecv_client_private* irecv_client_t;
/* library */
void irecv_set_debug_level(int level);
const char* irecv_strerror(irecv_error_t error);
-void irecv_init(void);
-void irecv_exit(void);
+void irecv_init(void); /* deprecated: libirecovery has constructor now */
+void irecv_exit(void); /* deprecated: libirecovery has destructor now */
/* device connectivity */
irecv_error_t irecv_open_with_ecid(irecv_client_t* client, unsigned long long ecid);
@@ -123,6 +134,10 @@ int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, u
int irecv_usb_bulk_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);
+typedef struct irecv_device_event_context* irecv_device_event_context_t;
+irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data);
+irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context);
typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event);
irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data);
irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type);
diff --git a/src/Makefile.am b/src/Makefile.am
index 73ae945..265c91a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,19 +1,21 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
-AM_CFLAGS = \
- $(GLOBAL_CFLAGS) \
- $(LFS_CFLAGS) \
+AM_CFLAGS = \
+ $(GLOBAL_CFLAGS) \
+ $(LFS_CFLAGS) \
$(libusb_CFLAGS)
-AM_LDFLAGS = \
- $(libusb_LIBS) \
- $(LIBIRECOVERYLDFLAGS)
+AM_LDFLAGS = \
+ $(GLOBAL_LDFLAGS) \
+ $(libusb_LIBS)
lib_LTLIBRARIES = libirecovery.la
libirecovery_la_CFLAGS = $(AM_CFLAGS)
libirecovery_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIRECOVERY_SO_VERSION) -no-undefined
-libirecovery_la_SOURCES = libirecovery.c
+libirecovery_la_SOURCES = libirecovery.c \
+ utils.c utils.h \
+ thread.c thread.h
if WIN32
libirecovery_la_LDFLAGS += -avoid-version
-endif \ No newline at end of file
+endif
diff --git a/src/libirecovery.c b/src/libirecovery.c
index be3ee35..5b86786 100644
--- a/src/libirecovery.c
+++ b/src/libirecovery.c
@@ -2,7 +2,7 @@
* libirecovery.c
* Communication to iBoot/iBSS on Apple iOS devices via USB
*
- * Copyright (c) 2011-2017 Nikias Bassen
+ * Copyright (c) 2011-2019 Nikias Bassen <nikias@gmx.li>
* Copyright (c) 2012-2015 Martin Szulecki <martin.szulecki@libimobiledevice.org>
* Copyright (c) 2010 Chronic-Dev Team
* Copyright (c) 2010 Joshua Hill
@@ -35,6 +35,10 @@
#ifndef WIN32
#ifndef HAVE_IOKIT
#include <libusb.h>
+#include <pthread.h>
+#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102))
+#define HAVE_LIBUSB_HOTPLUG_API 1
+#endif
#else
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/usb/IOUSBLib.h>
@@ -66,6 +70,8 @@
#endif
#include "libirecovery.h"
+#include "utils.h"
+#include "thread.h"
struct irecv_client_private {
int debug;
@@ -287,6 +293,78 @@ static unsigned int crc32_lookup_t1[256] = {
#define crc32_step(a,b) \
a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8))
+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
+#ifdef HAVE_IOKIT
+static CFRunLoopRef iokit_runloop = NULL;
+#else
+static libusb_context* irecv_hotplug_ctx = NULL;
+#endif
+#endif
+
+static void _irecv_init(void)
+{
+#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 HAVE_IOKIT
+ if (libirecovery_context != NULL) {
+ libusb_exit(libirecovery_context);
+ libirecovery_context = NULL;
+ }
+#endif
+#endif
+ collection_free(&listeners);
+ mutex_destroy(&listener_mutex);
+#endif
+}
+
+static thread_once_t init_once = THREAD_ONCE_INIT;
+static thread_once_t deinit_once = THREAD_ONCE_INIT;
+
+#ifdef WIN32
+BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
+{
+ 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;
+ }
+ return 1;
+}
+#else
+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);
+}
+#endif
+
#ifdef HAVE_IOKIT
static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) {
@@ -794,27 +872,14 @@ static int check_context(irecv_client_t client) {
}
#endif
-IRECV_API void irecv_init(void) {
-#ifndef USE_DUMMY
-#ifndef WIN32
-#ifndef HAVE_IOKIT
- libusb_init(&libirecovery_context);
-#endif
-#endif
-#endif
+IRECV_API void irecv_init(void)
+{
+ thread_once(&init_once, _irecv_init);
}
-IRECV_API void irecv_exit(void) {
-#ifndef USE_DUMMY
-#ifndef WIN32
-#ifndef HAVE_IOKIT
- if (libirecovery_context != NULL) {
- libusb_exit(libirecovery_context);
- libirecovery_context = NULL;
- }
-#endif
-#endif
-#endif
+IRECV_API void irecv_exit(void)
+{
+ thread_once(&deinit_once, _irecv_deinit);
}
#ifndef USE_DUMMY
@@ -1114,8 +1179,8 @@ static irecv_error_t iokit_open_with_ecid(irecv_client_t* pclient, unsigned long
CFRange range;
UInt16 wtf_pids[] = { IRECV_K_WTF_MODE, 0};
- UInt16 all_pids[] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_RECOVERY_MODE_2, 0 }; // 0x1222, 0x1227, 0x1281
-UInt16 *pids = all_pids;
+ UInt16 all_pids[] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, 0 };
+ UInt16 *pids = all_pids;
int i;
if (pclient == NULL) {
@@ -1187,12 +1252,14 @@ IRECV_API irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, unsigned l
#ifdef USE_DUMMY
return IRECV_E_UNSUPPORTED;
#else
+ int ret = IRECV_E_UNABLE_TO_CONNECT;
+
if(libirecovery_debug) {
irecv_set_debug_level(libirecovery_debug);
}
#ifndef WIN32
#ifdef HAVE_IOKIT
- return iokit_open_with_ecid(pclient, ecid);
+ ret = iokit_open_with_ecid(pclient, ecid);
#else
int i = 0;
struct libusb_device* usb_device = NULL;
@@ -1297,15 +1364,13 @@ IRECV_API irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, unsigned l
libusb_free_device_list(usb_device_list, 1);
- return IRECV_E_SUCCESS;
+ ret = IRECV_E_SUCCESS;
}
}
}
-
- return IRECV_E_UNABLE_TO_CONNECT;
#endif
#else
- int ret = mobiledevice_connect(pclient, ecid);
+ ret = mobiledevice_connect(pclient, ecid);
if (ret == IRECV_E_SUCCESS) {
irecv_client_t client = *pclient;
int error = IRECV_E_SUCCESS;
@@ -1321,9 +1386,18 @@ IRECV_API irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, unsigned l
debug("WARNING: set interface failed, error %d\n", error);
}
}
-
- return ret;
#endif
+ if (ret == IRECV_E_SUCCESS) {
+ if ((*pclient)->connected_callback != NULL) {
+ irecv_event_t event;
+ event.size = 0;
+ event.data = NULL;
+ event.progress = 0;
+ event.type = IRECV_CONNECTED;
+ (*pclient)->connected_callback(*pclient, &event);
+ }
+ }
+ return ret;
#endif
}
@@ -1609,6 +1683,619 @@ IRECV_API irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_eve
#endif
}
+#ifndef USE_DUMMY
+struct irecv_device_event_context {
+ irecv_device_event_cb_t callback;
+ void *user_data;
+};
+
+struct irecv_usb_device_info {
+ struct irecv_device_info device_info;
+ uint32_t location;
+ int alive;
+};
+
+#ifdef WIN32
+struct irecv_win_dev_ctx {
+ PSP_DEVICE_INTERFACE_DETAIL_DATA details;
+ uint32_t location;
+};
+#else
+#ifdef HAVE_IOKIT
+struct irecv_iokit_dev_ctx {
+ io_service_t device;
+ IOUSBDeviceInterface **dev;
+};
+#endif
+#endif
+
+static int _irecv_is_recovery_device(void *device)
+{
+ uint16_t vendor_id = 0;
+ uint16_t product_id = 0;
+#ifdef WIN32
+ const char *path = (const char*)device;
+ unsigned int vendor = 0;
+ unsigned int product = 0;
+ if (sscanf(path, "\\usb#vid_%04x&pid_%04x#", &vendor, &product) != 2) {
+ return 0;
+ }
+ vendor_id = (uint16_t)vendor;
+ product_id = (uint16_t)product;
+#else
+#ifdef HAVE_IOKIT
+ kern_return_t kr;
+ IOUSBDeviceInterface **dev = device;
+ kr = (*dev)->GetDeviceVendor(dev, &vendor_id);
+ kr = (*dev)->GetDeviceProduct(dev, &product_id);
+#else
+ libusb_device *device_ = (libusb_device*)device;
+ struct libusb_device_descriptor devdesc;
+ int libusb_error;
+
+ libusb_error = libusb_get_device_descriptor(device_, &devdesc);
+ if (libusb_error != 0) {
+ debug("%s: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
+ return 0;
+ }
+ vendor_id = devdesc.idVendor;
+ product_id = devdesc.idProduct;
+#endif
+#endif
+
+ if (vendor_id != APPLE_VENDOR_ID) {
+ return 0;
+ }
+
+ switch (product_id) {
+ case IRECV_K_DFU_MODE:
+ case IRECV_K_WTF_MODE:
+ case IRECV_K_RECOVERY_MODE_1:
+ case IRECV_K_RECOVERY_MODE_2:
+ case IRECV_K_RECOVERY_MODE_3:
+ case IRECV_K_RECOVERY_MODE_4:
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static void* _irecv_handle_device_add(void *userdata)
+{
+ struct irecv_client_private client_loc;
+ char serial_str[256];
+ uint32_t location = 0;
+ uint16_t product_id = 0;
+
+ serial_str[0] = '\0';
+#ifdef WIN32
+ struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA details = win_ctx->details;
+ LPSTR result = (LPSTR)details->DevicePath;
+ location = win_ctx->location;
+
+ char *p = result + strlen(result) - 1;
+ while (p-- && p > result) {
+ if (*p == '\\' && (strncmp(p, "\\usb", 4) == 0)) {
+ break;
+ }
+ }
+
+ unsigned int pid = 0;
+ if (!p || (sscanf(p, "\\usb#vid_%*04x&pid_%04x#%s", &pid, serial_str) != 2) || (serial_str[0] == '\0')) {
+ fprintf(stderr, "%s: ERROR: failed to parse DevicePath?!\n", __func__);
+ return NULL;
+ }
+ if (!_irecv_is_recovery_device(p)) {
+ return NULL;
+ }
+
+ p = strchr(serial_str, '#');
+ if (p) {
+ *p = '\0';
+ }
+
+ unsigned int j;
+ for (j = 0; j < strlen(serial_str); j++) {
+ if (serial_str[j] == '_') {
+ serial_str[j] = ' ';
+ } else {
+ serial_str[j] = toupper(serial_str[j]);
+ }
+ }
+ product_id = (uint16_t)pid;
+
+#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;
+ IOUSBDeviceInterface **dev = iokit_ctx->dev;
+
+ if (!device) {
+ fprintf(stderr, "%s: ERROR: no device?!\n", __func__);
+ return NULL;
+ }
+ if (!dev) {
+ fprintf(stderr, "%s: ERROR: no device interface?!\n", __func__);
+ return NULL;
+ }
+
+ (*dev)->GetDeviceProduct(dev, &product_id);
+ if (!product_id) {
+ fprintf(stderr, "%s: ERROR: could not get product id?!\n", __func__);
+ return NULL;
+ }
+ CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
+ if (locationNum) {
+ CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
+ CFRelease(locationNum);
+ }
+ if (!location) {
+ fprintf(stderr, "%s: ERROR: could not get locationID?!\n", __func__);
+ return NULL;
+ }
+ CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
+ if (serialString) {
+ CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
+ CFRelease(serialString);
+ }
+#else /* !HAVE_IOKIT */
+ libusb_device *device = (libusb_device*)userdata;
+ struct libusb_device_descriptor devdesc;
+ struct libusb_device_handle* usb_handle = NULL;
+ int libusb_error;
+
+ libusb_error = libusb_get_device_descriptor(device, &devdesc);
+ if (libusb_error != 0) {
+ fprintf(stderr, "%s: ERROR: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
+ return NULL;
+ }
+ product_id = devdesc.idProduct;
+
+ uint8_t bus = libusb_get_bus_number(device);
+ uint8_t address = libusb_get_device_address(device);
+ location = (bus << 16) | address;
+
+ libusb_error = libusb_open(device, &usb_handle);
+ if (usb_handle == NULL || libusb_error != 0) {
+ fprintf(stderr, "%s: ERROR: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
+ libusb_close(usb_handle);
+ return 0;
+ }
+
+ libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, 255);
+ if (libusb_error < 0) {
+ fprintf(stderr, "%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error));
+ return 0;
+ }
+ libusb_close(usb_handle);
+#endif /* !HAVE_IOKIT */
+#endif /* !WIN32 */
+ memset(&client_loc, '\0', sizeof(client_loc));
+ irecv_load_device_info_from_iboot_string(&client_loc, serial_str);
+ client_loc.mode = product_id;
+
+ struct irecv_usb_device_info *usb_dev_info = (struct irecv_usb_device_info*)malloc(sizeof(struct irecv_usb_device_info));
+ memcpy(&(usb_dev_info->device_info), &(client_loc.device_info), sizeof(struct irecv_device_info));
+ usb_dev_info->location = location;
+ usb_dev_info->alive = 1;
+
+ collection_add(&devices, usb_dev_info);
+
+ irecv_device_event_t dev_event;
+ dev_event.type = IRECV_DEVICE_ADD;
+ dev_event.mode = client_loc.mode;
+ dev_event.device_info = &(usb_dev_info->device_info);
+
+ mutex_lock(&listener_mutex);
+ FOREACH(struct irecv_device_event_context* context, &listeners) {
+ context->callback(&dev_event, context->user_data);
+ } ENDFOREACH
+ mutex_unlock(&listener_mutex);
+
+ return NULL;
+}
+
+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.device_info = &(devinfo->device_info);
+ mutex_lock(&listener_mutex);
+ FOREACH(struct irecv_device_event_context* context, &listeners) {
+ context->callback(&dev_event, context->user_data);
+ } ENDFOREACH
+ mutex_unlock(&listener_mutex);
+ free(devinfo->device_info.srnm);
+ free(devinfo->device_info.imei);
+ devinfo->device_info.srnm = NULL;
+ devinfo->device_info.imei = NULL;
+ devinfo->alive = 0;
+ collection_remove(&devices, devinfo);
+ free(devinfo);
+}
+
+#ifndef WIN32
+#ifdef HAVE_IOKIT
+static void iokit_device_added(void *refcon, io_iterator_t iterator)
+{
+ kern_return_t kr;
+ io_service_t device;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ IOUSBDeviceInterface **dev = NULL;
+ HRESULT result;
+ SInt32 score;
+
+ while ((device = IOIteratorNext(iterator))) {
+ kr = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
+ if ((kIOReturnSuccess != kr) || !plugInInterface) {
+ fprintf(stderr, "%s: ERROR: Unable to create a plug-in (%08x)\n", __func__, kr);
+ kr = IOObjectRelease(device);
+ continue;
+ }
+ result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID *)&dev);
+ (*plugInInterface)->Release(plugInInterface);
+
+ if (result || !dev) {
+ fprintf(stderr, "%s: ERROR: Couldn't create a device interface (%08x)\n", __func__, (int)result);
+ kr = IOObjectRelease(device);
+ continue;
+ }
+
+ if (!_irecv_is_recovery_device(dev)) {
+ (void) (*dev)->Release(dev);
+ kr = IOObjectRelease(device);
+ continue;
+ }
+
+ struct irecv_iokit_dev_ctx idev;
+ idev.device = device;
+ idev.dev = dev;
+ _irecv_handle_device_add(&idev);
+ (void) (*dev)->Release(dev);
+ kr = IOObjectRelease(device);
+ }
+}
+
+static void iokit_device_removed(void *refcon, io_iterator_t iterator)
+{
+ io_service_t device;
+
+ while ((device = IOIteratorNext(iterator))) {
+ uint32_t location = 0;
+ CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
+ if (locationNum) {
+ CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
+ CFRelease(locationNum);
+ }
+ IOObjectRelease(device);
+
+ if (!location) {
+ continue;
+ }
+
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ if (devinfo->location == location) {
+ _irecv_handle_device_remove(devinfo);
+ break;
+ }
+ } ENDFOREACH
+ }
+}
+#else /* !HAVE_IOKIT */
+#ifdef HAVE_LIBUSB_HOTPLUG_API
+static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
+{
+ if (!_irecv_is_recovery_device(device)) {
+ return 0;
+ }
+ if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+ THREAD_T th_device;
+ if (thread_new(&th_device, _irecv_handle_device_add, device) != 0) {
+ fprintf(stderr, "%s: FATAL: failed to create thread to handle device add\n", __func__);
+ return 0;
+ }
+ thread_detach(th_device);
+ } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+ uint8_t bus = libusb_get_bus_number(device);
+ uint8_t address = libusb_get_device_address(device);
+ uint32_t location = (bus << 16) | address;
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ if (devinfo->location == location) {
+ _irecv_handle_device_remove(devinfo);
+ break;
+ }
+ } ENDFOREACH
+ }
+
+ return 0;
+}
+#endif /* HAVE_LIBUSB_HOTPLUG_API */
+#endif /* !HAVE_IOKIT */
+#endif /* !WIN32 */
+
+static void *_irecv_event_handler(void* unused)
+{
+#ifdef WIN32
+ const GUID *guids[] = { &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
+ int running = 1;
+ do {
+ SP_DEVICE_INTERFACE_DATA currentInterface;
+ HDEVINFO usbDevices;
+ DWORD i;
+ int k;
+ for (k = 0; guids[k]; k++) {
+ usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if (!usbDevices) {
+ fprintf(stderr, "%s: ERROR: SetupDiGetClassDevs failed\n", __func__);
+ return NULL;
+ }
+
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ devinfo->alive = 0;
+ } ENDFOREACH
+
+ memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
+ currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
+ DWORD requiredSize = 0;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA details;
+ SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
+ details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize);
+ details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+ SP_DEVINFO_DATA devinfodata;
+ devinfodata.cbSize = sizeof(SP_DEVINFO_DATA);
+ if(!SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, details, requiredSize, NULL, &devinfodata)) {
+ free(details);
+ continue;
+ }
+
+ DWORD sz = REG_SZ;
+ char driver[256];
+ driver[0] = '\0';
+ if (!SetupDiGetDeviceRegistryProperty(usbDevices, &devinfodata, SPDRP_DRIVER, &sz, (PBYTE)driver, sizeof(driver), NULL)) {
+ fprintf(stderr, "%s: ERROR: Failed to get driver key\n", __func__);
+ free(details);
+ continue;
+ }
+ char *p = strrchr(driver, '\\');
+ if (!p) {
+ fprintf(stderr, "%s: ERROR: Failed to parse device location\n", __func__);
+ free(details);
+ continue;
+ }
+ uint32_t location = strtoul(p+1, NULL, 10);
+ int found = 0;
+
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ if (devinfo->location == location) {
+ devinfo->alive = 1;
+ found = 1;
+ break;
+ }
+ } ENDFOREACH
+
+ if (!found) {
+ struct irecv_win_dev_ctx win_ctx;
+ win_ctx.details = details;
+ win_ctx.location = location;
+ _irecv_handle_device_add(&win_ctx);
+ }
+ free(details);
+ }
+ SetupDiDestroyDeviceInfoList(usbDevices);
+ }
+
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ if (!devinfo->alive) {
+ _irecv_handle_device_remove(devinfo);
+ }
+ } ENDFOREACH
+
+ mutex_lock(&listener_mutex);
+ if (collection_count(&listeners) == 0) {
+ running = 0;
+ }
+ mutex_unlock(&listener_mutex);
+
+ Sleep(500);
+ } while (running);
+
+#else /* !WIN32 */
+#ifdef HAVE_IOKIT
+ kern_return_t kr;
+
+ IONotificationPortRef notifyPort = IONotificationPortCreate(kIOMasterPortDefault);
+ CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notifyPort);
+ iokit_runloop = CFRunLoopGetCurrent();
+ CFRunLoopAddSource(iokit_runloop, runLoopSource, kCFRunLoopDefaultMode);
+
+ uint16_t pids[7] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, 0 };
+ int i = 0;
+ while (pids[i] > 0) {
+ CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+ iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
+ iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pids[i]);
+
+ matchingDict = (CFMutableDictionaryRef)CFRetain(matchingDict);
+
+ io_iterator_t devAddedIter;
+ kr = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, iokit_device_added, NULL, &devAddedIter);
+ iokit_device_added(NULL, devAddedIter);
+
+ io_iterator_t devRemovedIter;
+ kr = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, iokit_device_removed, NULL, &devRemovedIter);
+ iokit_device_removed(NULL, devRemovedIter);
+
+ i++;
+ }
+
+ CFRunLoopRun();
+
+#else /* !HAVE_IOKIT */
+#ifdef HAVE_LIBUSB_HOTPLUG_API
+ static libusb_hotplug_callback_handle usb_hotplug_cb_handle;
+ libusb_hotplug_register_callback(irecv_hotplug_ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_ENUMERATE, APPLE_VENDOR_ID, LIBUSB_HOTPLUG_MATCH_ANY, 0, _irecv_usb_hotplug_cb, NULL, &usb_hotplug_cb_handle);
+ int running = 1;
+ do {
+ struct timeval tv;
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(irecv_hotplug_ctx, &tv);
+
+ mutex_lock(&listener_mutex);
+ if (collection_count(&listeners) == 0) {
+ running = 0;
+ }
+ mutex_unlock(&listener_mutex);
+
+ usleep(100000);
+ } while (running);
+ libusb_hotplug_deregister_callback(irecv_hotplug_ctx, usb_hotplug_cb_handle);
+#else /* !HAVE_LIBUSB_HOTPLUG_API */
+ int i, cnt;
+ libusb_device **devs;
+ int running = 1;
+
+ do {
+ cnt = libusb_get_device_list(irecv_hotplug_ctx, &devs);
+ if (cnt < 0) {
+ fprintf(stderr, "%s: FATAL: Failed to get device list: %s\n", __func__, libusb_error_name(cnt));
+ return NULL;
+ }
+
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ devinfo->alive = 0;
+ } ENDFOREACH
+
+ for (i = 0; i < cnt; i++) {
+ libusb_device *dev = devs[i];
+ if (!_irecv_is_recovery_device(dev)) {
+ continue;
+ }
+ uint8_t bus = libusb_get_bus_number(dev);
+ uint8_t address = libusb_get_device_address(dev);
+ uint32_t location = (bus << 16) | address;
+ int found = 0;
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ if (devinfo->location == location) {
+ devinfo->alive = 1;
+ found = 1;
+ break;
+ }
+ } ENDFOREACH
+ if (!found) {
+ _irecv_handle_device_add(dev);
+ }
+ }
+
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ if (!devinfo->alive) {
+ _irecv_handle_device_remove(devinfo);
+ }
+ } ENDFOREACH
+
+ libusb_free_device_list(devs, 1);
+
+ mutex_lock(&listener_mutex);
+ if (collection_count(&listeners) == 0) {
+ running = 0;
+ }
+ mutex_unlock(&listener_mutex);
+ if (!running)
+ break;
+ usleep(500000);
+ } while (running);
+#endif /* !HAVE_LIBUSB_HOTPLUG_API */
+#endif /* !HAVE_IOKIT */
+#endif /* !WIN32 */
+ return NULL;
+}
+#endif /* !USE_DUMMY */
+
+IRECV_API irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data)
+{
+#ifdef USE_DUMMY
+ return IRECV_E_UNSUPPORTED;
+#else
+ if (!context || !callback)
+ return IRECV_E_INVALID_INPUT;
+
+ struct irecv_device_event_context* _context = malloc(sizeof(struct irecv_device_event_context));
+ if (!_context) {
+ return IRECV_E_OUT_OF_MEMORY;
+ }
+
+ _context->callback = callback;
+ _context->user_data = user_data;
+
+ mutex_lock(&listener_mutex);
+ collection_add(&listeners, _context);
+ mutex_unlock(&listener_mutex);
+
+ if (th_event_handler == THREAD_T_NULL || !thread_alive(th_event_handler)) {
+#ifndef WIN32
+#ifndef HAVE_IOKIT
+ libusb_init(&irecv_hotplug_ctx);
+#endif
+#endif
+ collection_init(&devices);
+ mutex_init(&device_mutex);
+ thread_new(&th_event_handler, _irecv_event_handler, NULL);
+ }
+
+ *context = _context;
+
+ return IRECV_E_SUCCESS;
+#endif
+}
+
+IRECV_API irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context)
+{
+#ifdef USE_DUMMY
+ return IRECV_E_UNSUPPORTED;
+#else
+ if (!context)
+ return IRECV_E_INVALID_INPUT;
+
+ mutex_lock(&listener_mutex);
+ collection_remove(&listeners, context);
+ int num = collection_count(&listeners);
+ mutex_unlock(&listener_mutex);
+
+ if (num == 0) {
+#ifdef HAVE_IOKIT
+ CFRunLoopStop(iokit_runloop);
+#endif
+ thread_join(th_event_handler);
+ thread_free(th_event_handler);
+ th_event_handler = THREAD_T_NULL;
+ mutex_lock(&device_mutex);
+ FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
+ free(devinfo->device_info.srnm);
+ free(devinfo->device_info.imei);
+ devinfo->device_info.srnm = NULL;
+ devinfo->device_info.imei = NULL;
+ free(devinfo);
+ } ENDFOREACH
+ collection_free(&devices);
+ mutex_unlock(&device_mutex);
+ mutex_destroy(&device_mutex);
+#ifndef WIN32
+#ifndef HAVE_IOKIT
+ libusb_exit(irecv_hotplug_ctx);
+ irecv_hotplug_ctx = NULL;
+#endif
+#endif
+ }
+
+ free(context);
+
+ return IRECV_E_SUCCESS;
+#endif
+}
+
IRECV_API irecv_error_t irecv_close(irecv_client_t client) {
#ifdef USE_DUMMY
return IRECV_E_UNSUPPORTED;
@@ -2432,6 +3119,11 @@ IRECV_API irecv_client_t irecv_reconnect(irecv_client_t client, int initial_paus
irecv_error_t error = 0;
irecv_client_t new_client = NULL;
irecv_event_cb_t progress_callback = client->progress_callback;
+ irecv_event_cb_t received_callback = client->received_callback;
+ irecv_event_cb_t connected_callback = client->connected_callback;
+ irecv_event_cb_t precommand_callback = client->precommand_callback;
+ irecv_event_cb_t postcommand_callback = client->postcommand_callback;
+ irecv_event_cb_t disconnected_callback = client->disconnected_callback;
unsigned long long ecid = client->device_info.ecid;
@@ -2450,6 +3142,20 @@ IRECV_API irecv_client_t irecv_reconnect(irecv_client_t client, int initial_paus
}
new_client->progress_callback = progress_callback;
+ new_client->received_callback = received_callback;
+ new_client->connected_callback = connected_callback;
+ new_client->precommand_callback = precommand_callback;
+ new_client->postcommand_callback = postcommand_callback;
+ new_client->disconnected_callback = disconnected_callback;
+
+ if (new_client->connected_callback != NULL) {
+ irecv_event_t event;
+ event.size = 0;
+ event.data = NULL;
+ event.progress = 0;
+ event.type = IRECV_CONNECTED;
+ new_client->connected_callback(new_client, &event);
+ }
return new_client;
#endif
diff --git a/tools/irecovery.c b/tools/irecovery.c
index 280be7a..c901633 100644
--- a/tools/irecovery.c
+++ b/tools/irecovery.c
@@ -2,7 +2,7 @@
* irecovery.c
* Software frontend for iBoot/iBSS communication with iOS devices
*
- * Copyright (c) 2012-2017 Nikias Bassen
+ * Copyright (c) 2012-2019 Nikias Bassen <nikias@gmx.li>
* Copyright (c) 2012-2015 Martin Szulecki <martin.szulecki@libimobiledevice.org>
* Copyright (c) 2010-2011 Chronic-Dev Team
* Copyright (c) 2010-2011 Joshua Hill
@@ -454,7 +454,6 @@ int main(int argc, char* argv[]) {
if (verbose)
irecv_set_debug_level(verbose);
- irecv_init();
irecv_client_t client = NULL;
for (i = 0; i <= 5; i++) {
debug("Attempting to connect... \n");
@@ -565,7 +564,6 @@ int main(int argc, char* argv[]) {
}
irecv_close(client);
- irecv_exit();
return 0;
}