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