mirror of
https://github.com/palera1n/palera1n.git
synced 2026-10-25 02:29:49 +01:00
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:
128 files changed
+12054
-3114
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user