refactor: libciderra1n support (#764)

* refactor

refactor: remove wdi

refactor: move openra1n to its own library

refactor: move everything standalone file to src root

* submodule

* okay these shouldnt of been included

* remove

* readd

* fix openra1n libusb

* liteusb for ra1ncovery

* fix action

* token

* whatever lwra bw apwxidix

* I knew this was gonna happen

* fix mac build

* what if this doesnt work

* okay bro

* lzma resources

* rename

* use idevice++, fix multiple devices, enter recovery

* whatever

* fix

* --force-enable-ssv

* improve exploit stuff, mirror tui logic to gui

also cooool logging for libopenra1n

* fix idevice windows build

* lets not be fancy

* new idevice patch

* easier to read

* lets hope this action works!

* remove useless ifs

* not required for non tui

* PLEASE COMPILE

* gsed lmao

* this might work idk

* whatever bro

* ah

* amd fix

* fix ts

* fix

* licensing

* update paleinfo

* LETS NOT do that

* forgot a file
This commit is contained in:
c authored and GitHub committed 2026-07-21 20:22:02 -07:00
1 parent f15011828b
commit 0cf2c6492f
128 files changed
+12054 -3114

No files matched your search

+87
View File
@@ -0,0 +1,87 @@
/*
* palera1n - https://palera.in
*
* Copyright (C) 2026 palera1n team
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#if defined(WITH_GUI) || defined(WITH_TUI)
#include "device_info.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <cstring>
static uint64_t get_hex_field(const char *serial, const char *field) {
char *p = strstr((char *)serial, field);
return p ? strtoull(p + strlen(field), nullptr, 16) : 0;
}
void get_recovery_info(char *sn, const char **ecid, const char **product_model, const char **product_name)
{
static char cpid_buf[16];
static char ecid_buf[32];
static char bdid_buf[16];
uint16_t cpid_val = (uint16_t)get_hex_field(sn, "CPID:");
uint32_t bdid_val = (uint32_t)get_hex_field(sn, "BDID:");
uint64_t ecid_val = get_hex_field(sn, "ECID:");
snprintf(cpid_buf, sizeof(cpid_buf), "%04X", cpid_val);
snprintf(bdid_buf, sizeof(bdid_buf), "%08X", bdid_val);
snprintf(ecid_buf, sizeof(ecid_buf), "%016llX", (unsigned long long)ecid_val);
*ecid = ecid_buf;
*product_model = nullptr;
*product_name = nullptr;
for (int i = 0; irecv_devices[i].product_type; i++) {
if (irecv_devices[i].cpid == cpid_val &&
irecv_devices[i].bdid == bdid_val) {
*product_model = irecv_devices[i].product_type;
*product_name = irecv_devices[i].name;
break;
}
}
}
void get_name_from_product_type(const char *product_type, const char **name) {
*name = nullptr;
for (int i = 0; irecv_devices[i].product_type; i++) {
if (strcmp(irecv_devices[i].product_type, product_type) == 0) {
*name = irecv_devices[i].name;
break;
}
}
}
#endif // WITH_GUI || WITH_TUI
+382
View File
@@ -0,0 +1,382 @@
/*
* palera1n - https://palera.in
*
* Copyright (C) 2026 palera1n team
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#if defined(WITH_GUI) || defined(WITH_TUI)
#ifndef MAIN__DEVICE_INFO_H
#define MAIN__DEVICE_INFO_H
struct irecv_device {
const char *product_type;
const char *board;
int bdid;
int cpid;
const char *name;
};
static struct irecv_device irecv_devices[] = {
/* iPhone */
{ "iPhone1,1", "m68ap", 0x00, 0x8900, "iPhone 2G" },
{ "iPhone1,2", "n82ap", 0x04, 0x8900, "iPhone 3G" },
{ "iPhone2,1", "n88ap", 0x00, 0x8920, "iPhone 3Gs" },
{ "iPhone3,1", "n90ap", 0x00, 0x8930, "iPhone 4 (GSM)" },
{ "iPhone3,2", "n90bap", 0x04, 0x8930, "iPhone 4 (GSM) R2 2012" },
{ "iPhone3,3", "n92ap", 0x06, 0x8930, "iPhone 4 (CDMA)" },
{ "iPhone4,1", "n94ap", 0x08, 0x8940, "iPhone 4s" },
{ "iPhone5,1", "n41ap", 0x00, 0x8950, "iPhone 5 (GSM)" },
{ "iPhone5,2", "n42ap", 0x02, 0x8950, "iPhone 5 (Global)" },
{ "iPhone5,3", "n48ap", 0x0a, 0x8950, "iPhone 5c (GSM)" },
{ "iPhone5,4", "n49ap", 0x0e, 0x8950, "iPhone 5c (Global)" },
{ "iPhone6,1", "n51ap", 0x00, 0x8960, "iPhone 5s (GSM)" },
{ "iPhone6,2", "n53ap", 0x02, 0x8960, "iPhone 5s (Global)" },
{ "iPhone7,1", "n56ap", 0x04, 0x7000, "iPhone 6 Plus" },
{ "iPhone7,2", "n61ap", 0x06, 0x7000, "iPhone 6" },
{ "iPhone8,1", "n71ap", 0x04, 0x8000, "iPhone 6s" },
{ "iPhone8,1", "n71map", 0x04, 0x8003, "iPhone 6s" },
{ "iPhone8,2", "n66ap", 0x06, 0x8000, "iPhone 6s Plus" },
{ "iPhone8,2", "n66map", 0x06, 0x8003, "iPhone 6s Plus" },
{ "iPhone8,4", "n69ap", 0x02, 0x8003, "iPhone SE (1st gen)" },
{ "iPhone8,4", "n69uap", 0x02, 0x8000, "iPhone SE (1st gen)" },
{ "iPhone9,1", "d10ap", 0x08, 0x8010, "iPhone 7 (Global)" },
{ "iPhone9,2", "d11ap", 0x0a, 0x8010, "iPhone 7 Plus (Global)" },
{ "iPhone9,3", "d101ap", 0x0c, 0x8010, "iPhone 7 (GSM)" },
{ "iPhone9,4", "d111ap", 0x0e, 0x8010, "iPhone 7 Plus (GSM)" },
{ "iPhone10,1", "d20ap", 0x02, 0x8015, "iPhone 8 (Global)" },
{ "iPhone10,2", "d21ap", 0x04, 0x8015, "iPhone 8 Plus (Global)" },
{ "iPhone10,3", "d22ap", 0x06, 0x8015, "iPhone X (Global)" },
{ "iPhone10,4", "d201ap", 0x0a, 0x8015, "iPhone 8 (GSM)" },
{ "iPhone10,5", "d211ap", 0x0c, 0x8015, "iPhone 8 Plus (GSM)" },
{ "iPhone10,6", "d221ap", 0x0e, 0x8015, "iPhone X (GSM)" },
{ "iPhone11,2", "d321ap", 0x0e, 0x8020, "iPhone XS" },
{ "iPhone11,4", "d331ap", 0x0a, 0x8020, "iPhone XS Max (China)" },
{ "iPhone11,6", "d331pap", 0x1a, 0x8020, "iPhone XS Max" },
{ "iPhone11,8", "n841ap", 0x0c, 0x8020, "iPhone XR" },
{ "iPhone12,1", "n104ap", 0x04, 0x8030, "iPhone 11" },
{ "iPhone12,3", "d421ap", 0x06, 0x8030, "iPhone 11 Pro" },
{ "iPhone12,5", "d431ap", 0x02, 0x8030, "iPhone 11 Pro Max" },
{ "iPhone12,8", "d79ap", 0x10, 0x8030, "iPhone SE (2nd gen)" },
{ "iPhone13,1", "d52gap", 0x0A, 0x8101, "iPhone 12 mini" },
{ "iPhone13,2", "d53gap", 0x0C, 0x8101, "iPhone 12" },
{ "iPhone13,3", "d53pap", 0x0E, 0x8101, "iPhone 12 Pro" },
{ "iPhone13,4", "d54pap", 0x08, 0x8101, "iPhone 12 Pro Max" },
{ "iPhone14,2", "d63ap", 0x0C, 0x8110, "iPhone 13 Pro" },
{ "iPhone14,3", "d64ap", 0x0E, 0x8110, "iPhone 13 Pro Max" },
{ "iPhone14,4", "d16ap", 0x08, 0x8110, "iPhone 13 mini" },
{ "iPhone14,5", "d17ap", 0x0A, 0x8110, "iPhone 13" },
{ "iPhone14,6", "d49ap", 0x10, 0x8110, "iPhone SE (3rd gen)" },
{ "iPhone14,7", "d27ap", 0x18, 0x8110, "iPhone 14" },
{ "iPhone14,8", "d28ap", 0x1A, 0x8110, "iPhone 14 Plus" },
{ "iPhone15,2", "d73ap", 0x0C, 0x8120, "iPhone 14 Pro" },
{ "iPhone15,3", "d74ap", 0x0E, 0x8120, "iPhone 14 Pro Max" },
{ "iPhone15,4", "d37ap", 0x08, 0x8120, "iPhone 15" },
{ "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" },
{ "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" },
{ "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" },
{ "iPhone17,1", "d93ap", 0x0C, 0x8140, "iPhone 16 Pro" },
{ "iPhone17,2", "d94ap", 0x0E, 0x8140, "iPhone 16 Pro Max" },
{ "iPhone17,3", "d47ap", 0x08, 0x8140, "iPhone 16" },
{ "iPhone17,4", "d48ap", 0x0A, 0x8140, "iPhone 16 Plus" },
{ "iPhone17,5", "v59ap", 0x04, 0x8140, "iPhone 16e" },
{ "iPhone18,1", "v53ap", 0x0C, 0x8150, "iPhone 17 Pro" },
{ "iPhone18,2", "v54ap", 0x0E, 0x8150, "iPhone 17 Pro Max" },
{ "iPhone18,3", "v57ap", 0x08, 0x8150, "iPhone 17" },
{ "iPhone18,4", "d23ap", 0x0A, 0x8150, "iPhone Air" },
{ "iPhone18,5", "v159ap", 0x16, 0x8150, "iPhone 17e" },
/* iPod */
{ "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" },
{ "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" },
{ "iPod3,1", "n18ap", 0x02, 0x8922, "iPod Touch (3rd gen)" },
{ "iPod4,1", "n81ap", 0x08, 0x8930, "iPod Touch (4th gen)" },
{ "iPod5,1", "n78ap", 0x00, 0x8942, "iPod Touch (5th gen)" },
{ "iPod7,1", "n102ap", 0x10, 0x7000, "iPod Touch (6th gen)" },
{ "iPod9,1", "n112ap", 0x16, 0x8010, "iPod Touch (7th gen)" },
/* iPad */
{ "iPad1,1", "k48ap", 0x02, 0x8930, "iPad" },
{ "iPad2,1", "k93ap", 0x04, 0x8940, "iPad 2 (WiFi)" },
{ "iPad2,2", "k94ap", 0x06, 0x8940, "iPad 2 (GSM)" },
{ "iPad2,3", "k95ap", 0x02, 0x8940, "iPad 2 (CDMA)" },
{ "iPad2,4", "k93aap", 0x06, 0x8942, "iPad 2 (WiFi) R2 2012" },
{ "iPad2,5", "p105ap", 0x0a, 0x8942, "iPad mini (WiFi)" },
{ "iPad2,6", "p106ap", 0x0c, 0x8942, "iPad mini (GSM)" },
{ "iPad2,7", "p107ap", 0x0e, 0x8942, "iPad mini (Global)" },
{ "iPad3,1", "j1ap", 0x00, 0x8945, "iPad (3rd gen, WiFi)" },
{ "iPad3,2", "j2ap", 0x02, 0x8945, "iPad (3rd gen, CDMA)" },
{ "iPad3,3", "j2aap", 0x04, 0x8945, "iPad (3rd gen, GSM)" },
{ "iPad3,4", "p101ap", 0x00, 0x8955, "iPad (4th gen, WiFi)" },
{ "iPad3,5", "p102ap", 0x02, 0x8955, "iPad (4th gen, GSM)" },
{ "iPad3,6", "p103ap", 0x04, 0x8955, "iPad (4th gen, Global)" },
{ "iPad4,1", "j71ap", 0x10, 0x8960, "iPad Air (WiFi)" },
{ "iPad4,2", "j72ap", 0x12, 0x8960, "iPad Air (Cellular)" },
{ "iPad4,3", "j73ap", 0x14, 0x8960, "iPad Air (China)" },
{ "iPad4,4", "j85ap", 0x0a, 0x8960, "iPad mini 2 (WiFi)" },
{ "iPad4,5", "j86ap", 0x0c, 0x8960, "iPad mini 2 (Cellular)" },
{ "iPad4,6", "j87ap", 0x0e, 0x8960, "iPad mini 2 (China)" },
{ "iPad4,7", "j85map", 0x32, 0x8960, "iPad mini 3 (WiFi)" },
{ "iPad4,8", "j86map", 0x34, 0x8960, "iPad mini 3 (Cellular)" },
{ "iPad4,9", "j87map", 0x36, 0x8960, "iPad mini 3 (China)" },
{ "iPad5,1", "j96ap", 0x08, 0x7000, "iPad mini 4 (WiFi)" },
{ "iPad5,2", "j97ap", 0x0A, 0x7000, "iPad mini 4 (Cellular)" },
{ "iPad5,3", "j81ap", 0x06, 0x7001, "iPad Air 2 (WiFi)" },
{ "iPad5,4", "j82ap", 0x02, 0x7001, "iPad Air 2 (Cellular)" },
{ "iPad6,3", "j127ap", 0x08, 0x8001, "iPad Pro 9.7-inch (WiFi)" },
{ "iPad6,4", "j128ap", 0x0a, 0x8001, "iPad Pro 9.7-inch (Cellular)" },
{ "iPad6,7", "j98aap", 0x10, 0x8001, "iPad Pro 12.9-inch (1st gen, WiFi)" },
{ "iPad6,8", "j99aap", 0x12, 0x8001, "iPad Pro 12.9-inch (1st gen, Cellular)" },
{ "iPad6,11", "j71sap", 0x10, 0x8000, "iPad (5th gen, WiFi)" },
{ "iPad6,11", "j71tap", 0x10, 0x8003, "iPad (5th gen, WiFi)" },
{ "iPad6,12", "j72sap", 0x12, 0x8000, "iPad (5th gen, Cellular)" },
{ "iPad6,12", "j72tap", 0x12, 0x8003, "iPad (5th gen, Cellular)" },
{ "iPad7,1", "j120ap", 0x0C, 0x8011, "iPad Pro 12.9-inch (2nd gen, WiFi)" },
{ "iPad7,2", "j121ap", 0x0E, 0x8011, "iPad Pro 12.9-inch (2nd gen, Cellular)" },
{ "iPad7,3", "j207ap", 0x04, 0x8011, "iPad Pro 10.5-inch (WiFi)" },
{ "iPad7,4", "j208ap", 0x06, 0x8011, "iPad Pro 10.5-inch (Cellular)" },
{ "iPad7,5", "j71bap", 0x18, 0x8010, "iPad (6th gen, WiFi)" },
{ "iPad7,6", "j72bap", 0x1A, 0x8010, "iPad (6th gen, Cellular)" },
{ "iPad7,11", "j171ap", 0x1C, 0x8010, "iPad (7th gen, WiFi)" },
{ "iPad7,12", "j172ap", 0x1E, 0x8010, "iPad (7th gen, Cellular)" },
{ "iPad8,1", "j317ap", 0x0C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi)" },
{ "iPad8,2", "j317xap", 0x1C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi, 1TB)" },
{ "iPad8,3", "j318ap", 0x0E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular)" },
{ "iPad8,4", "j318xap", 0x1E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular, 1TB)" },
{ "iPad8,5", "j320ap", 0x08, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi)" },
{ "iPad8,6", "j320xap", 0x18, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi, 1TB)" },
{ "iPad8,7", "j321ap", 0x0A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular)" },
{ "iPad8,8", "j321xap", 0x1A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular, 1TB)" },
{ "iPad8,9", "j417ap", 0x3C, 0x8027, "iPad Pro 11-inch (2nd gen, WiFi)" },
{ "iPad8,10", "j418ap", 0x3E, 0x8027, "iPad Pro 11-inch (2nd gen, Cellular)" },
{ "iPad8,11", "j420ap", 0x38, 0x8027, "iPad Pro 12.9-inch (4th gen, WiFi)" },
{ "iPad8,12", "j421ap", 0x3A, 0x8027, "iPad Pro 12.9-inch (4th gen, Cellular)" },
{ "iPad11,1", "j210ap", 0x14, 0x8020, "iPad mini (5th gen, WiFi)" },
{ "iPad11,2", "j211ap", 0x16, 0x8020, "iPad mini (5th gen, Cellular)" },
{ "iPad11,3", "j217ap", 0x1C, 0x8020, "iPad Air (3rd gen, WiFi)" },
{ "iPad11,4", "j218ap", 0x1E, 0x8020, "iPad Air (3rd gen, Cellular)" },
{ "iPad11,6", "j171aap", 0x24, 0x8020, "iPad (8th gen, WiFi)" },
{ "iPad11,7", "j172aap", 0x26, 0x8020, "iPad (8th gen, Cellular)" },
{ "iPad12,1", "j181ap", 0x18, 0x8030, "iPad (9th gen, WiFi)" },
{ "iPad12,2", "j182ap", 0x1A, 0x8030, "iPad (9th gen, Cellular)" },
{ "iPad13,1", "j307ap", 0x04, 0x8101, "iPad Air (4th gen, WiFi)" },
{ "iPad13,2", "j308ap", 0x06, 0x8101, "iPad Air (4th gen, Cellular)" },
{ "iPad13,4", "j517ap", 0x08, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi)" },
{ "iPad13,5", "j517xap", 0x0A, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi, 2TB)" },
{ "iPad13,6", "j518ap", 0x0C, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular)" },
{ "iPad13,7", "j518xap", 0x0E, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular, 2TB)" },
{ "iPad13,8", "j522ap", 0x18, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi)" },
{ "iPad13,9", "j522xap", 0x1A, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi, 2TB)" },
{ "iPad13,10", "j523ap", 0x1C, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular)" },
{ "iPad13,11", "j523xap", 0x1E, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular, 2TB)" },
{ "iPad13,16", "j407ap", 0x10, 0x8103, "iPad Air (5th gen, WiFi)" },
{ "iPad13,17", "j408ap", 0x12, 0x8103, "iPad Air (5th gen, Cellular)" },
{ "iPad13,18", "j271ap", 0x14, 0x8101, "iPad (10th gen, WiFi)" },
{ "iPad13,19", "j272ap", 0x16, 0x8101, "iPad (10th gen, Cellular)" },
{ "iPad14,1", "j310ap", 0x04, 0x8110, "iPad mini (6th gen, WiFi)" },
{ "iPad14,2", "j311ap", 0x06, 0x8110, "iPad mini (6th gen, Cellular)" },
{ "iPad14,3", "j617ap", 0x08, 0x8112, "iPad Pro 11-inch (4th gen, WiFi)" },
{ "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" },
{ "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" },
{ "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" },
{ "iPad14,8", "j507ap", 0x10, 0x8112, "iPad Air 11-inch (M2, WiFi)" },
{ "iPad14,9", "j508ap", 0x12, 0x8112, "iPad Air 11-inch (M2, Cellular)" },
{ "iPad14,10", "j537ap", 0x14, 0x8112, "iPad Air 13-inch (M2, WiFi)" },
{ "iPad14,11", "j538ap", 0x16, 0x8112, "iPad Air 13-inch (M2, Cellular)" },
{ "iPad15,3", "j607ap", 0x08, 0x8122, "iPad Air 11-inch (M3, WiFi)" },
{ "iPad15,4", "j608ap", 0x0A, 0x8122, "iPad Air 11-inch (M3, Cellular)" },
{ "iPad15,5", "j637ap", 0x0C, 0x8122, "iPad Air 13-inch (M3, WiFi)" },
{ "iPad15,6", "j638ap", 0x0E, 0x8122, "iPad Air 13-inch (M3, Cellular)" },
{ "iPad15,7", "j481ap", 0x10, 0x8120, "iPad (A16, WiFi)" },
{ "iPad15,8", "j482ap", 0x12, 0x8120, "iPad (A16, Cellular)" },
{ "iPad16,1", "j410ap", 0x08, 0x8130, "iPad mini (A17 Pro, WiFi)" },
{ "iPad16,2", "j411ap", 0x0A, 0x8130, "iPad mini (A17 Pro, Cellular)" },
{ "iPad16,3", "j717ap", 0x08, 0x8132, "iPad Pro 11-inch (M4, WiFi)" },
{ "iPad16,4", "j718ap", 0x0A, 0x8132, "iPad Pro 11-inch (M4, Cellular)" },
{ "iPad16,5", "j720ap", 0x0C, 0x8132, "iPad Pro 13-inch (M4, WiFi)" },
{ "iPad16,6", "j721ap", 0x0E, 0x8132, "iPad Pro 13-inch (M4, Cellular)" },
{ "iPad16,8", "j707ap", 0x10, 0x8132, "iPad Air 11-inch (M4, Wi-Fi)" },
{ "iPad16,9", "j708ap", 0x12, 0x8132, "iPad Air 11-inch (M4, Cellular)" },
{ "iPad16,10", "j737ap", 0x14, 0x8132, "iPad Air 13-inch (M4, Wi-Fi)" },
{ "iPad16,11", "j738ap", 0x16, 0x8132, "iPad Air 13-inch (M4, Cellular)" },
{ "iPad17,1", "j817ap", 0x08, 0x8142, "iPad Pro 11-inch (M5, WiFi)" },
{ "iPad17,2", "j818ap", 0x0A, 0x8142, "iPad Pro 11-inch (M5, Cellular)" },
{ "iPad17,3", "j820ap", 0x0C, 0x8142, "iPad Pro 13-inch (M5, WiFi)" },
{ "iPad17,4", "j821ap", 0x0E, 0x8142, "iPad Pro 13-inch (M5, Cellular)" },
/* Apple TV */
{ "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" },
{ "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" },
{ "AppleTV3,2", "j33iap", 0x00, 0x8947, "Apple TV 3 (2013)" },
{ "AppleTV5,3", "j42dap", 0x34, 0x7000, "Apple TV 4" },
{ "AppleTV6,2", "j105aap", 0x02, 0x8011, "Apple TV 4K" },
{ "AppleTV11,1", "j305ap", 0x08, 0x8020, "Apple TV 4K (2nd gen)" },
{ "AppleTV14,1", "j255ap", 0x02, 0x8110, "Apple TV 4K (3rd gen)" },
/* HomePod */
{ "AudioAccessory1,1", "b238aap", 0x38, 0x7000, "HomePod (1st gen)" },
{ "AudioAccessory1,2", "b238ap", 0x1A, 0x7000, "HomePod (1st gen)" },
{ "AudioAccessory5,1", "b520ap", 0x22, 0x8006, "HomePod mini" },
{ "AudioAccessory6,1", "b620ap", 0x18, 0x8301, "HomePod (2nd gen)" },
/* Apple Watch */
{ "Watch1,1", "n27aap", 0x02, 0x7002, "Apple Watch 38mm (1st gen)" },
{ "Watch1,2", "n28aap", 0x04, 0x7002, "Apple Watch 42mm (1st gen)" },
{ "Watch2,6", "n27dap", 0x02, 0x8002, "Apple Watch Series 1 (38mm)" },
{ "Watch2,7", "n28dap", 0x04, 0x8002, "Apple Watch Series 1 (42mm)" },
{ "Watch2,3", "n74ap", 0x0C, 0x8002, "Apple Watch Series 2 (38mm)" },
{ "Watch2,4", "n75ap", 0x0E, 0x8002, "Apple Watch Series 2 (42mm)" },
{ "Watch3,1", "n111sap", 0x1C, 0x8004, "Apple Watch Series 3 (38mm Cellular)" },
{ "Watch3,2", "n111bap", 0x1E, 0x8004, "Apple Watch Series 3 (42mm Cellular)" },
{ "Watch3,3", "n121sap", 0x18, 0x8004, "Apple Watch Series 3 (38mm)" },
{ "Watch3,4", "n121bap", 0x1A, 0x8004, "Apple Watch Series 3 (42mm)" },
{ "Watch4,1", "n131sap", 0x08, 0x8006, "Apple Watch Series 4 (40mm)" },
{ "Watch4,2", "n131bap", 0x0A, 0x8006, "Apple Watch Series 4 (44mm)" },
{ "Watch4,3", "n141sap", 0x0C, 0x8006, "Apple Watch Series 4 (40mm Cellular)" },
{ "Watch4,4", "n141bap", 0x0E, 0x8006, "Apple Watch Series 4 (44mm Cellular)" },
{ "Watch5,1", "n144sap", 0x10, 0x8006, "Apple Watch Series 5 (40mm)" },
{ "Watch5,2", "n144bap", 0x12, 0x8006, "Apple Watch Series 5 (44mm)" },
{ "Watch5,3", "n146sap", 0x14, 0x8006, "Apple Watch Series 5 (40mm Cellular)" },
{ "Watch5,4", "n146bap", 0x16, 0x8006, "Apple Watch Series 5 (44mm Cellular)" },
{ "Watch5,9", "n140sap", 0x28, 0x8006, "Apple Watch SE (40mm)" },
{ "Watch5,10", "n140bap", 0x2A, 0x8006, "Apple Watch SE (44mm)" },
{ "Watch5,11", "n142sap", 0x2C, 0x8006, "Apple Watch SE (40mm Cellular)" },
{ "Watch5,12", "n142bap", 0x2E, 0x8006, "Apple Watch SE (44mm Cellular)" },
{ "Watch6,1", "n157sap", 0x08, 0x8301, "Apple Watch Series 6 (40mm)" },
{ "Watch6,2", "n157bap", 0x0A, 0x8301, "Apple Watch Series 6 (44mm)" },
{ "Watch6,3", "n158sap", 0x0C, 0x8301, "Apple Watch Series 6 (40mm Cellular)" },
{ "Watch6,4", "n158bap", 0x0E, 0x8301, "Apple Watch Series 6 (44mm Cellular)" },
{ "Watch6,6", "n187sap", 0x10, 0x8301, "Apple Watch Series 7 (41mm)" },
{ "Watch6,7", "n187bap", 0x12, 0x8301, "Apple Watch Series 7 (45mm)" },
{ "Watch6,8", "n188sap", 0x14, 0x8301, "Apple Watch Series 7 (41mm Cellular)" },
{ "Watch6,9", "n188bap", 0x16, 0x8301, "Apple Watch Series 7 (45mm Cellular)" },
{ "Watch6,10", "n143sap", 0x28, 0x8301, "Apple Watch SE 2 (40mm)" },
{ "Watch6,11", "n143bap", 0x2A, 0x8301, "Apple Watch SE 2 (44mm)" },
{ "Watch6,12", "n149sap", 0x2C, 0x8301, "Apple Watch SE 2 (40mm Cellular)" },
{ "Watch6,13", "n149bap", 0x2E, 0x8301, "Apple Watch SE 2 (44mm Cellular)" },
{ "Watch6,14", "n197sap", 0x30, 0x8301, "Apple Watch Series 8 (41mm)" },
{ "Watch6,15", "n197bap", 0x32, 0x8301, "Apple Watch Series 8 (45mm)" },
{ "Watch6,16", "n198sap", 0x34, 0x8301, "Apple Watch Series 8 (41mm Cellular)" },
{ "Watch6,17", "n198bap", 0x36, 0x8301, "Apple Watch Series 8 (45mm Cellular)" },
{ "Watch6,18", "n199ap", 0x26, 0x8301, "Apple Watch Ultra" },
{ "Watch7,1", "n207sap", 0x08, 0x8310, "Apple Watch Series 9 (41mm)" },
{ "Watch7,2", "n207bap", 0x0A, 0x8310, "Apple Watch Series 9 (45mm)" },
{ "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" },
{ "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" },
{ "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" },
{ "Watch7,8", "n217sap", 0x10, 0x8310, "Apple Watch Series 10 (42mm)" },
{ "Watch7,9", "n217bap", 0x12, 0x8310, "Apple Watch Series 10 (46mm)" },
{ "Watch7,10", "n218sap", 0x14, 0x8310, "Apple Watch Series 10 (42mm Cellular)" },
{ "Watch7,11", "n218bap", 0x16, 0x8310, "Apple Watch Series 10 (46mm Cellular)" },
{ "Watch7,12", "n230ap", 0x22, 0x8310, "Apple Watch Ultra 3" },
{ "Watch7,13", "n243sap", 0x28, 0x8310, "Apple Watch SE 3 (40mm)" },
{ "Watch7,14", "n243bap", 0x2A, 0x8310, "Apple Watch SE 3 (44mm)" },
{ "Watch7,15", "n244sap", 0x2C, 0x8310, "Apple Watch SE 3 (40mm Cellular)" },
{ "Watch7,16", "n244bap", 0x2E, 0x8310, "Apple Watch SE 3 (44mm Cellular)" },
{ "Watch7,17", "n227sap", 0x18, 0x8310, "Apple Watch Series 11 (42mm)" },
{ "Watch7,18", "n227bap", 0x1A, 0x8310, "Apple Watch Series 11 (46mm)" },
{ "Watch7,19", "n228sap", 0x1C, 0x8310, "Apple Watch Series 11 (42mm Cellular)" },
{ "Watch7,20", "n228bap", 0x1E, 0x8310, "Apple Watch Series 11 (46mm Cellular)" },
/* Apple Silicon Macs */
{ "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" },
{ "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" },
{ "MacBookPro17,1", "j293ap", 0x24, 0x8103, "MacBook Pro (M1, 13-inch, 2020)" },
{ "MacBookPro18,1", "j316sap", 0x0A, 0x6000, "MacBook Pro (M1 Pro, 16-inch, 2021)" },
{ "MacBookPro18,2", "j316cap", 0x0A, 0x6001, "MacBook Pro (M1 Max, 16-inch, 2021)" },
{ "MacBookPro18,3", "j314sap", 0x08, 0x6000, "MacBook Pro (M1 Pro, 14-inch, 2021)" },
{ "MacBookPro18,4", "j314cap", 0x08, 0x6001, "MacBook Pro (M1 Max, 14-inch, 2021)" },
{ "MacBookAir10,1", "j313ap", 0x26, 0x8103, "MacBook Air (M1, 2020)" },
{ "iMac21,1", "j456ap", 0x28, 0x8103, "iMac 24-inch (M1, Two Ports, 2021)" },
{ "iMac21,2", "j457ap", 0x2A, 0x8103, "iMac 24-inch (M1, Four Ports, 2021)" },
{ "Mac13,1", "j375cap", 0x04, 0x6001, "Mac Studio (M1 Max, 2022)" },
{ "Mac13,2", "j375dap", 0x0C, 0x6002, "Mac Studio (M1 Ultra, 2022)" },
{ "Mac14,2", "j413ap", 0x28, 0x8112, "MacBook Air (M2, 2022)" },
{ "Mac14,7", "j493ap", 0x2A, 0x8112, "MacBook Pro (M2, 13-inch, 2022)" },
{ "Mac14,3", "j473ap", 0x24, 0x8112, "Mac mini (M2, 2023)" },
{ "Mac14,5", "j414cap", 0x04, 0x6021, "MacBook Pro (14-inch, M2 Max, 2023)" },
{ "Mac14,6", "j416cap", 0x06, 0x6021, "MacBook Pro (16-inch, M2 Max, 2023)" },
{ "Mac14,8", "j180dap", 0x08, 0x6022, "Mac Pro (2023)" },
{ "Mac14,9", "j414sap", 0x04, 0x6020, "MacBook Pro (14-inch, M2 Pro, 2023)" },
{ "Mac14,10", "j416sap", 0x06, 0x6020, "MacBook Pro (16-inch, M2 Pro, 2023)" },
{ "Mac14,12", "j474sap", 0x02, 0x6020, "Mac mini (M2 Pro, 2023)" },
{ "Mac14,13", "j475cap", 0x0A, 0x6021, "Mac Studio (M2 Max, 2023)" },
{ "Mac14,14", "j475dap", 0x0A, 0x6022, "Mac Studio (M2 Ultra, 2023)" },
{ "Mac14,15", "j415ap", 0x2E, 0x8112, "MacBook Air (M2, 15-inch, 2023)" },
{ "Mac15,3", "j504ap", 0x22, 0x8122, "MacBook Pro (14-inch, M3, Nov 2023)" },
{ "Mac15,4", "j433ap", 0x28, 0x8122, "iMac 24-inch (M3, Two Ports, 2023)" },
{ "Mac15,5", "j434ap", 0x2A, 0x8122, "iMac 24-inch (M3, Four Ports, 2023)" },
{ "Mac15,6", "j514sap", 0x04, 0x6030, "MacBook Pro (14-inch, M3 Pro, Nov 2023)" },
{ "Mac15,7", "j516sap", 0x06, 0x6030, "MacBook Pro (16-inch, M3 Pro, Nov 2023)" },
{ "Mac15,8", "j514cap", 0x44, 0x6031, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
{ "Mac15,9", "j516cap", 0x46, 0x6031, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
{ "Mac15,10", "j514map", 0x44, 0x6034, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
{ "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
{ "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" },
{ "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-inch, M3, 2024)" },
{ "Mac15,14", "j575dap", 0x44, 0x6032, "Mac Studio (M3 Ultra, 2025)" },
{ "Mac16,1", "j604ap", 0x22, 0x8132, "MacBook Pro (14-inch, M4, Nov 2024)" },
{ "Mac16,2", "j623ap", 0x24, 0x8132, "iMac 24-inch (M4, Two Ports, 2024)" },
{ "Mac16,3", "j624ap", 0x26, 0x8132, "iMac 24-inch (M4, Four Ports, 2024)" },
{ "Mac16,5", "j616cap", 0x06, 0x6041, "MacBook Pro (16-inch, M4 Max, Nov 2024)" },
{ "Mac16,6", "j614cap", 0x04, 0x6041, "MacBook Pro (14-inch, M4 Max, Nov 2024)" },
{ "Mac16,7", "j616sap", 0x06, 0x6040, "MacBook Pro (16-inch, M4 Pro, Nov 2024)" },
{ "Mac16,8", "j614sap", 0x04, 0x6040, "MacBook Pro (14-inch, M4 Pro, Nov 2024)" },
{ "Mac16,9", "j575cap", 0x02, 0x6041, "Mac Studio (M4 Max, 2025)" },
{ "Mac16,10", "j773gap", 0x2A, 0x8132, "Mac mini (M4, 2024)" },
{ "Mac16,11", "j773sap", 0x02, 0x6040, "Mac mini (M4 Pro, 2024)" },
{ "Mac16,12", "j713ap", 0x2C, 0x8132, "MacBook Air (13-inch, M4, 2025)" },
{ "Mac16,13", "j715ap", 0x2E, 0x8132, "MacBook Air (15-inch, M4, 2025)" },
{ "Mac17,2", "j704ap", 0x22, 0x8142, "MacBook Pro (14-inch, M5, 2025)" },
/* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */
{ "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" },
/* Apple T2 Coprocessor */
{ "iBridge2,1", "j137ap", 0x0A, 0x8012, "Apple T2 iMacPro1,1 (j137)" },
{ "iBridge2,3", "j680ap", 0x0B, 0x8012, "Apple T2 MacBookPro15,1 (j680)" },
{ "iBridge2,4", "j132ap", 0x0C, 0x8012, "Apple T2 MacBookPro15,2 (j132)" },
{ "iBridge2,5", "j174ap", 0x0E, 0x8012, "Apple T2 Macmini8,1 (j174)" },
{ "iBridge2,6", "j160ap", 0x0F, 0x8012, "Apple T2 MacPro7,1 (j160)" },
{ "iBridge2,7", "j780ap", 0x07, 0x8012, "Apple T2 MacBookPro15,3 (j780)" },
{ "iBridge2,8", "j140kap", 0x17, 0x8012, "Apple T2 MacBookAir8,1 (j140k)" },
{ "iBridge2,10", "j213ap", 0x18, 0x8012, "Apple T2 MacBookPro15,4 (j213)" },
{ "iBridge2,12", "j140aap", 0x37, 0x8012, "Apple T2 MacBookAir8,2 (j140a)" },
{ "iBridge2,14", "j152fap", 0x3A, 0x8012, "Apple T2 MacBookPro16,1 (j152f)" },
{ "iBridge2,15", "j230kap", 0x3F, 0x8012, "Apple T2 MacBookAir9,1 (j230k)" },
{ "iBridge2,16", "j214kap", 0x3E, 0x8012, "Apple T2 MacBookPro16,2 (j214k)" },
{ "iBridge2,19", "j185ap", 0x22, 0x8012, "Apple T2 iMac20,1 (j185)" },
{ "iBridge2,20", "j185fap", 0x23, 0x8012, "Apple T2 iMac20,2 (j185f)" },
{ "iBridge2,21", "j223ap", 0x3B, 0x8012, "Apple T2 MacBookPro16,3 (j223)" },
{ "iBridge2,22", "j215ap", 0x38, 0x8012, "Apple T2 MacBookPro16,4 (j215)" },
/* Apple Displays */
{ "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" },
/* Apple Vision Pro */
{ "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" },
{ "RealityDevice17,1", "n301aap", 0x42, 0x8142, "Apple Vision Pro (M5)" },
/* Private Cloud Compute Research Environment */
{ "iPhone99,11", "vresearch101ap", 0x90, 0xFE01, "iPhone 99,11" },
{ nullptr, nullptr, -1, -1, nullptr }
};
#include "../pongo_helper.h"
void get_recovery_info(char *sn, const char **ecid, const char **product_model, const char **product_name);
void get_name_from_product_type(const char *product_type, const char **name);
#endif // MAIN__DEVICE_INFO_H
#endif // WITH_GUI || WITH_TUI
+493
View File
@@ -0,0 +1,493 @@
/*
* palera1n - https://palera.in
*
* Copyright (C) 2026 palera1n team
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#if defined(WITH_GUI) || defined(WITH_TUI)
#include "event.hpp"
#include "../sequence.hpp"
#include "device_info.hpp"
#include <chrono>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <memory>
#include <condition_variable>
#include <cstring>
#include <iostream>
#if WITH_CIDERRAIN
# include <ciderra1n/log.h>
#else
# include <openra1n/utils.h>
# include <openra1n/shim.h>
#endif
#include "ra1ncovery/hotplug.hpp"
#include <idevice++/lockdown.hpp>
#include <idevice++/usbmuxd.hpp>
#include <idevice++/provider.hpp>
#if defined(__APPLE__)
using platform_service_t = io_service_t;
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
using platform_service_t = libusb_device*;
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
using platform_service_t = liteusb_handle_t*;
#endif
#ifndef __APPLE__
# define IO_OBJECT_NULL nullptr
#endif
namespace {
struct ManagedDevice {
DeviceState state{};
bool normalPresent = false;
bool recoveryPresent = false;
bool dfuPresent = false;
hotplug_handle_t handle{};
uint32_t device_id = 0;
std::unique_ptr<IdeviceFFI::UsbmuxdDevice> usbmuxd_device;
};
void release_handle(hotplug_handle_t& h) {
#ifdef __APPLE__
if (h.serv != IO_OBJECT_NULL) {
IOObjectRelease(h.serv);
h.serv = IO_OBJECT_NULL;
}
if (h.device) {
(*h.device)->Release(h.device);
h.device = nullptr;
}
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
if (h.serv != nullptr) {
libusb_unref_device(h.serv);
h.serv = nullptr;
}
h.device = nullptr;
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
h.handle = nullptr;
#endif
}
void retain_handle(const hotplug_handle_t& h) {
#ifdef __APPLE__
if (h.serv != IO_OBJECT_NULL) {
IOObjectRetain(h.serv);
}
if (h.device) (*h.device)->AddRef(h.device);
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
if (h.serv != nullptr) {
libusb_ref_device(h.serv);
}
#endif
}
template<auto FreeFn>
struct Releaser {
template<typename T>
void operator()(T* ptr) const { if (ptr) FreeFn(ptr); }
};
template<typename T, auto FreeFn>
using unique_c_ptr = std::unique_ptr<T, Releaser<FreeFn>>;
std::mutex g_mutex;
std::vector<DeviceStateCallback> g_callbacks;
std::unordered_map<uint64_t, ManagedDevice> g_devices;
uint64_t g_activeEcid = 0;
std::once_flag g_startOnce;
#ifndef __APPLE__
std::condition_variable g_recoveryCv;
bool g_recoveryRunning = false;
#endif
void dispatch_callbacks(const std::vector<DeviceStateCallback>& callbacks, const DeviceState& state) {
for (const auto& cb : callbacks) {
if (cb) cb(state);
}
}
DeviceState build_public_state_locked() {
DeviceState out{};
const auto total_devices = static_cast<uint32_t>(g_devices.size());
out.connected = false;
out.connectedDeviceCount = total_devices;
out.multipleDevices = total_devices > 1;
if (g_devices.empty()) {
g_activeEcid = 0;
return out;
}
auto it = g_devices.find(g_activeEcid);
if (it == g_devices.end()) {
it = g_devices.begin();
}
g_activeEcid = it->first;
out = it->second.state;
out.connected = true;
out.connectedDeviceCount = total_devices;
out.multipleDevices = total_devices > 1;
return out;
}
void publish_state() {
std::vector<DeviceStateCallback> callbacks;
DeviceState state;
{
std::lock_guard lock(g_mutex);
callbacks = g_callbacks;
state = build_public_state_locked();
}
dispatch_callbacks(callbacks, state);
}
bool lockdown_get_string(IdeviceFFI::Lockdown& client, const char* key, std::string& out) {
auto res = client.get_value(key, nullptr);
if (res.is_err()) return false;
plist_t raw_plist = res.unwrap();
if (!raw_plist) return false;
char* raw = nullptr;
plist_get_string_val(raw_plist, &raw);
if (raw) {
out = raw;
}
return !out.empty();
}
bool lockdown_get_uint(IdeviceFFI::Lockdown& client, const char* key, uint64_t& out) {
auto res = client.get_value(key, nullptr);
if (res.is_err()) return false;
plist_t plist = res.unwrap();
if (!plist) return false;
plist_get_uint_val(plist, &out);
return out != 0;
}
bool build_normal_state(const IdeviceFFI::UsbmuxdDevice& device, DeviceState& out) {
auto id_opt = device.get_id();
if (id_opt.is_none()) return false;
uint32_t device_id = id_opt.unwrap();
auto udid_opt = device.get_udid();
if (udid_opt.is_none()) return false;
std::string udid_str = udid_opt.unwrap();
auto prov_res = IdeviceFFI::Provider::usbmuxd_new(IdeviceFFI::UsbmuxdAddr::default_new(), 0, udid_str, device_id, "ra1ncovery");
if (prov_res.is_err()) return false;
auto prov = std::move(prov_res).unwrap();
auto lockdown_res = IdeviceFFI::Lockdown::connect(prov);
if (lockdown_res.is_err()) return false;
auto session = std::move(lockdown_res).unwrap();
auto pf_res = prov.get_pairing_file();
if (pf_res.is_ok()) {
(void)session.start_session(pf_res.unwrap());
}
out.connected = true;
out.mode = DeviceMode::Normal;
out.udid = udid_str;
lockdown_get_string(session, "ProductType", out.productType);
lockdown_get_string(session, "ProductVersion", out.productVersion);
lockdown_get_uint(session, "UniqueChipID", out.ecid);
for (int i = 0; irecv_devices[i].product_type; i++) {
if (irecv_devices[i].product_type == out.productType) {
out.displayName = irecv_devices[i].name;
break;
}
}
out.isSupported = SequenceIsSupported(out.productType);
return true;
}
static void handle_other_add(hotplug_handle_t handle, DeviceMode mode, bool supported) {
char serial[256] = {};
if (!get_usb_device_serial(handle, serial, sizeof(serial))) return;
const char *ecid_str = nullptr, *product_model = nullptr, *product_name = nullptr;
get_recovery_info(serial, &ecid_str, &product_model, &product_name);
uint64_t ecid = ecid_str ? strtoull(ecid_str, nullptr, 16) : 0;
if (ecid == 0) return;
{
std::lock_guard lock(g_mutex);
ManagedDevice& m = g_devices[ecid];
release_handle(m.handle);
m.handle = handle;
retain_handle(m.handle);
m.state.connected = true;
m.state.mode = mode;
m.state.ecid = ecid;
m.state.productType = product_model ? product_model : "";
m.state.productVersion.clear();
m.state.displayName = product_name ? product_name : "";
m.state.isSupported = supported;
m.recoveryPresent = (mode == DeviceMode::Recovery);
m.dfuPresent = (mode == DeviceMode::DFU);
}
publish_state();
}
static void remove_device(platform_service_t service, DeviceMode mode, uint32_t target_device_id = 0, const std::string& target_udid = "") {
bool changed = false;
{
std::lock_guard lock(g_mutex);
auto it = std::find_if(g_devices.begin(), g_devices.end(), [&](const auto& pair) {
if (service != IO_OBJECT_NULL) {
#if !defined(__APPLE__) && defined(WITH_CIDERRAIN)
return pair.second.handle.handle == service;
#else
return pair.second.handle.serv == service;
#endif
}
if (mode == DeviceMode::Normal) {
if (!target_udid.empty()) return pair.second.state.udid == target_udid;
if (target_device_id != 0) return pair.second.device_id == target_device_id;
return pair.second.normalPresent;
}
switch (mode) {
case DeviceMode::Recovery: return pair.second.recoveryPresent;
case DeviceMode::DFU: return pair.second.dfuPresent;
default: return false;
}
});
if (it != g_devices.end()) {
auto& [ecid, m] = *it;
if (mode == DeviceMode::Normal) {
m.normalPresent = false;
m.usbmuxd_device.reset();
} else if (mode == DeviceMode::Recovery) {
m.recoveryPresent = false;
} else if (mode == DeviceMode::DFU) {
m.dfuPresent = false;
}
if (!m.normalPresent && !m.recoveryPresent && !m.dfuPresent) {
release_handle(m.handle);
g_devices.erase(it);
}
changed = true;
}
}
if (changed) publish_state();
}
void usbmuxd_listener_worker() {
std::unordered_map<uint32_t, std::string> known_normal_devices;
for (;;) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::unordered_map<uint32_t, std::string> current_devices;
std::vector<IdeviceFFI::UsbmuxdDevice> usb_only_devices;
auto conn_res = IdeviceFFI::UsbmuxdConnection::default_new(0);
if (conn_res.is_ok()) {
auto devices_res = std::move(conn_res).unwrap().get_devices();
if (devices_res.is_ok()) {
auto current_dev_list = std::move(devices_res).unwrap();
usb_only_devices.reserve(current_dev_list.size());
for (auto& dev : current_dev_list) {
auto conn_type_opt = dev.get_connection_type();
if (conn_type_opt.is_none() || conn_type_opt.unwrap().to_string() != "USB") {
continue;
}
auto id_opt = dev.get_id();
auto udid_opt = dev.get_udid();
if (id_opt.is_some() && udid_opt.is_some()) {
current_devices[id_opt.unwrap()] = udid_opt.unwrap();
usb_only_devices.push_back(std::move(dev));
}
}
}
}
for (auto it = known_normal_devices.begin(); it != known_normal_devices.end();) {
if (current_devices.find(it->first) == current_devices.end()) {
uint32_t removed_id = it->first;
std::string removed_udid = it->second;
it = known_normal_devices.erase(it);
remove_device(IO_OBJECT_NULL, DeviceMode::Normal, removed_id, removed_udid);
} else {
++it;
}
}
for (auto& dev : usb_only_devices) {
uint32_t id = dev.get_id().unwrap();
if (known_normal_devices.find(id) == known_normal_devices.end()) {
DeviceState discovered;
if (build_normal_state(dev, discovered)) {
if (discovered.ecid == 0) continue;
known_normal_devices[id] = dev.get_udid().unwrap();
{
std::lock_guard lock(g_mutex);
ManagedDevice& m = g_devices[discovered.ecid];
release_handle(m.handle);
m.state = discovered;
m.normalPresent = true;
m.recoveryPresent = false;
m.dfuPresent = false;
m.device_id = id;
m.usbmuxd_device = std::make_unique<IdeviceFFI::UsbmuxdDevice>(std::move(dev));
}
publish_state();
}
}
}
}
}
static void hotplug_callback(hotplug_event_t event, hotplug_handle_t handle) {
#if !defined(__APPLE__) && defined(WITH_CIDERRAIN)
platform_service_t srv = handle.handle;
#else
platform_service_t srv = handle.serv;
#endif
switch (event) {
case HOTPLUG_EVENT_DFU_ADD: handle_other_add(handle, DeviceMode::DFU, false); break;
case HOTPLUG_EVENT_DFU_REMOVE: remove_device(srv, DeviceMode::DFU); break;
case HOTPLUG_EVENT_RECOVERY_ADD: handle_other_add(handle, DeviceMode::Recovery, true); break;
case HOTPLUG_EVENT_RECOVERY_REMOVE: remove_device(srv, DeviceMode::Recovery); break;
}
}
void recovery_listener_worker() {
if (!start_hotplug_monitoring(hotplug_callback)) return;
#ifdef __APPLE__
CFRunLoopRun();
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
std::unique_lock<std::mutex> lock(g_mutex);
g_recoveryRunning = true;
g_recoveryCv.wait(lock, [] { return !g_recoveryRunning; });
#endif
}
} // namespace
void register_device_state_callback(DeviceStateCallback cb) {
if (!cb) return;
DeviceState snapshot;
{
std::lock_guard lock(g_mutex);
g_callbacks.push_back(cb);
snapshot = build_public_state_locked();
}
cb(snapshot);
}
void ensure_device_event_system_started() {
std::call_once(g_startOnce, []() {
std::thread(usbmuxd_listener_worker).detach();
std::thread(recovery_listener_worker).detach();
});
}
bool enter_recovery() {
uint32_t device_id = 0;
std::string udid_str = "";
{
std::lock_guard lock(g_mutex);
if (g_activeEcid == 0) return false;
auto it = g_devices.find(g_activeEcid);
if (it == g_devices.end() || it->second.state.mode != DeviceMode::Normal) return false;
device_id = it->second.device_id;
udid_str = it->second.state.udid;
}
auto prov_res = IdeviceFFI::Provider::usbmuxd_new(IdeviceFFI::UsbmuxdAddr::default_new(), 0, udid_str, device_id, "ra1ncovery");
if (prov_res.is_err()) return false;
auto prov = std::move(prov_res).unwrap();
auto lockdown_res = IdeviceFFI::Lockdown::connect(prov);
if (lockdown_res.is_err()) return false;
auto session = std::move(lockdown_res).unwrap();
auto pf_res = prov.get_pairing_file();
if (pf_res.is_ok()) {
(void)session.start_session(pf_res.unwrap());
}
auto res = session.enter_recovery();
return res.is_ok();
}
void exit_recovery() {
hotplug_handle_t handle;
std::memset(&handle, 0, sizeof(handle));
{
std::lock_guard lock(g_mutex);
if (g_activeEcid == 0) return;
auto it = g_devices.find(g_activeEcid);
if (it == g_devices.end() || it->second.state.mode != DeviceMode::Recovery) return;
handle = it->second.handle;
}
exit_recovery(handle);
}
#endif // WITH_GUI || WITH_TUI
+72
View File
@@ -0,0 +1,72 @@
/*
* palera1n - https://palera.in
*
* Copyright (C) 2026 palera1n team
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#if defined(WITH_GUI) || defined(WITH_TUI)
#ifndef EVENT_H
#define EVENT_H
#include <cstdint>
#include <functional>
#include <string>
#ifdef WITH_GUI
# include <wx/wx.h>
#endif
enum class DeviceMode
{
None,
Normal,
Recovery,
DFU
};
struct DeviceState
{
bool connected = false;
bool isSupported = false;
bool multipleDevices = false;
DeviceMode mode = DeviceMode::None;
std::string productVersion;
std::string productType;
std::string displayName;
uint64_t ecid = 0;
std::string udid;
uint32_t connectedDeviceCount = 0;
};
using DeviceStateCallback = std::function<void(const DeviceState&)>;
void register_device_state_callback(DeviceStateCallback cb);
void ensure_device_event_system_started();
bool enter_recovery();
void exit_recovery();
#endif // EVENT_H
#endif // WITH_GUI || WITH_TUI
+419
View File
@@ -0,0 +1,419 @@
/*
* palera1n - https://palera.in
*
* Copyright (C) 2026 palera1n team
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#if defined(WITH_GUI) || defined(WITH_TUI)
#include "hotplug.hpp"
#include <vector>
#include <algorithm>
#include <string>
#include <thread>
#include <atomic>
#include <chrono>
constexpr int32_t APPLE_VID = 0x05AC;
constexpr int32_t PID_DFU = 0x1227;
constexpr int32_t PID_RECOVERY_1 = 0x1280;
constexpr int32_t PID_RECOVERY_2 = 0x1281;
constexpr int32_t PID_RECOVERY_3 = 0x1282;
constexpr int32_t PID_RECOVERY_4 = 0x1283;
static const std::vector<int32_t> TARGET_PIDS = {
PID_DFU, PID_RECOVERY_1, PID_RECOVERY_2, PID_RECOVERY_3, PID_RECOVERY_4
};
static hotplug_callback_t gUserCallback = nullptr;
static bool is_target_pid(int32_t pid) {
return std::find(TARGET_PIDS.begin(), TARGET_PIDS.end(), pid) != TARGET_PIDS.end();
}
static hotplug_event_t get_hotplug_event(int32_t pid, bool arrived) {
if (pid == PID_DFU) {
return arrived ? HOTPLUG_EVENT_DFU_ADD : HOTPLUG_EVENT_DFU_REMOVE;
}
return arrived ? HOTPLUG_EVENT_RECOVERY_ADD : HOTPLUG_EVENT_RECOVERY_REMOVE;
}
// MARK: IOKit
#if __APPLE__
# include <CoreFoundation/CoreFoundation.h>
static IONotificationPortRef gNotifyPort = nullptr;
static std::vector<io_iterator_t> gIterators;
static CFRunLoopSourceRef gRunLoopSource = nullptr;
static void process_device_iterator(void *refcon, io_iterator_t iterator, bool arrived) {
int32_t pid = static_cast<int32_t>(reinterpret_cast<uintptr_t>(refcon));
io_service_t device;
while ((device = IOIteratorNext(iterator))) {
if (gUserCallback) {
hotplug_handle_t handle = { device, nullptr };
if (arrived) {
IOCFPlugInInterface **plugIn = nullptr;
SInt32 score = 0;
if (IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugIn, &score) == kIOReturnSuccess && plugIn) {
(*plugIn)->QueryInterface(plugIn, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&handle.device);
IODestroyPlugInInterface(plugIn);
}
}
gUserCallback(get_hotplug_event(pid, arrived), handle);
if (handle.device) {
(*handle.device)->Release(handle.device);
}
}
IOObjectRelease(device);
}
}
static void device_added_callback(void *refcon, io_iterator_t iterator) { process_device_iterator(refcon, iterator, true); }
static void device_removed_callback(void *refcon, io_iterator_t iterator) { process_device_iterator(refcon, iterator, false); }
static bool register_pid_node(int32_t pid) {
void* refcon = reinterpret_cast<void*>(static_cast<uintptr_t>(pid));
CFNumberRef vid_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &APPLE_VID);
CFNumberRef pid_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pid);
auto setup_notification = [&](const char* notification_type, void (*cb)(void*, io_iterator_t)) -> bool {
CFMutableDictionaryRef match = IOServiceMatching(kIOUSBDeviceClassName);
CFDictionarySetValue(match, CFSTR(kUSBVendorID), vid_num);
CFDictionarySetValue(match, CFSTR(kUSBProductID), pid_num);
io_iterator_t iter = IO_OBJECT_NULL;
if (IOServiceAddMatchingNotification(gNotifyPort, notification_type, match, cb, refcon, &iter) == KERN_SUCCESS && iter != IO_OBJECT_NULL) {
cb(refcon, iter);
gIterators.push_back(iter);
return true;
}
return false;
};
bool ok = setup_notification(kIOFirstMatchNotification, device_added_callback) &&
setup_notification(kIOTerminatedNotification, device_removed_callback);
CFRelease(vid_num);
CFRelease(pid_num);
return ok;
}
bool start_hotplug_monitoring(hotplug_callback_t callback) {
if (gNotifyPort != nullptr) return true;
if (!callback) return false;
gUserCallback = callback;
gNotifyPort = IONotificationPortCreate(MACH_PORT_NULL);
if (!gNotifyPort) return false;
gRunLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
CFRunLoopAddSource(CFRunLoopGetCurrent(), gRunLoopSource, kCFRunLoopDefaultMode);
for (int32_t pid : TARGET_PIDS) register_pid_node(pid);
return true;
}
void stop_hotplug_monitoring(void) {
if (!gNotifyPort) return;
if (gRunLoopSource) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), gRunLoopSource, kCFRunLoopDefaultMode);
gRunLoopSource = nullptr;
}
for (io_iterator_t iter : gIterators) {
if (iter != IO_OBJECT_NULL) IOObjectRelease(iter);
}
gIterators.clear();
IONotificationPortDestroy(gNotifyPort);
gNotifyPort = nullptr;
gUserCallback = nullptr;
}
bool get_usb_device_serial(hotplug_handle_t handle, char* buffer, size_t maxLen) {
if (handle.serv == IO_OBJECT_NULL || !buffer || maxLen == 0) return false;
CFTypeRef serialProp = IORegistryEntryCreateCFProperty(handle.serv, CFSTR("USB Serial Number"), kCFAllocatorDefault, 0);
if (!serialProp) serialProp = IORegistryEntryCreateCFProperty(handle.serv, CFSTR("Serial Number"), kCFAllocatorDefault, 0);
if (!serialProp) return false;
bool success = false;
if (CFGetTypeID(serialProp) == CFStringGetTypeID()) {
success = CFStringGetCString(static_cast<CFStringRef>(serialProp), buffer, static_cast<CFIndex>(maxLen), kCFStringEncodingUTF8);
}
CFRelease(serialProp);
return success;
}
static bool send_usb_control_request(hotplug_handle_t handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, void* data, uint16_t wLength) {
if (!handle.device || (*handle.device)->USBDeviceOpen(handle.device) != kIOReturnSuccess) return false;
IOUSBDevRequest request = { bmRequestType, bRequest, wValue, wIndex, wLength, data };
kern_return_t kr = (*handle.device)->DeviceRequest(handle.device, &request);
(*handle.device)->USBDeviceClose(handle.device);
return (kr == kIOReturnSuccess);
}
// MARK: Libusb
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
static libusb_context *gCtx = nullptr;
static std::vector<libusb_hotplug_callback_handle> gCallbackHandles;
static std::thread gEventThread;
static std::atomic<bool> gRunning{false};
static int LIBUSB_CALL hotplug_callback(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) {
(void)ctx; (void)user_data;
struct libusb_device_descriptor desc;
if (libusb_get_device_descriptor(dev, &desc) != LIBUSB_SUCCESS || desc.idVendor != APPLE_VID || !is_target_pid(desc.idProduct)) return 0;
if (gUserCallback) {
libusb_ref_device(dev);
std::thread([dev, event, desc]() {
hotplug_handle_t handle = { dev, nullptr };
bool arrived = (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
gUserCallback(get_hotplug_event(desc.idProduct, arrived), handle);
libusb_unref_device(dev);
}).detach();
}
return 0;
}
bool start_hotplug_monitoring(hotplug_callback_t callback) {
if (gCtx != nullptr) return true;
if (!callback || libusb_init(&gCtx) != LIBUSB_SUCCESS) return false;
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
libusb_exit(gCtx);
gCtx = nullptr;
return false;
}
gUserCallback = callback;
gRunning = true;
gEventThread = std::thread([]() {
while (gRunning) libusb_handle_events_completed(gCtx, nullptr);
});
libusb_hotplug_callback_handle cb_handle;
int rc = libusb_hotplug_register_callback(
gCtx, static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
LIBUSB_HOTPLUG_ENUMERATE, APPLE_VID, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
hotplug_callback, nullptr, &cb_handle
);
if (rc == LIBUSB_SUCCESS) {
gCallbackHandles.push_back(cb_handle);
return true;
}
stop_hotplug_monitoring();
return false;
}
void stop_hotplug_monitoring(void) {
if (!gCtx) return;
for (auto cb_handle : gCallbackHandles) libusb_hotplug_deregister_callback(gCtx, cb_handle);
gCallbackHandles.clear();
gRunning = false;
libusb_unref_device(nullptr);
if (gEventThread.joinable()) gEventThread.join();
libusb_exit(gCtx);
gCtx = nullptr;
gUserCallback = nullptr;
}
bool get_usb_device_serial(hotplug_handle_t handle, char* buffer, size_t maxLen) {
if (!handle.serv || !buffer || maxLen == 0) return false;
struct libusb_device_descriptor desc;
if (libusb_get_device_descriptor(handle.serv, &desc) != LIBUSB_SUCCESS || desc.iSerialNumber == 0) return false;
libusb_device_handle *dev_handle = nullptr;
if (libusb_open(handle.serv, &dev_handle) != LIBUSB_SUCCESS) return false;
int rc = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, reinterpret_cast<unsigned char*>(buffer), static_cast<int>(maxLen));
libusb_close(dev_handle);
return (rc > 0);
}
static bool send_usb_control_request(hotplug_handle_t handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, void* data, uint16_t wLength) {
if (!handle.serv) return false;
libusb_device_handle *dev_handle = handle.device;
bool must_close = false;
if (!dev_handle) {
if (libusb_open(handle.serv, &dev_handle) != LIBUSB_SUCCESS) return false;
must_close = true;
}
int transferred = libusb_control_transfer(
dev_handle, bmRequestType, bRequest, wValue, wIndex,
reinterpret_cast<unsigned char*>(data), wLength, 1000
);
if (must_close && dev_handle) {
libusb_close(dev_handle);
}
return transferred >= 0;
}
// MARK: Liteusb
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
# include <liteusb.h>
# include <map>
# include <set>
struct PersistentDeviceContext {
liteusb_device_t device;
liteusb_handle_t* session = nullptr;
};
static std::thread gHotplugThread;
static std::atomic<bool> gHotplugRunning{false};
static std::map<std::string, std::unique_ptr<PersistentDeviceContext>> gConnectedDevices;
static void hotplug_polling_worker() {
liteusb_init(LITEUSB_FLAG_NONE);
while (gHotplugRunning) {
liteusb_query_t *query = nullptr;
if (liteusb_query_devices(&query) != LITEUSB_ERR_SUCCESS || !query) {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
continue;
}
std::set<std::string> active_paths;
for (uint32_t i = 0; i < query->count; i++) {
const auto& dev = query->list[i];
if (dev.vid != APPLE_VID || !is_target_pid(dev.pid) || !dev.sysfs_path) continue;
std::string path(dev.sysfs_path);
active_paths.insert(path);
if (gConnectedDevices.find(path) == gConnectedDevices.end()) {
auto ctx = std::make_unique<PersistentDeviceContext>();
ctx->device = dev;
if (liteusb_open_handle(&ctx->device, &ctx->session) == LITEUSB_ERR_SUCCESS) {
auto* ctx_ptr = ctx.get();
gConnectedDevices[path] = std::move(ctx);
if (gUserCallback) {
gUserCallback(get_hotplug_event(ctx_ptr->device.pid, true), { reinterpret_cast<liteusb_handle_t*>(ctx_ptr) });
}
}
}
}
for (auto it = gConnectedDevices.begin(); it != gConnectedDevices.end(); ) {
if (active_paths.find(it->first) == active_paths.end()) {
if (gUserCallback) {
gUserCallback(get_hotplug_event(it->second->device.pid, false), { reinterpret_cast<liteusb_handle_t*>(it->second.get()) });
}
if (it->second->session) liteusb_close_handle(it->second->session);
it = gConnectedDevices.erase(it);
} else {
++it;
}
}
liteusb_release_query(query);
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
}
bool start_hotplug_monitoring(hotplug_callback_t callback) {
if (gHotplugRunning) return true;
if (!callback) return false;
gUserCallback = callback;
gHotplugRunning = true;
gHotplugThread = std::thread(hotplug_polling_worker);
return true;
}
void stop_hotplug_monitoring(void) {
if (!gHotplugRunning) return;
gHotplugRunning = false;
if (gHotplugThread.joinable()) gHotplugThread.join();
for (const auto& [path, ctx] : gConnectedDevices) {
if (ctx->session) liteusb_close_handle(ctx->session);
}
gConnectedDevices.clear();
gUserCallback = nullptr;
}
bool get_usb_device_serial(hotplug_handle_t handle, char* buffer, size_t maxLen) {
auto* ctx = reinterpret_cast<PersistentDeviceContext*>(handle.handle);
if (!ctx || !ctx->session || !buffer || maxLen == 0) return false;
char *serial_num = nullptr;
bool success = false;
if (liteusb_get_device_serial_num(ctx->session, &serial_num) == LITEUSB_ERR_SUCCESS && serial_num) {
success = ((size_t)snprintf(buffer, maxLen, "%s", serial_num) < maxLen);
free(serial_num);
}
return success;
}
static bool send_usb_control_request(hotplug_handle_t handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, void* data, uint16_t wLength) {
auto* ctx = reinterpret_cast<PersistentDeviceContext*>(handle.handle);
if (!ctx || !ctx->session) return false;
uint32_t transferred = 0;
return liteusb_immediate_ctrl_transfer(
ctx->session, bmRequestType, bRequest, wValue, wIndex,
reinterpret_cast<uint8_t*>(data), wLength, 5000, &transferred
) == LITEUSB_ERR_SUCCESS;
}
#endif
void exit_recovery(hotplug_handle_t handle) {
char cmd1[] = "setenv auto-boot true";
char cmd2[] = "saveenv";
char cmd3[] = "reboot";
send_usb_control_request(handle, 0x40, 0, 0, 0, cmd1, sizeof(cmd1));
send_usb_control_request(handle, 0x40, 0, 0, 0, cmd2, sizeof(cmd2));
send_usb_control_request(handle, 0x40, 0, 0, 0, cmd3, sizeof(cmd3));
}
#endif // WITH_GUI || WITH_TUI
+82
View File
@@ -0,0 +1,82 @@
/*
* palera1n - https://palera.in
*
* Copyright (C) 2026 palera1n team
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#if defined(WITH_GUI) || defined(WITH_TUI)
#ifndef MAIN__HOTPLUG_HPP
#define MAIN__HOTPLUG_HPP
#include <stdbool.h>
#include <stdint.h>
#ifdef __APPLE__
# include <IOKit/IOKitLib.h>
# include <IOKit/IOCFPlugIn.h>
# include <IOKit/usb/IOUSBLib.h>
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
# include <libusb-1.0/libusb.h>
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
# include <liteusb.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
HOTPLUG_EVENT_DFU_ADD,
HOTPLUG_EVENT_DFU_REMOVE,
HOTPLUG_EVENT_RECOVERY_ADD,
HOTPLUG_EVENT_RECOVERY_REMOVE
} hotplug_event_t;
typedef struct {
#ifdef __APPLE__
io_service_t serv;
IOUSBDeviceInterface320 **device;
#elif !defined(__APPLE__) && !defined(WITH_CIDERRAIN)
libusb_device *serv;
libusb_device_handle *device;
#elif !defined(__APPLE__) && defined(WITH_CIDERRAIN)
liteusb_handle_t *handle;
#endif
} hotplug_handle_t;
typedef void (*hotplug_callback_t)(hotplug_event_t event, hotplug_handle_t handle);
bool start_hotplug_monitoring(hotplug_callback_t callback);
void stop_hotplug_monitoring(void);
bool get_usb_device_serial(hotplug_handle_t handle, char* buffer, size_t maxLen);
void exit_recovery(hotplug_handle_t handle);
#ifdef __cplusplus
}
#endif
#endif // MAIN__HOTPLUG_HPP
#endif // WITH_GUI || WITH_TUI