diff options
| author | 2025-11-27 20:53:30 +0000 | |
|---|---|---|
| committer | 2025-11-27 21:16:37 +0000 | |
| commit | e7aadfe47394eaf81f6e76e49be8b300b5f23763 (patch) | |
| tree | 383f19a9652244a86cf9cf52717ed284b078481e /src | |
| parent | b59ef4814525f487287da1609864f432cd79e3ed (diff) | |
| download | libirecovery-e7aadfe47394eaf81f6e76e49be8b300b5f23763.tar.gz libirecovery-e7aadfe47394eaf81f6e76e49be8b300b5f23763.tar.bz2 | |
Added detection of stalled pipes for IOKIT and added functions for async requests
Diffstat (limited to 'src')
| -rw-r--r-- | src/libirecovery.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/src/libirecovery.c b/src/libirecovery.c index bf9a0d6..390f5cc 100644 --- a/src/libirecovery.c +++ b/src/libirecovery.c | |||
| @@ -98,6 +98,18 @@ | |||
| 98 | static void f(void) | 98 | static void f(void) |
| 99 | #endif | 99 | #endif |
| 100 | 100 | ||
| 101 | #ifndef WIN32 | ||
| 102 | struct irecv_async_transfer { | ||
| 103 | uint32_t len; | ||
| 104 | #ifdef HAVE_IOKIT | ||
| 105 | kern_return_t ret; | ||
| 106 | #else | ||
| 107 | enum libusb_transfer_status ret; | ||
| 108 | #endif | ||
| 109 | }; | ||
| 110 | |||
| 111 | #endif | ||
| 112 | |||
| 101 | struct irecv_client_private { | 113 | struct irecv_client_private { |
| 102 | int debug; | 114 | int debug; |
| 103 | int usb_config; | 115 | int usb_config; |
| @@ -113,6 +125,7 @@ struct irecv_client_private { | |||
| 113 | #else | 125 | #else |
| 114 | IOUSBDeviceInterface320 **handle; | 126 | IOUSBDeviceInterface320 **handle; |
| 115 | IOUSBInterfaceInterface300 **usbInterface; | 127 | IOUSBInterfaceInterface300 **usbInterface; |
| 128 | CFRunLoopSourceRef async_event_source; | ||
| 116 | #endif | 129 | #endif |
| 117 | #else | 130 | #else |
| 118 | HANDLE handle; | 131 | HANDLE handle; |
| @@ -1402,6 +1415,7 @@ static int iokit_usb_control_transfer(irecv_client_t client, uint8_t bm_request_ | |||
| 1402 | result = (*client->handle)->DeviceRequestTO(client->handle, &req); | 1415 | result = (*client->handle)->DeviceRequestTO(client->handle, &req); |
| 1403 | switch (result) { | 1416 | switch (result) { |
| 1404 | case kIOReturnSuccess: return req.wLenDone; | 1417 | case kIOReturnSuccess: return req.wLenDone; |
| 1418 | case kIOUSBPipeStalled: return IRECV_E_PIPE; | ||
| 1405 | case kIOReturnTimeout: return IRECV_E_TIMEOUT; | 1419 | case kIOReturnTimeout: return IRECV_E_TIMEOUT; |
| 1406 | case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT; | 1420 | case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT; |
| 1407 | case kIOReturnNotResponding: return IRECV_E_NO_DEVICE; | 1421 | case kIOReturnNotResponding: return IRECV_E_NO_DEVICE; |
| @@ -1472,6 +1486,158 @@ int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, u | |||
| 1472 | #endif | 1486 | #endif |
| 1473 | } | 1487 | } |
| 1474 | 1488 | ||
| 1489 | #ifndef WIN32 | ||
| 1490 | #ifdef HAVE_IOKIT | ||
| 1491 | |||
| 1492 | static void iokit_async_cb(void *refcon, kern_return_t ret, void *arg_0) | ||
| 1493 | { | ||
| 1494 | struct irecv_async_transfer* transfer = refcon; | ||
| 1495 | |||
| 1496 | if(transfer != NULL) { | ||
| 1497 | transfer->ret = ret; | ||
| 1498 | memcpy(&transfer->len, &arg_0, sizeof(transfer->len)); | ||
| 1499 | CFRunLoopStop(CFRunLoopGetCurrent()); | ||
| 1500 | } | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | static int iokit_async_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, struct irecv_async_transfer* transfer) | ||
| 1504 | { | ||
| 1505 | IOReturn result; | ||
| 1506 | IOUSBDevRequest req; | ||
| 1507 | |||
| 1508 | bzero(&req, sizeof(req)); | ||
| 1509 | req.bmRequestType = bm_request_type; | ||
| 1510 | req.bRequest = b_request; | ||
| 1511 | req.wValue = OSSwapLittleToHostInt16(w_value); | ||
| 1512 | req.wIndex = OSSwapLittleToHostInt16(w_index); | ||
| 1513 | req.wLength = OSSwapLittleToHostInt16(w_length); | ||
| 1514 | req.pData = data; | ||
| 1515 | result = (*client->handle)->DeviceRequestAsync(client->handle, &req, iokit_async_cb, transfer); | ||
| 1516 | switch (result) { | ||
| 1517 | case kIOReturnSuccess: return IRECV_E_SUCCESS; | ||
| 1518 | case kIOUSBPipeStalled: return IRECV_E_PIPE; | ||
| 1519 | case kIOReturnTimeout: return IRECV_E_TIMEOUT; | ||
| 1520 | case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT; | ||
| 1521 | case kIOReturnNotResponding: return IRECV_E_NO_DEVICE; | ||
| 1522 | case kIOReturnNoDevice: return IRECV_E_NO_DEVICE; | ||
| 1523 | default: | ||
| 1524 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1525 | } | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | #else | ||
| 1529 | |||
| 1530 | static void async_cb(struct libusb_transfer* usb_transfer) { | ||
| 1531 | struct irecv_async_transfer* transfer = usb_transfer->user_data; | ||
| 1532 | transfer->ret = usb_transfer->status; | ||
| 1533 | transfer->len += usb_transfer->actual_length; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | #endif | ||
| 1537 | #endif | ||
| 1538 | |||
| 1539 | IRECV_API int irecv_async_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) { | ||
| 1540 | #ifdef USE_DUMMY | ||
| 1541 | return IRECV_E_UNSUPPORTED; | ||
| 1542 | #else | ||
| 1543 | #ifndef _WIN32 | ||
| 1544 | #ifdef HAVE_IOKIT | ||
| 1545 | return iokit_async_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, NULL); | ||
| 1546 | #else | ||
| 1547 | irecv_error_t error; | ||
| 1548 | |||
| 1549 | unsigned char* buffer = malloc(w_length + 8); | ||
| 1550 | if(!buffer) { | ||
| 1551 | return IRECV_E_OUT_OF_MEMORY; | ||
| 1552 | } | ||
| 1553 | struct libusb_transfer* usb_transfer = libusb_alloc_transfer(0); | ||
| 1554 | if(!usb_transfer) { | ||
| 1555 | free(buffer); | ||
| 1556 | return IRECV_E_OUT_OF_MEMORY; | ||
| 1557 | } | ||
| 1558 | memcpy((buffer + 8), data, w_length); | ||
| 1559 | libusb_fill_control_setup(buffer, bm_request_type, b_request, w_value, w_index, w_length); | ||
| 1560 | libusb_fill_control_transfer(usb_transfer, client->handle, buffer, NULL, NULL, 0); | ||
| 1561 | usb_transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER; | ||
| 1562 | error = libusb_submit_transfer(usb_transfer); | ||
| 1563 | if(error != 0) { | ||
| 1564 | free(buffer); | ||
| 1565 | return error; | ||
| 1566 | } | ||
| 1567 | free(buffer); | ||
| 1568 | return IRECV_E_SUCCESS; | ||
| 1569 | #endif | ||
| 1570 | #else | ||
| 1571 | return IRECV_E_UNSUPPORTED; | ||
| 1572 | #endif | ||
| 1573 | #endif | ||
| 1574 | |||
| 1575 | } | ||
| 1576 | |||
| 1577 | IRECV_API int irecv_async_usb_control_transfer_with_cancel(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 u_time) { | ||
| 1578 | #ifdef USE_DUMMY | ||
| 1579 | return IRECV_E_UNSUPPORTED; | ||
| 1580 | #else | ||
| 1581 | #ifndef _WIN32 | ||
| 1582 | irecv_error_t error; | ||
| 1583 | struct irecv_async_transfer transfer; | ||
| 1584 | bzero(&transfer, sizeof(struct irecv_async_transfer)); | ||
| 1585 | |||
| 1586 | #ifdef HAVE_IOKIT | ||
| 1587 | |||
| 1588 | error = iokit_async_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, &transfer); | ||
| 1589 | if(error != IRECV_E_SUCCESS) { | ||
| 1590 | return error; | ||
| 1591 | } | ||
| 1592 | usleep(u_time); | ||
| 1593 | error = (*client->handle)->USBDeviceAbortPipeZero(client->handle); | ||
| 1594 | if(error != kIOReturnSuccess) { | ||
| 1595 | return IRECV_E_UNKNOWN_ERROR; | ||
| 1596 | } | ||
| 1597 | while(transfer.ret != kIOReturnAborted){ | ||
| 1598 | CFRunLoopRun(); | ||
| 1599 | } | ||
| 1600 | return transfer.len; | ||
| 1601 | #else | ||
| 1602 | unsigned char* buffer = malloc(w_length + 8); | ||
| 1603 | if(!buffer) { | ||
| 1604 | return IRECV_E_OUT_OF_MEMORY; | ||
| 1605 | } | ||
| 1606 | struct libusb_transfer* usb_transfer = libusb_alloc_transfer(0); | ||
| 1607 | if(!usb_transfer) { | ||
| 1608 | free(buffer); | ||
| 1609 | return IRECV_E_OUT_OF_MEMORY; | ||
| 1610 | } | ||
| 1611 | memcpy((buffer + 8), data, w_length); | ||
| 1612 | libusb_fill_control_setup(buffer, bm_request_type, b_request, w_value, w_index, w_length); | ||
| 1613 | libusb_fill_control_transfer(usb_transfer, client->handle, buffer, async_cb, &transfer, 0); | ||
| 1614 | usb_transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER; | ||
| 1615 | error = libusb_submit_transfer(usb_transfer); | ||
| 1616 | if(error != 0) { | ||
| 1617 | free(buffer); | ||
| 1618 | return error; | ||
| 1619 | } | ||
| 1620 | |||
| 1621 | usleep(u_time); | ||
| 1622 | |||
| 1623 | error = libusb_cancel_transfer(usb_transfer); | ||
| 1624 | if(error != 0) { | ||
| 1625 | free(buffer); | ||
| 1626 | return error; | ||
| 1627 | } | ||
| 1628 | free(buffer); | ||
| 1629 | |||
| 1630 | while(transfer.ret != LIBUSB_TRANSFER_CANCELLED){ | ||
| 1631 | libusb_handle_events_completed(libirecovery_context, NULL); | ||
| 1632 | } | ||
| 1633 | return transfer.len; | ||
| 1634 | #endif | ||
| 1635 | #else | ||
| 1636 | return IRECV_E_UNSUPPORTED; | ||
| 1637 | #endif | ||
| 1638 | #endif | ||
| 1639 | } | ||
| 1640 | |||
| 1475 | #ifndef USE_DUMMY | 1641 | #ifndef USE_DUMMY |
| 1476 | #ifdef HAVE_IOKIT | 1642 | #ifdef HAVE_IOKIT |
| 1477 | static int iokit_usb_bulk_transfer(irecv_client_t client, | 1643 | static int iokit_usb_bulk_transfer(irecv_client_t client, |
| @@ -1978,6 +2144,15 @@ irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, uint64_t ecid) | |||
| 1978 | return error; | 2144 | return error; |
| 1979 | } | 2145 | } |
| 1980 | 2146 | ||
| 2147 | #ifdef HAVE_IOKIT | ||
| 2148 | error = (*client->handle)->CreateDeviceAsyncEventSource(client->handle, &client->async_event_source); | ||
| 2149 | if (error != IRECV_E_SUCCESS) { | ||
| 2150 | free(client); | ||
| 2151 | return error; | ||
| 2152 | } | ||
| 2153 | CFRunLoopAddSource(CFRunLoopGetCurrent(), client->async_event_source, kCFRunLoopDefaultMode); | ||
| 2154 | #endif | ||
| 2155 | |||
| 1981 | if (client->mode == IRECV_K_DFU_MODE || client->mode == IRECV_K_PORT_DFU_MODE || client->mode == IRECV_K_WTF_MODE || client->mode == KIS_PRODUCT_ID) { | 2156 | if (client->mode == IRECV_K_DFU_MODE || client->mode == IRECV_K_PORT_DFU_MODE || client->mode == IRECV_K_WTF_MODE || client->mode == KIS_PRODUCT_ID) { |
| 1982 | error = irecv_usb_set_interface(client, 0, 0); | 2157 | error = irecv_usb_set_interface(client, 0, 0); |
| 1983 | } else { | 2158 | } else { |
| @@ -3196,6 +3371,10 @@ static irecv_error_t irecv_cleanup(irecv_client_t client) | |||
| 3196 | (*client->handle)->Release(client->handle); | 3371 | (*client->handle)->Release(client->handle); |
| 3197 | client->handle = NULL; | 3372 | client->handle = NULL; |
| 3198 | } | 3373 | } |
| 3374 | if(client->async_event_source) { | ||
| 3375 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), client->async_event_source, kCFRunLoopDefaultMode); | ||
| 3376 | CFRelease(client->async_event_source); | ||
| 3377 | } | ||
| 3199 | #else | 3378 | #else |
| 3200 | if (client->handle != NULL) { | 3379 | if (client->handle != NULL) { |
| 3201 | if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE) && (client->isKIS == 0)) { | 3380 | if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE) && (client->isKIS == 0)) { |
