diff options
Diffstat (limited to 'libirecovery.c')
| -rw-r--r-- | libirecovery.c | 162 |
1 files changed, 122 insertions, 40 deletions
diff --git a/libirecovery.c b/libirecovery.c index f8f33ca..0c166ff 100644 --- a/libirecovery.c +++ b/libirecovery.c | |||
| @@ -25,10 +25,14 @@ | |||
| 25 | 25 | ||
| 26 | #ifndef WIN32 | 26 | #ifndef WIN32 |
| 27 | #include <libusb-1.0/libusb.h> | 27 | #include <libusb-1.0/libusb.h> |
| 28 | #define _FMT_qX "%qX" | ||
| 29 | #define _FMT_016llx "%016llx" | ||
| 28 | #else | 30 | #else |
| 29 | #define WIN32_LEAN_AND_MEAN | 31 | #define WIN32_LEAN_AND_MEAN |
| 30 | #include <windows.h> | 32 | #include <windows.h> |
| 31 | #include <setupapi.h> | 33 | #include <setupapi.h> |
| 34 | #define _FMT_qX "%I64X" | ||
| 35 | #define _FMT_016llx "%016I64x" | ||
| 32 | #endif | 36 | #endif |
| 33 | 37 | ||
| 34 | #include "libirecovery.h" | 38 | #include "libirecovery.h" |
| @@ -130,86 +134,157 @@ typedef struct usb_control_request { | |||
| 130 | char data[]; | 134 | char data[]; |
| 131 | } usb_control_request; | 135 | } usb_control_request; |
| 132 | 136 | ||
| 137 | int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size); | ||
| 138 | |||
| 133 | irecv_error_t mobiledevice_openpipes(irecv_client_t client); | 139 | irecv_error_t mobiledevice_openpipes(irecv_client_t client); |
| 134 | void mobiledevice_closepipes(irecv_client_t client); | 140 | void mobiledevice_closepipes(irecv_client_t client); |
| 135 | 141 | ||
| 136 | irecv_error_t mobiledevice_connect(irecv_client_t* client) { | 142 | irecv_error_t mobiledevice_connect(irecv_client_t* client, unsigned long long ecid) { |
| 137 | irecv_error_t ret; | 143 | irecv_error_t ret; |
| 138 | 144 | int found = 0; | |
| 139 | SP_DEVICE_INTERFACE_DATA currentInterface; | 145 | SP_DEVICE_INTERFACE_DATA currentInterface; |
| 140 | HDEVINFO usbDevices; | 146 | HDEVINFO usbDevices; |
| 141 | DWORD i; | 147 | DWORD i; |
| 142 | LPSTR path; | ||
| 143 | irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client)); | 148 | irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client)); |
| 144 | memset(_client, 0, sizeof(struct irecv_client)); | 149 | memset(_client, 0, sizeof(struct irecv_client)); |
| 145 | 150 | ||
| 146 | // Get DFU paths | 151 | // Get DFU paths |
| 147 | usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DFU, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); | 152 | usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DFU, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); |
| 148 | if(!usbDevices) { | 153 | memset(¤tInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA)); |
| 149 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 150 | } | ||
| 151 | currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | 154 | currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
| 152 | for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_DFU, i, ¤tInterface); i++) { | 155 | for(i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_DFU, i, ¤tInterface); i++) { |
| 156 | if (_client->DfuPath) { | ||
| 157 | free(_client->DfuPath); | ||
| 158 | _client->DfuPath = NULL; | ||
| 159 | } | ||
| 160 | _client->handle = NULL; | ||
| 153 | DWORD requiredSize = 0; | 161 | DWORD requiredSize = 0; |
| 154 | PSP_DEVICE_INTERFACE_DETAIL_DATA details; | 162 | PSP_DEVICE_INTERFACE_DETAIL_DATA details; |
| 155 | SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); | 163 | SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); |
| 156 | details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); | 164 | details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); |
| 157 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | 165 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); |
| 158 | if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { | 166 | if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { |
| 159 | irecv_close(_client); | ||
| 160 | free(details); | 167 | free(details); |
| 161 | SetupDiDestroyDeviceInfoList(usbDevices); | 168 | continue; |
| 162 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 163 | } else { | 169 | } else { |
| 164 | LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); | 170 | LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); |
| 165 | memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); | 171 | memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); |
| 166 | free(details); | 172 | free(details); |
| 167 | path = (LPSTR) malloc(requiredSize - sizeof(DWORD)); | 173 | |
| 168 | memcpy((void*) path, (void*) result, requiredSize - sizeof(DWORD)); | ||
| 169 | TCHAR* pathEnd = strstr(path, "#{"); | ||
| 170 | *pathEnd = '\0'; | ||
| 171 | _client->DfuPath = result; | 174 | _client->DfuPath = result; |
| 175 | if (mobiledevice_openpipes(_client) != IRECV_E_SUCCESS) { | ||
| 176 | mobiledevice_closepipes(_client); | ||
| 177 | continue; | ||
| 178 | } | ||
| 179 | |||
| 180 | if ((ecid != 0) && (_client->mode == kWTFMode)) { | ||
| 181 | // we can't get ecid in WTF mode | ||
| 182 | mobiledevice_closepipes(_client); | ||
| 183 | continue; | ||
| 184 | } | ||
| 185 | |||
| 186 | _client->serial[0] = '\0'; | ||
| 187 | irecv_get_string_descriptor_ascii(_client, 3, (unsigned char*)_client->serial, 255); | ||
| 188 | if (_client->serial[0] == '\0') { | ||
| 189 | mobiledevice_closepipes(_client); | ||
| 190 | continue; | ||
| 191 | } | ||
| 192 | |||
| 193 | if (ecid != 0) { | ||
| 194 | char* ecid_string = strstr(_client->serial, "ECID:"); | ||
| 195 | if (ecid_string == NULL) { | ||
| 196 | debug("%s: could not get ECID for device\n", __func__); | ||
| 197 | mobiledevice_closepipes(_client); | ||
| 198 | continue; | ||
| 199 | } | ||
| 200 | |||
| 201 | unsigned long long this_ecid = 0; | ||
| 202 | sscanf(ecid_string, "ECID:" _FMT_qX, (unsigned long long*)&this_ecid); | ||
| 203 | if (this_ecid != ecid) { | ||
| 204 | mobiledevice_closepipes(_client); | ||
| 205 | continue; | ||
| 206 | } | ||
| 207 | debug("found device with ECID " _FMT_016llx "\n", (unsigned long long)ecid); | ||
| 208 | } | ||
| 209 | found = 1; | ||
| 172 | break; | 210 | break; |
| 173 | } | 211 | } |
| 174 | } | 212 | } |
| 175 | SetupDiDestroyDeviceInfoList(usbDevices); | 213 | SetupDiDestroyDeviceInfoList(usbDevices); |
| 214 | |||
| 215 | if (found) { | ||
| 216 | *client = _client; | ||
| 217 | return IRECV_E_SUCCESS; | ||
| 218 | } | ||
| 219 | |||
| 176 | // Get iBoot path | 220 | // Get iBoot path |
| 177 | usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IBOOT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); | 221 | usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IBOOT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); |
| 178 | if(!usbDevices) { | 222 | memset(¤tInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA)); |
| 179 | irecv_close(_client); | ||
| 180 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 181 | } | ||
| 182 | currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | 223 | currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
| 183 | for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_IBOOT, i, ¤tInterface); i++) { | 224 | for(i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_IBOOT, i, ¤tInterface); i++) { |
| 225 | if (_client->iBootPath) { | ||
| 226 | free(_client->iBootPath); | ||
| 227 | _client->iBootPath = NULL; | ||
| 228 | } | ||
| 229 | _client->handle = NULL; | ||
| 184 | DWORD requiredSize = 0; | 230 | DWORD requiredSize = 0; |
| 185 | PSP_DEVICE_INTERFACE_DETAIL_DATA details; | 231 | PSP_DEVICE_INTERFACE_DETAIL_DATA details; |
| 186 | SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); | 232 | SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); |
| 187 | details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); | 233 | details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); |
| 188 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | 234 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); |
| 189 | if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { | 235 | if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { |
| 190 | irecv_close(_client); | ||
| 191 | free(details); | 236 | free(details); |
| 192 | SetupDiDestroyDeviceInfoList(usbDevices); | 237 | continue; |
| 193 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 194 | } else { | 238 | } else { |
| 195 | LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); | 239 | LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); |
| 196 | memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); | 240 | memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); |
| 197 | free(details); | 241 | free(details); |
| 198 | 242 | ||
| 199 | if(strstr(result, path) == NULL) { | 243 | _client->iBootPath = result; |
| 200 | free(result); | 244 | if (mobiledevice_openpipes(_client) != IRECV_E_SUCCESS) { |
| 245 | mobiledevice_closepipes(_client); | ||
| 201 | continue; | 246 | continue; |
| 202 | } | 247 | } |
| 203 | 248 | ||
| 204 | _client->iBootPath = result; | 249 | if ((ecid != 0) && (_client->mode == kWTFMode)) { |
| 250 | // we can't get ecid in WTF mode | ||
| 251 | mobiledevice_closepipes(_client); | ||
| 252 | continue; | ||
| 253 | } | ||
| 254 | |||
| 255 | _client->serial[0] = '\0'; | ||
| 256 | irecv_get_string_descriptor_ascii(_client, 3, (unsigned char*)_client->serial, 255); | ||
| 257 | if (_client->serial[0] == '\0') { | ||
| 258 | mobiledevice_closepipes(_client); | ||
| 259 | continue; | ||
| 260 | } | ||
| 261 | |||
| 262 | if (ecid != 0) { | ||
| 263 | char* ecid_string = strstr(_client->serial, "ECID:"); | ||
| 264 | if (ecid_string == NULL) { | ||
| 265 | debug("%s: could not get ECID for device\n", __func__); | ||
| 266 | mobiledevice_closepipes(_client); | ||
| 267 | continue; | ||
| 268 | } | ||
| 269 | |||
| 270 | unsigned long long this_ecid = 0; | ||
| 271 | sscanf(ecid_string, "ECID:" _FMT_qX, (unsigned long long*)&this_ecid); | ||
| 272 | if (this_ecid != ecid) { | ||
| 273 | mobiledevice_closepipes(_client); | ||
| 274 | continue; | ||
| 275 | } | ||
| 276 | debug("found device with ECID " _FMT_016llx" \n", (unsigned long long)ecid); | ||
| 277 | } | ||
| 278 | found = 1; | ||
| 205 | break; | 279 | break; |
| 206 | } | 280 | } |
| 207 | } | 281 | } |
| 208 | SetupDiDestroyDeviceInfoList(usbDevices); | 282 | SetupDiDestroyDeviceInfoList(usbDevices); |
| 209 | free(path); | 283 | |
| 210 | 284 | if (!found) { | |
| 211 | ret = mobiledevice_openpipes(_client); | 285 | irecv_close(_client); |
| 212 | if (ret != IRECV_E_SUCCESS) return ret; | 286 | return IRECV_E_UNABLE_TO_CONNECT; |
| 287 | } | ||
| 213 | 288 | ||
| 214 | *client = _client; | 289 | *client = _client; |
| 215 | return IRECV_E_SUCCESS; | 290 | return IRECV_E_SUCCESS; |
| @@ -225,14 +300,24 @@ irecv_error_t mobiledevice_openpipes(irecv_client_t client) { | |||
| 225 | return IRECV_E_UNABLE_TO_CONNECT; | 300 | return IRECV_E_UNABLE_TO_CONNECT; |
| 226 | } | 301 | } |
| 227 | 302 | ||
| 303 | client->mode = 0; | ||
| 228 | if (client->iBootPath == NULL) { | 304 | if (client->iBootPath == NULL) { |
| 229 | client->mode = kDfuMode; | 305 | if (strncmp(client->DfuPath, "\\\\?\\usb#vid_05ac&pid_", 21) == 0) { |
| 306 | sscanf(client->DfuPath+21, "%x#", &client->mode); | ||
| 307 | } | ||
| 230 | client->handle = client->hDFU; | 308 | client->handle = client->hDFU; |
| 231 | } else { | 309 | } else { |
| 232 | client->mode = kRecoveryMode2; | 310 | if (strncmp(client->iBootPath, "\\\\?\\usb#vid_05ac&pid_", 21) == 0) { |
| 311 | sscanf(client->iBootPath+21, "%x#", &client->mode); | ||
| 312 | } | ||
| 233 | client->handle = client->hIB; | 313 | client->handle = client->hIB; |
| 234 | } | 314 | } |
| 235 | 315 | ||
| 316 | if (client->mode == 0) { | ||
| 317 | irecv_close(client); | ||
| 318 | return IRECV_E_UNABLE_TO_CONNECT; | ||
| 319 | } | ||
| 320 | |||
| 236 | return IRECV_E_SUCCESS; | 321 | return IRECV_E_SUCCESS; |
| 237 | } | 322 | } |
| 238 | 323 | ||
| @@ -346,7 +431,7 @@ int irecv_bulk_transfer(irecv_client_t client, | |||
| 346 | } else { | 431 | } else { |
| 347 | ret = 0; | 432 | ret = 0; |
| 348 | } | 433 | } |
| 349 | ret==0?-1:0; | 434 | ret = (ret==0) ? -1 : 0; |
| 350 | #endif | 435 | #endif |
| 351 | 436 | ||
| 352 | return ret; | 437 | return ret; |
| @@ -455,12 +540,12 @@ irecv_error_t irecv_open(irecv_client_t* pclient, unsigned long long ecid) { | |||
| 455 | } | 540 | } |
| 456 | 541 | ||
| 457 | unsigned long long this_ecid = 0; | 542 | unsigned long long this_ecid = 0; |
| 458 | sscanf(ecid_string, "ECID:%qX", (unsigned long long*)&this_ecid); | 543 | sscanf(ecid_string, "ECID:" _FMT_qX, (unsigned long long*)&this_ecid); |
| 459 | if (this_ecid != ecid) { | 544 | if (this_ecid != ecid) { |
| 460 | irecv_close(client); | 545 | irecv_close(client); |
| 461 | continue; | 546 | continue; |
| 462 | } | 547 | } |
| 463 | debug("found device with ECID %016llx\n", (unsigned long long)ecid); | 548 | debug("found device with ECID " _FMT_016llx "\n", (unsigned long long)ecid); |
| 464 | } | 549 | } |
| 465 | 550 | ||
| 466 | error = irecv_set_configuration(client, 1); | 551 | error = irecv_set_configuration(client, 1); |
| @@ -489,10 +574,7 @@ irecv_error_t irecv_open(irecv_client_t* pclient, unsigned long long ecid) { | |||
| 489 | 574 | ||
| 490 | return IRECV_E_UNABLE_TO_CONNECT; | 575 | return IRECV_E_UNABLE_TO_CONNECT; |
| 491 | #else | 576 | #else |
| 492 | int ret = mobiledevice_connect(pclient); | 577 | int ret = mobiledevice_connect(pclient, ecid); |
| 493 | if (ret == IRECV_E_SUCCESS) { | ||
| 494 | irecv_get_string_descriptor_ascii(*pclient, 3, (unsigned char*) (*pclient)->serial, 255); | ||
| 495 | } | ||
| 496 | return ret; | 578 | return ret; |
| 497 | #endif | 579 | #endif |
| 498 | } | 580 | } |
| @@ -1015,7 +1097,7 @@ irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) { | |||
| 1015 | *ecid = 0; | 1097 | *ecid = 0; |
| 1016 | return IRECV_E_UNKNOWN_ERROR; | 1098 | return IRECV_E_UNKNOWN_ERROR; |
| 1017 | } | 1099 | } |
| 1018 | sscanf(ecid_string, "ECID:%qX", ecid); | 1100 | sscanf(ecid_string, "ECID:" _FMT_qX, ecid); |
| 1019 | 1101 | ||
| 1020 | return IRECV_E_SUCCESS; | 1102 | return IRECV_E_SUCCESS; |
| 1021 | } | 1103 | } |
