From 38befc71844a6108a78e1fe5a76946450d49ca76 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Wed, 4 Sep 2019 23:56:37 +0200 Subject: Add device add/remove event subscription API --- configure.ac | 96 +++---- include/libirecovery.h | 23 +- src/Makefile.am | 18 +- src/libirecovery.c | 764 +++++++++++++++++++++++++++++++++++++++++++++++-- tools/irecovery.c | 4 +- 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 * Copyright (c) 2012-2013 Martin Szulecki - * Copyright (c) 2012-2013 Nikias Bassen * Copyright (c) 2010 Chronic-Dev Team * Copyright (c) 2010 Joshua Hill * @@ -27,7 +27,7 @@ extern "C" { #include -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 * Copyright (c) 2012-2015 Martin Szulecki * Copyright (c) 2010 Chronic-Dev Team * Copyright (c) 2010 Joshua Hill @@ -35,6 +35,10 @@ #ifndef WIN32 #ifndef HAVE_IOKIT #include +#include +#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 #include @@ -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(¤tInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA)); + currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, ¤tInterface); i++) { + DWORD requiredSize = 0; + PSP_DEVICE_INTERFACE_DETAIL_DATA details; + SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, 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, ¤tInterface, 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 * Copyright (c) 2012-2015 Martin Szulecki * 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; } -- cgit v1.1-32-gdbae