summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar tihmstar2023-12-14 01:13:00 +0100
committerGravatar Nikias Bassen2025-09-06 19:55:49 +0200
commit52a81e111664cb267bd5b646ece9cb90c1a6fa94 (patch)
treea7c3e1d8fcded4c3fa700972c28875394dea2c79 /src
parent3fa36c5a7a745fd334bed8a3d5e432c626677910 (diff)
downloadlibirecovery-52a81e111664cb267bd5b646ece9cb90c1a6fa94.tar.gz
libirecovery-52a81e111664cb267bd5b646ece9cb90c1a6fa94.tar.bz2
Add iOS 1 support
Diffstat (limited to 'src')
-rw-r--r--src/libirecovery.c200
1 files changed, 197 insertions, 3 deletions
diff --git a/src/libirecovery.c b/src/libirecovery.c
index f544fbd..dd07110 100644
--- a/src/libirecovery.c
+++ b/src/libirecovery.c
@@ -628,6 +628,24 @@ typedef struct {
#pragma pack()
#endif
+#pragma pack(1)
+typedef struct {
+ uint16_t cmdcode;
+#define MSG_VERSION_QUERY 0x000
+#define MSG_ECHO 0x801
+#define MSG_DUMP_BUFFER 0x802
+#define MSG_SEND_COMMAND 0x803
+#define MSG_READ_FILE 0x804
+#define MSG_SEND_FILE 0x805
+#define MSG_CRC 0x807
+#define MSG_ACK 0x808
+#define MSG_REJECT 0x809
+ uint16_t magic; // always 0x1234
+ uint32_t size;
+ uint32_t loadaddr; // 0x0 for commands, 0x09000000 for files
+} legacyCMD;
+#pragma pack()
+
static THREAD_T th_event_handler = THREAD_T_NULL;
struct collection listeners;
static mutex_t listener_mutex;
@@ -777,6 +795,9 @@ static void irecv_load_device_info_from_iboot_string(irecv_client_t client, cons
ptr = strstr(iboot_string, "CPID:");
if (ptr != NULL) {
sscanf(ptr, "CPID:%x", &client->device_info.cpid);
+ } else {
+ // early iOS 1 doesn't identify itself
+ client->device_info.cpid = 0x8900;
}
ptr = strstr(iboot_string, "CPRV:");
@@ -1495,6 +1516,74 @@ static int iokit_usb_bulk_transfer(irecv_client_t client,
return IRECV_E_USB_INTERFACE;
}
+
+static int iokit_usb_interrupt_transfer(irecv_client_t client,
+ unsigned char endpoint,
+ unsigned char *data,
+ int length,
+ int *transferred,
+ unsigned int timeout)
+{
+ IOReturn result;
+ IOUSBInterfaceInterface300 **intf = client->usbInterface;
+ UInt32 size = length;
+ UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
+ UInt8 numEndpoints;
+
+ if (!intf) return IRECV_E_USB_INTERFACE;
+
+ result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
+
+ if (result != kIOReturnSuccess)
+ return IRECV_E_USB_INTERFACE;
+
+ for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
+ UInt8 direction = 0;
+ UInt8 number = 0;
+ UInt8 transferType = 0;
+ UInt16 maxPacketSize = 0;
+ UInt8 interval = 0;
+
+ result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
+ if (result != kIOReturnSuccess)
+ continue;
+
+ if (direction == 3)
+ direction = isUSBIn;
+
+ if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
+ continue;
+
+ // Just because
+ result = (*intf)->GetPipeStatus(intf, pipeRef);
+ switch (result) {
+ case kIOReturnSuccess: break;
+ case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
+ case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
+ default: return IRECV_E_USB_STATUS;
+ }
+
+ // Do the transfer
+ if (isUSBIn) {
+ result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
+ if (result != kIOReturnSuccess)
+ return IRECV_E_PIPE;
+ *transferred = size;
+
+ return IRECV_E_SUCCESS;
+ }
+ else {
+ result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
+ if (result != kIOReturnSuccess)
+ return IRECV_E_PIPE;
+ *transferred = size;
+
+ return IRECV_E_SUCCESS;
+ }
+ }
+
+ return IRECV_E_USB_INTERFACE;
+}
#endif
#endif
@@ -1532,6 +1621,36 @@ int irecv_usb_bulk_transfer(irecv_client_t client,
#endif
}
+IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client,
+ unsigned char endpoint,
+ unsigned char *data,
+ int length,
+ int *transferred,
+ unsigned int timeout)
+{
+#ifdef USE_DUMMY
+ return IRECV_E_UNSUPPORTED;
+#else
+ int ret;
+
+#ifndef _WIN32
+#ifdef HAVE_IOKIT
+ return iokit_usb_interrupt_transfer(client, endpoint, data, length, transferred, timeout);
+#else
+ ret = libusb_interrupt_transfer(client->handle, endpoint, data, length, transferred, timeout);
+ if (ret < 0) {
+ libusb_clear_halt(client->handle, endpoint);
+ }
+#endif
+#else
+ // win32
+ return IRECV_E_UNSUPPORTED;
+#endif
+
+ return ret;
+#endif
+}
+
#ifndef USE_DUMMY
#ifdef HAVE_IOKIT
static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service)
@@ -3112,6 +3231,35 @@ static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* c
return IRECV_E_INVALID_INPUT;
}
+ if (length > 0 && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
+ int bytes = 0;
+ irecv_error_t error = 0;
+#ifdef DEBUG
+ uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
+ if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
+ if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
+#endif
+ char cmdstr[0x100] = {};
+ if (length & 0xf) {
+ length &= ~0xf;
+ length += 0x10;
+ }
+ snprintf(cmdstr, sizeof(cmdstr), "%s\n",command);
+ legacyCMD cmd = {
+ MSG_SEND_COMMAND,
+ 0x1234, //magic
+ (uint32_t)length, //zero terminated?
+ 0x0
+ };
+ if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
+ if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x02, (unsigned char*)cmdstr, length, &bytes, USB_TIMEOUT))) return error;
+ sleep(1); //go easy on this old device
+ return IRECV_E_SUCCESS;
+ }
+
if (length > 0) {
irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT);
}
@@ -3323,6 +3471,18 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
irecv_error_t error = 0;
int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
+ int legacy_recovery_mode = 0;
+
+ if (recovery_mode && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
+#ifdef DEBUG
+ uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
+ int bytes = 0;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
+ if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
+#endif
+ legacy_recovery_mode = 1;
+ }
if (check_context(client) != IRECV_E_SUCCESS)
return IRECV_E_NO_DEVICE;
@@ -3331,6 +3491,7 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10};
int dfu_crc = 1;
int packet_size = recovery_mode ? 0x8000 : 0x800;
+ if (legacy_recovery_mode) packet_size = 0x200;
if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) {
packet_size = 0x40;
dfu_crc = 0;
@@ -3345,7 +3506,24 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
}
/* initiate transfer */
- if (recovery_mode) {
+ if (legacy_recovery_mode) {
+ int bytes = 0;
+ uint32_t loadaddr0x8900 = 0x09000000;
+ const char *ios1_overwrite_loadaddr = getenv("LIBIRECOVERY_IOS1_OVERWRITE_LOADADDR");
+ if (ios1_overwrite_loadaddr) {
+ sscanf(ios1_overwrite_loadaddr, "0x%x",&loadaddr0x8900);
+ debug("Overwriting loadaddr requested by env var. uploading to 0x%08x\n",loadaddr0x8900);
+ }
+ legacyCMD cmd = {
+ MSG_SEND_FILE,
+ 0x1234, //magic
+ (uint32_t)length,
+ loadaddr0x8900
+ };
+ if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
+ if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
+ if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
+ } else if (recovery_mode) {
error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT);
} else {
uint8_t state = 0;
@@ -3382,10 +3560,14 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
for (i = 0; i < packets; i++) {
int size = (i + 1) < packets ? packet_size : last;
- /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */
- if (recovery_mode) {
+ if (legacy_recovery_mode) {
+ // Use interrupt transfer for legacy devices
+ error = irecv_usb_interrupt_transfer(client, 0x05, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
+ } else if (recovery_mode) {
+ // Use bulk transfer for recovery mode
error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
} else {
+ // Use control transfer for DFU and WTF mode
if (dfu_crc) {
int j;
for (j = 0; j < size; j++) {
@@ -3493,6 +3675,13 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
irecv_reset(client);
}
+ if (legacy_recovery_mode) {
+ irecv_reconnect(client, 0);
+ char cmdstr[0x100] = {};
+ snprintf(cmdstr, sizeof(cmdstr), "setenv filesize %d", (int)length);
+ irecv_send_command(client, cmdstr);
+ }
+
return IRECV_E_SUCCESS;
#endif
}
@@ -3548,6 +3737,11 @@ irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** v
return IRECV_E_INVALID_INPUT;
}
+ if (client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
+ debug("iOS 1 doesn't support getenv\n");
+ return IRECV_E_UNSUPPORTED;
+ }
+
memset(command, '\0', sizeof(command));
snprintf(command, sizeof(command)-1, "getenv %s", variable);
irecv_error_t error = irecv_send_command_raw(client, command, 0);