mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge remote-tracking branch 'ul/dev' into mntm-dev --nobuild
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -63,8 +63,11 @@
|
|||||||
- UI: Dictionary attack scene and menu options
|
- UI: Dictionary attack scene and menu options
|
||||||
- OFW: FeliCa Service Directory Traverse + Dump All Unencrypted-Readable Services' Blocks (by @zinongli)
|
- OFW: FeliCa Service Directory Traverse + Dump All Unencrypted-Readable Services' Blocks (by @zinongli)
|
||||||
- OFW: FeliCa Emulation Handle certain Polling commands in firmware (by @dogtopus)
|
- OFW: FeliCa Emulation Handle certain Polling commands in firmware (by @dogtopus)
|
||||||
|
- OFW: FeliCa Dump All Systems (by @zinongli)
|
||||||
- OFW: Amusement IC Card Parser for FeliCa Lite & Lite-S (by @zinongli)
|
- OFW: Amusement IC Card Parser for FeliCa Lite & Lite-S (by @zinongli)
|
||||||
- OFW: MFC 1k Banapass Parser (by @zinongli)
|
- OFW: MFC 1k Banapass Parser (by @zinongli)
|
||||||
|
- UL: Returning fix for reading PWD locked MFUL (by @mishamyte)
|
||||||
|
- UL: Added UL-C keys to the dictionary (by @mishamyte)
|
||||||
- Add MIFARE Classic "Show Keys" UI (#473 by @aaronjamt)
|
- Add MIFARE Classic "Show Keys" UI (#473 by @aaronjamt)
|
||||||
- SubGHz:
|
- SubGHz:
|
||||||
- UL: Roger (static 28 bit) with add manually support (by @xMasterX & @mishamyte)
|
- UL: Roger (static 28 bit) with add manually support (by @xMasterX & @mishamyte)
|
||||||
@@ -77,6 +80,9 @@
|
|||||||
- UL: Add ZKTeco 430.5 MHz add manually support (by @xMasterX)
|
- UL: Add ZKTeco 430.5 MHz add manually support (by @xMasterX)
|
||||||
- UL: Add Elplast 18bit static code protocol (hello Hackcat ^_^)
|
- UL: Add Elplast 18bit static code protocol (hello Hackcat ^_^)
|
||||||
- UL: Try to decode BFT (2 buttons remotes only) on the fly in regular Read mode (by @xMasterX)
|
- UL: Try to decode BFT (2 buttons remotes only) on the fly in regular Read mode (by @xMasterX)
|
||||||
|
- UL: Add support for Came Atomo TOP44RBN remotes (by @xMasterX & @mishamyte)
|
||||||
|
- UL: Add IL-100 Smart support for Add manually (by @xMasterX)
|
||||||
|
- UL: Add experimental counter overflow mode (OFEX), replicates how some key duplicators work, DO NOT USE if you don't know what you are doing, it will reset your counter value! (by @xMasterX)
|
||||||
- RFID:
|
- RFID:
|
||||||
- Support writing Securakey, Jablotron and FDX-B to EM4305 cards (#434 by @jamisonderek)
|
- Support writing Securakey, Jablotron and FDX-B to EM4305 cards (#434 by @jamisonderek)
|
||||||
- OFW: Show ISO-3166 Country Names For Pet Chips (by @zinongli)
|
- OFW: Show ISO-3166 Country Names For Pet Chips (by @zinongli)
|
||||||
@@ -106,6 +112,7 @@
|
|||||||
- Apps:
|
- Apps:
|
||||||
- XERO: MFKey: Key recovery is 20% faster, new write buffering of Static Encrypted Nested key candidates performs recovery 70x faster (by @noproto)
|
- XERO: MFKey: Key recovery is 20% faster, new write buffering of Static Encrypted Nested key candidates performs recovery 70x faster (by @noproto)
|
||||||
- UL: Sub-GHz Remote: Add possibility to use custom buttons (by @MrLego8-9)
|
- UL: Sub-GHz Remote: Add possibility to use custom buttons (by @MrLego8-9)
|
||||||
|
- UL: BT/USB Remote: PTT global zoom and google meet shortcuts for MacOS (by @hryamzik)
|
||||||
- Asteroids: Bugfixes, title screen, Drone Buddy power-up (by @SimplyMinimal)
|
- Asteroids: Bugfixes, title screen, Drone Buddy power-up (by @SimplyMinimal)
|
||||||
- Combo Cracker: Allow press and hold to change values, add tutorial (by @TAxelAnderson), support alphabetic combination locks (by @henrygab)
|
- Combo Cracker: Allow press and hold to change values, add tutorial (by @TAxelAnderson), support alphabetic combination locks (by @henrygab)
|
||||||
- ESP Flasher: Bump Marauder 1.8.4 (by @justcallmekoko), add C5 support (by @Play2BReal), more reliable bootloader mode on SWCLK (by @WillyJL)
|
- ESP Flasher: Bump Marauder 1.8.4 (by @justcallmekoko), add C5 support (by @Play2BReal), more reliable bootloader mode on SWCLK (by @WillyJL)
|
||||||
@@ -131,11 +138,14 @@
|
|||||||
- UL: Add 868.46 MHz to default subghz freqs list (by @xMasterX)
|
- UL: Add 868.46 MHz to default subghz freqs list (by @xMasterX)
|
||||||
- UL: Reduce less popular freqs in default hopper preset, make it faster (by @xMasterX)
|
- UL: Reduce less popular freqs in default hopper preset, make it faster (by @xMasterX)
|
||||||
- UL: Tune Linear (add better EZCode support), Dickert MAHS decoders (by @xMasterX)
|
- UL: Tune Linear (add better EZCode support), Dickert MAHS decoders (by @xMasterX)
|
||||||
|
- UL: Some fixes and improvements to Honeywell Sec protocol (by @xMasterX)
|
||||||
- Infrared:
|
- Infrared:
|
||||||
- OFW: Add an old JVC model to universal remotes (by @zgracem)
|
- OFW: Add an old JVC model to universal remotes (by @zgracem)
|
||||||
- OFW: Add Daikin FTXN25LV1B9 and Toyotomi KTN22-12R32 to universal remotes (by @minchogaydarov)
|
- OFW: Add Daikin FTXN25LV1B9 and Toyotomi KTN22-12R32 to universal remotes (by @minchogaydarov)
|
||||||
- OFW: BLE: Improved pairing security (by @hedger)
|
- OFW: BLE: Improved pairing security (by @hedger)
|
||||||
- JS: Expose button event type in `gui/widget` button callback, see breaking changes above (by @WillyJL)
|
- JS: Expose button event type in `gui/widget` button callback, see breaking changes above (by @WillyJL)
|
||||||
|
- OFW: NFC: Expose nfc_common.h (by @zinongli)
|
||||||
|
- OFW: GUI: Store View by value inViewStack to save memory (by @CookiePLMonster)
|
||||||
- UL: Docs: Update Sub-GHz DoorHan programming instructions (by @li0ard)
|
- UL: Docs: Update Sub-GHz DoorHan programming instructions (by @li0ard)
|
||||||
|
|
||||||
### Fixed:
|
### Fixed:
|
||||||
|
|||||||
@@ -102,19 +102,21 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
|
|||||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||||
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||||
} else {
|
} else {
|
||||||
bool all_unlocked = data->blocks_read == data->blocks_total;
|
if(data->workflow_type == FelicaLite) {
|
||||||
furi_string_cat_printf(
|
bool all_unlocked = data->blocks_read == data->blocks_total;
|
||||||
temp_str,
|
furi_string_cat_printf(
|
||||||
"\e#%s\n",
|
temp_str,
|
||||||
all_unlocked ? "All Blocks Are Unlocked" : "Some Blocks Are Locked");
|
"\e#%s\n",
|
||||||
nfc_render_felica_idm(data, NfcProtocolFormatTypeShort, temp_str);
|
all_unlocked ? "All Blocks Are Unlocked" : "Some Blocks Are Locked");
|
||||||
uint8_t* ck_data = instance->felica_auth->card_key.data;
|
nfc_render_felica_idm(data, NfcProtocolFormatTypeShort, temp_str);
|
||||||
furi_string_cat_printf(temp_str, "Key:");
|
uint8_t* ck_data = instance->felica_auth->card_key.data;
|
||||||
for(uint8_t i = 0; i < 7; i++) {
|
furi_string_cat_printf(temp_str, "Key:");
|
||||||
furi_string_cat_printf(temp_str, " %02X", ck_data[i]);
|
for(uint8_t i = 0; i < 7; i++) {
|
||||||
if(i == 6) furi_string_cat_printf(temp_str, "...");
|
furi_string_cat_printf(temp_str, " %02X", ck_data[i]);
|
||||||
|
if(i == 6) furi_string_cat_printf(temp_str, "...");
|
||||||
|
}
|
||||||
|
nfc_render_felica_blocks_count(data, temp_str, false);
|
||||||
}
|
}
|
||||||
nfc_render_felica_blocks_count(data, temp_str, false);
|
|
||||||
}
|
}
|
||||||
felica_auth_reset(instance->felica_auth);
|
felica_auth_reset(instance->felica_auth);
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,10 @@ void nfc_render_felica_blocks_count(
|
|||||||
bool render_auth_notification) {
|
bool render_auth_notification) {
|
||||||
if(data->workflow_type == FelicaLite) {
|
if(data->workflow_type == FelicaLite) {
|
||||||
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||||
|
|
||||||
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||||
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||||
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||||
}
|
}
|
||||||
} else if(data->workflow_type == FelicaStandard) {
|
|
||||||
furi_string_cat_printf(
|
|
||||||
str, "Public blocks Read: %lu", simple_array_get_count(data->public_blocks));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,11 +50,7 @@ void nfc_render_felica_info(
|
|||||||
}
|
}
|
||||||
|
|
||||||
furi_string_cat_printf(str, "\n");
|
furi_string_cat_printf(str, "\n");
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(str, "Systems found: %lu \n", simple_array_get_count(data->systems));
|
||||||
str,
|
|
||||||
"Services found: %lu \nAreas found: %lu\n",
|
|
||||||
simple_array_get_count(data->services),
|
|
||||||
simple_array_get_count(data->areas));
|
|
||||||
|
|
||||||
nfc_render_felica_blocks_count(data, str, true);
|
nfc_render_felica_blocks_count(data, str, true);
|
||||||
}
|
}
|
||||||
@@ -136,9 +128,9 @@ void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* s
|
|||||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) {
|
void nfc_more_info_render_felica_dir(const FelicaSystem* system, FuriString* str) {
|
||||||
const size_t area_count = simple_array_get_count(data->areas);
|
const size_t area_count = simple_array_get_count(system->areas);
|
||||||
const size_t service_count = simple_array_get_count(data->services);
|
const size_t service_count = simple_array_get_count(system->services);
|
||||||
|
|
||||||
furi_string_cat_printf(str, "\e#Directory Tree:\n");
|
furi_string_cat_printf(str, "\e#Directory Tree:\n");
|
||||||
|
|
||||||
@@ -150,11 +142,12 @@ void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) {
|
|||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
str, "::: ... are readable services\n||| ... are locked services\n");
|
str, "::: ... are readable services\n||| ... are locked services\n");
|
||||||
}
|
}
|
||||||
felica_write_directory_tree(data, str);
|
felica_write_directory_tree(system, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfc_more_info_render_felica_blocks(
|
void nfc_more_info_render_felica_blocks(
|
||||||
const FelicaData* data,
|
const FelicaData* data,
|
||||||
|
const FelicaSystem* system,
|
||||||
FuriString* str,
|
FuriString* str,
|
||||||
const uint16_t service_code_key) {
|
const uint16_t service_code_key) {
|
||||||
furi_string_cat_printf(str, "\n");
|
furi_string_cat_printf(str, "\n");
|
||||||
@@ -190,9 +183,9 @@ void nfc_more_info_render_felica_blocks(
|
|||||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||||
|
|
||||||
} else if(data->workflow_type == FelicaStandard) {
|
} else if(data->workflow_type == FelicaStandard) {
|
||||||
uint32_t public_blocks_count = simple_array_get_count(data->public_blocks);
|
uint32_t public_blocks_count = simple_array_get_count(system->public_blocks);
|
||||||
for(size_t i = 0; i < public_blocks_count; i++) {
|
for(size_t i = 0; i < public_blocks_count; i++) {
|
||||||
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
|
FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i);
|
||||||
if(public_block->service_code != service_code_key) {
|
if(public_block->service_code != service_code_key) {
|
||||||
continue; // Skip blocks not matching the requested service code
|
continue; // Skip blocks not matching the requested service code
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ void nfc_render_felica_idm(
|
|||||||
NfcProtocolFormatType format_type,
|
NfcProtocolFormatType format_type,
|
||||||
FuriString* str);
|
FuriString* str);
|
||||||
|
|
||||||
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str);
|
void nfc_more_info_render_felica_dir(const FelicaSystem* system, FuriString* str);
|
||||||
|
|
||||||
void nfc_more_info_render_felica_blocks(
|
void nfc_more_info_render_felica_blocks(
|
||||||
const FelicaData* data,
|
const FelicaData* data,
|
||||||
|
const FelicaSystem* system,
|
||||||
FuriString* str,
|
FuriString* str,
|
||||||
const uint16_t service_code_key);
|
const uint16_t service_code_key);
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
49454D4B41455242214E4143554F5900
|
49454D4B41455242214E4143554F5900
|
||||||
# Modified Semnox Key (IEMKAERB!NACUOYF)
|
# Modified Semnox Key (IEMKAERB!NACUOYF)
|
||||||
49454D4B41455242214E4143554F5946
|
49454D4B41455242214E4143554F5946
|
||||||
|
# iKey UL-C Magic Tag Key
|
||||||
|
921AAC1654B8D83A669A9012AE7C1222
|
||||||
|
# Kyiv NKS Vizit Key
|
||||||
|
D2C8C2CEC8CCC420204E4042544E5846
|
||||||
|
|
||||||
# Mix of Proxmark and ChameleonMiniLiveDebugger
|
# Mix of Proxmark and ChameleonMiniLiveDebugger
|
||||||
00000000000000000000000000000000
|
00000000000000000000000000000000
|
||||||
|
|||||||
@@ -71,5 +71,6 @@ ADD_SCENE(nfc, slix_unlock, SlixUnlock)
|
|||||||
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)
|
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)
|
||||||
|
|
||||||
ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)
|
ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)
|
||||||
|
ADD_SCENE(nfc, felica_system, FelicaSystem)
|
||||||
|
|
||||||
ADD_SCENE(nfc, generate_info, GenerateInfo)
|
ADD_SCENE(nfc, generate_info, GenerateInfo)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexDirectory,
|
|
||||||
SubmenuIndexDynamic, // dynamic indices start here
|
SubmenuIndexDynamic, // dynamic indices start here
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -21,33 +20,22 @@ void nfc_scene_felica_more_info_on_enter(void* context) {
|
|||||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
|
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
|
||||||
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
||||||
|
|
||||||
submenu_add_item(
|
|
||||||
submenu,
|
|
||||||
"Directory",
|
|
||||||
SubmenuIndexDirectory,
|
|
||||||
nfc_protocol_support_common_submenu_callback,
|
|
||||||
nfc);
|
|
||||||
|
|
||||||
FuriString* label = furi_string_alloc();
|
|
||||||
|
|
||||||
switch(data->workflow_type) {
|
switch(data->workflow_type) {
|
||||||
case FelicaLite:
|
case FelicaLite:
|
||||||
furi_string_printf(label, "All blocks");
|
widget_reset(nfc->widget);
|
||||||
submenu_add_item(
|
FuriString* temp_str = furi_string_alloc();
|
||||||
submenu,
|
nfc_more_info_render_felica_lite_dump(data, temp_str);
|
||||||
furi_string_get_cstr(label),
|
widget_add_text_scroll_element(nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||||
SubmenuIndexDynamic,
|
furi_string_free(temp_str);
|
||||||
nfc_protocol_support_common_submenu_callback,
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
nfc);
|
return;
|
||||||
break;
|
break;
|
||||||
case FelicaStandard:
|
case FelicaStandard:
|
||||||
for(uint32_t i = 0; i < simple_array_get_count(data->services); ++i) {
|
FuriString* label = furi_string_alloc();
|
||||||
const FelicaService* service = simple_array_cget(data->services, i);
|
|
||||||
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1;
|
for(uint32_t i = 0; i < simple_array_get_count(data->systems); ++i) {
|
||||||
if(!is_public) {
|
const FelicaSystem* system = simple_array_cget(data->systems, i);
|
||||||
continue;
|
furi_string_printf(label, "System %04X", system->system_code);
|
||||||
}
|
|
||||||
furi_string_printf(label, "Readable serv %04X", service->code);
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
furi_string_get_cstr(label),
|
furi_string_get_cstr(label),
|
||||||
@@ -55,13 +43,12 @@ void nfc_scene_felica_more_info_on_enter(void* context) {
|
|||||||
nfc_protocol_support_common_submenu_callback,
|
nfc_protocol_support_common_submenu_callback,
|
||||||
nfc);
|
nfc);
|
||||||
}
|
}
|
||||||
|
furi_string_free(label);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_string_free(label);
|
|
||||||
|
|
||||||
if(state >= FelicaMoreInfoStateItem) {
|
if(state >= FelicaMoreInfoStateItem) {
|
||||||
submenu_set_selected_item(
|
submenu_set_selected_item(
|
||||||
nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic);
|
nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic);
|
||||||
@@ -78,49 +65,16 @@ bool nfc_scene_felica_more_info_on_event(void* context, SceneManagerEvent event)
|
|||||||
|
|
||||||
const uint32_t state =
|
const uint32_t state =
|
||||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
|
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
|
||||||
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SubmenuIndexDirectory) {
|
const uint32_t index = event.event - SubmenuIndexDynamic;
|
||||||
FuriString* temp_str = furi_string_alloc();
|
|
||||||
nfc_more_info_render_felica_dir(data, temp_str);
|
|
||||||
|
|
||||||
widget_add_text_scroll_element(
|
scene_manager_set_scene_state(
|
||||||
nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + index);
|
||||||
furi_string_free(temp_str);
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, index << 4);
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaSystem);
|
||||||
|
consumed = true;
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
nfc->scene_manager,
|
|
||||||
NfcSceneFelicaMoreInfo,
|
|
||||||
FelicaMoreInfoStateItem + SubmenuIndexDirectory);
|
|
||||||
consumed = true;
|
|
||||||
} else {
|
|
||||||
const uint16_t service_ind = event.event - 1; // offset the three enums above
|
|
||||||
|
|
||||||
text_box_reset(nfc->text_box);
|
|
||||||
furi_string_reset(nfc->text_box_store);
|
|
||||||
|
|
||||||
switch(data->workflow_type) {
|
|
||||||
case FelicaLite:
|
|
||||||
nfc_more_info_render_felica_lite_dump(data, nfc->text_box_store);
|
|
||||||
break;
|
|
||||||
case FelicaStandard:
|
|
||||||
const FelicaService* service = simple_array_cget(data->services, service_ind);
|
|
||||||
furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code);
|
|
||||||
nfc_more_info_render_felica_blocks(data, nfc->text_box_store, service->code);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
furi_string_set_str(nfc->text_box_store, "IC type not implemented yet");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
text_box_set_font(nfc->text_box, TextBoxFontHex);
|
|
||||||
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
|
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + event.event);
|
|
||||||
consumed = true;
|
|
||||||
}
|
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
if(state >= FelicaMoreInfoStateItem) {
|
if(state >= FelicaMoreInfoStateItem) {
|
||||||
widget_reset(nfc->widget);
|
widget_reset(nfc->widget);
|
||||||
|
|||||||
114
applications/main/nfc/scenes/nfc_scene_felica_system.c
Normal file
114
applications/main/nfc/scenes/nfc_scene_felica_system.c
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
|
||||||
|
#include "../helpers/protocol_support/felica/felica_render.h"
|
||||||
|
|
||||||
|
enum SubmenuIndex {
|
||||||
|
SubmenuIndexDirectory,
|
||||||
|
SubmenuIndexDynamic, // dynamic indices start here
|
||||||
|
};
|
||||||
|
|
||||||
|
static void nfc_scene_felica_system_submenu_callback(void* context, uint32_t index) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_felica_system_on_enter(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
Submenu* submenu = nfc->submenu;
|
||||||
|
submenu_reset(nfc->submenu);
|
||||||
|
|
||||||
|
const uint32_t system_index =
|
||||||
|
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaSystem) >> 4;
|
||||||
|
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu, "Directory", SubmenuIndexDirectory, nfc_scene_felica_system_submenu_callback, nfc);
|
||||||
|
|
||||||
|
FuriString* label = furi_string_alloc();
|
||||||
|
|
||||||
|
const FelicaSystem* system = simple_array_cget(data->systems, system_index);
|
||||||
|
for(uint32_t i = 0; i < simple_array_get_count(system->services); ++i) {
|
||||||
|
const FelicaService* service = simple_array_cget(system->services, i);
|
||||||
|
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1;
|
||||||
|
if(!is_public) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
furi_string_printf(label, "Readable serv %04X", service->code);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
furi_string_get_cstr(label),
|
||||||
|
i + SubmenuIndexDynamic,
|
||||||
|
nfc_protocol_support_common_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_free(label);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_felica_system_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaSystem);
|
||||||
|
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
||||||
|
|
||||||
|
const uint32_t system_index = state >> 4;
|
||||||
|
const FelicaSystem* system = simple_array_cget(data->systems, system_index);
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcCustomEventViewExit) {
|
||||||
|
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||||
|
} else {
|
||||||
|
if(event.event == SubmenuIndexDirectory) {
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
nfc_more_info_render_felica_dir(system, temp_str);
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
|
} else {
|
||||||
|
const uint32_t service_ind =
|
||||||
|
event.event - SubmenuIndexDynamic; // offset the three enums above
|
||||||
|
|
||||||
|
text_box_reset(nfc->text_box);
|
||||||
|
furi_string_reset(nfc->text_box_store);
|
||||||
|
|
||||||
|
const FelicaService* service = simple_array_cget(system->services, service_ind);
|
||||||
|
furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code);
|
||||||
|
nfc_more_info_render_felica_blocks(
|
||||||
|
data, system, nfc->text_box_store, service->code);
|
||||||
|
|
||||||
|
text_box_set_font(nfc->text_box, TextBoxFontHex);
|
||||||
|
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||||
|
}
|
||||||
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, state | 1);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
if(state & 1) {
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||||
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, state & ~1);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_felica_system_on_exit(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
// Clear views
|
||||||
|
widget_reset(nfc->widget);
|
||||||
|
text_box_reset(nfc->text_box);
|
||||||
|
furi_string_reset(nfc->text_box_store);
|
||||||
|
submenu_reset(nfc->submenu);
|
||||||
|
}
|
||||||
@@ -99,6 +99,7 @@ typedef enum {
|
|||||||
SetTypeSommer_FM238_868,
|
SetTypeSommer_FM238_868,
|
||||||
SetTypeStilmatic,
|
SetTypeStilmatic,
|
||||||
SetTypeIronLogic,
|
SetTypeIronLogic,
|
||||||
|
SetTypeIronLogicSmart,
|
||||||
SetTypeDeaMio433,
|
SetTypeDeaMio433,
|
||||||
SetTypeDTMNeo433,
|
SetTypeDTMNeo433,
|
||||||
SetTypeGibidi433,
|
SetTypeGibidi433,
|
||||||
|
|||||||
@@ -398,6 +398,16 @@ void subghz_scene_set_type_fill_generation_infos(GenInfo* infos_dest, SetType ty
|
|||||||
.keeloq.cnt = 0x05,
|
.keeloq.cnt = 0x05,
|
||||||
.keeloq.manuf = "IronLogic"};
|
.keeloq.manuf = "IronLogic"};
|
||||||
break;
|
break;
|
||||||
|
case SetTypeIronLogicSmart:
|
||||||
|
gen_info = (GenInfo){
|
||||||
|
.type = GenKeeloq,
|
||||||
|
.mod = "AM650",
|
||||||
|
.freq = 433920000,
|
||||||
|
.keeloq.serial = key & 0x00FFFFF0,
|
||||||
|
.keeloq.btn = 0x04,
|
||||||
|
.keeloq.cnt = 0x05,
|
||||||
|
.keeloq.manuf = "IL-100(Smart)"};
|
||||||
|
break;
|
||||||
case SetTypeStilmatic:
|
case SetTypeStilmatic:
|
||||||
gen_info = (GenInfo){
|
gen_info = (GenInfo){
|
||||||
.type = GenKeeloq,
|
.type = GenKeeloq,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const char* const gps_text[GPS_COUNT] = {
|
|||||||
"115200",
|
"115200",
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEBUG_COUNTER_COUNT 16
|
#define DEBUG_COUNTER_COUNT 17
|
||||||
const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
|
const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
|
||||||
"+1",
|
"+1",
|
||||||
"+2",
|
"+2",
|
||||||
@@ -46,6 +46,7 @@ const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
|
|||||||
"+10",
|
"+10",
|
||||||
"+50",
|
"+50",
|
||||||
"OVFL",
|
"OVFL",
|
||||||
|
"OFEX",
|
||||||
"No",
|
"No",
|
||||||
"-1",
|
"-1",
|
||||||
"-2",
|
"-2",
|
||||||
@@ -64,6 +65,7 @@ const int32_t debug_counter_val[DEBUG_COUNTER_COUNT] = {
|
|||||||
10,
|
10,
|
||||||
50,
|
50,
|
||||||
65535,
|
65535,
|
||||||
|
65534,
|
||||||
0,
|
0,
|
||||||
-1,
|
-1,
|
||||||
-2,
|
-2,
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ static const char* submenu_names[SetTypeMAX] = {
|
|||||||
[SetTypeSommer_FM238_868] = "KL: Sommer fm2 868Mhz",
|
[SetTypeSommer_FM238_868] = "KL: Sommer fm2 868Mhz",
|
||||||
[SetTypeStilmatic] = "KL: Stilmatic 433MHz",
|
[SetTypeStilmatic] = "KL: Stilmatic 433MHz",
|
||||||
[SetTypeIronLogic] = "KL: IronLogic 433MHz",
|
[SetTypeIronLogic] = "KL: IronLogic 433MHz",
|
||||||
|
[SetTypeIronLogicSmart] = "KL: IronLogic SM 433MHz",
|
||||||
[SetTypeDeaMio433] = "KL: DEA Mio 433MHz",
|
[SetTypeDeaMio433] = "KL: DEA Mio 433MHz",
|
||||||
[SetTypeDTMNeo433] = "KL: DTM Neo 433MHz",
|
[SetTypeDTMNeo433] = "KL: DTM Neo 433MHz",
|
||||||
[SetTypeGibidi433] = "KL: Gibidi 433MHz",
|
[SetTypeGibidi433] = "KL: Gibidi 433MHz",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
View* view_alloc(void) {
|
View* view_alloc(void) {
|
||||||
View* view = malloc(sizeof(View));
|
View* view = malloc(sizeof(View));
|
||||||
view->orientation = ViewOrientationHorizontal;
|
view_init(view);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,6 +12,10 @@ void view_free(View* view) {
|
|||||||
free(view);
|
free(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void view_init(View* view) {
|
||||||
|
view->orientation = ViewOrientationHorizontal;
|
||||||
|
}
|
||||||
|
|
||||||
void view_tie_icon_animation(View* view, IconAnimation* icon_animation) {
|
void view_tie_icon_animation(View* view, IconAnimation* icon_animation) {
|
||||||
furi_check(view);
|
furi_check(view);
|
||||||
icon_animation_set_update_callback(icon_animation, view_icon_animation_callback, view);
|
icon_animation_set_update_callback(icon_animation, view_icon_animation_callback, view);
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ struct View {
|
|||||||
ViewAsciiCallback ascii_callback;
|
ViewAsciiCallback ascii_callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Initialize View (for internal use) */
|
||||||
|
void view_init(View* view);
|
||||||
|
|
||||||
/** IconAnimation tie callback */
|
/** IconAnimation tie callback */
|
||||||
void view_icon_animation_callback(IconAnimation* instance, void* context);
|
void view_icon_animation_callback(IconAnimation* instance, void* context);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
#include "view_stack.h"
|
#include "view_stack.h"
|
||||||
#include "view_i.h"
|
#include "view_i.h"
|
||||||
|
|
||||||
#include <gui/view.h>
|
|
||||||
#include <core/memmgr.h>
|
|
||||||
|
|
||||||
#define MAX_VIEWS 3
|
#define MAX_VIEWS 3
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -11,7 +8,7 @@ typedef struct {
|
|||||||
} ViewStackModel;
|
} ViewStackModel;
|
||||||
|
|
||||||
struct ViewStack {
|
struct ViewStack {
|
||||||
View* view;
|
View view;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void view_stack_draw(Canvas* canvas, void* model);
|
static void view_stack_draw(Canvas* canvas, void* model);
|
||||||
@@ -33,26 +30,26 @@ static void view_stack_enter(void* context) {
|
|||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
|
||||||
ViewStack* view_stack = context;
|
ViewStack* view_stack = context;
|
||||||
ViewStackModel* model = view_get_model(view_stack->view);
|
ViewStackModel* model = view_get_model(&view_stack->view);
|
||||||
|
|
||||||
/* if more than 1 Stack View hold same view they have to reassign update_callback_context */
|
/* if more than 1 Stack View hold same view they have to reassign update_callback_context */
|
||||||
for(int i = 0; i < MAX_VIEWS; ++i) {
|
for(int i = 0; i < MAX_VIEWS; ++i) {
|
||||||
if(model->views[i]) {
|
if(model->views[i]) {
|
||||||
view_set_update_callback_context(model->views[i], view_stack->view);
|
view_set_update_callback_context(model->views[i], &view_stack->view);
|
||||||
if(model->views[i]->enter_callback) {
|
if(model->views[i]->enter_callback) {
|
||||||
model->views[i]->enter_callback(model->views[i]->context);
|
model->views[i]->enter_callback(model->views[i]->context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view_commit_model(view_stack->view, false);
|
view_commit_model(&view_stack->view, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void view_stack_exit(void* context) {
|
static void view_stack_exit(void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
|
||||||
ViewStack* view_stack = context;
|
ViewStack* view_stack = context;
|
||||||
ViewStackModel* model = view_get_model(view_stack->view);
|
ViewStackModel* model = view_get_model(&view_stack->view);
|
||||||
|
|
||||||
for(int i = 0; i < MAX_VIEWS; ++i) {
|
for(int i = 0; i < MAX_VIEWS; ++i) {
|
||||||
if(model->views[i] && model->views[i]->exit_callback) {
|
if(model->views[i] && model->views[i]->exit_callback) {
|
||||||
@@ -60,36 +57,36 @@ static void view_stack_exit(void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view_commit_model(view_stack->view, false);
|
view_commit_model(&view_stack->view, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewStack* view_stack_alloc(void) {
|
ViewStack* view_stack_alloc(void) {
|
||||||
ViewStack* view_stack = malloc(sizeof(ViewStack));
|
ViewStack* view_stack = malloc(sizeof(ViewStack));
|
||||||
view_stack->view = view_alloc();
|
view_init(&view_stack->view);
|
||||||
|
|
||||||
view_allocate_model(view_stack->view, ViewModelTypeLocking, sizeof(ViewStackModel));
|
view_allocate_model(&view_stack->view, ViewModelTypeLocking, sizeof(ViewStackModel));
|
||||||
view_set_draw_callback(view_stack->view, view_stack_draw);
|
view_set_draw_callback(&view_stack->view, view_stack_draw);
|
||||||
view_set_input_callback(view_stack->view, view_stack_input);
|
view_set_input_callback(&view_stack->view, view_stack_input);
|
||||||
view_set_ascii_callback(view_stack->view, view_stack_ascii);
|
view_set_ascii_callback(&view_stack->view, view_stack_ascii);
|
||||||
view_set_context(view_stack->view, view_stack);
|
view_set_context(&view_stack->view, view_stack);
|
||||||
view_set_enter_callback(view_stack->view, view_stack_enter);
|
view_set_enter_callback(&view_stack->view, view_stack_enter);
|
||||||
view_set_exit_callback(view_stack->view, view_stack_exit);
|
view_set_exit_callback(&view_stack->view, view_stack_exit);
|
||||||
return view_stack;
|
return view_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
void view_stack_free(ViewStack* view_stack) {
|
void view_stack_free(ViewStack* view_stack) {
|
||||||
furi_assert(view_stack);
|
furi_assert(view_stack);
|
||||||
|
|
||||||
ViewStackModel* model = view_get_model(view_stack->view);
|
ViewStackModel* model = view_get_model(&view_stack->view);
|
||||||
for(int i = 0; i < MAX_VIEWS; ++i) {
|
for(int i = 0; i < MAX_VIEWS; ++i) {
|
||||||
if(model->views[i]) {
|
if(model->views[i]) {
|
||||||
view_set_update_callback(model->views[i], NULL);
|
view_set_update_callback(model->views[i], NULL);
|
||||||
view_set_update_callback_context(model->views[i], NULL);
|
view_set_update_callback_context(model->views[i], NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view_commit_model(view_stack->view, false);
|
view_commit_model(&view_stack->view, false);
|
||||||
|
|
||||||
view_free(view_stack->view);
|
view_free_model(&view_stack->view);
|
||||||
free(view_stack);
|
free(view_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,14 +108,14 @@ static bool view_stack_input(InputEvent* event, void* context) {
|
|||||||
ViewStack* view_stack = context;
|
ViewStack* view_stack = context;
|
||||||
|
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
ViewStackModel* model = view_get_model(view_stack->view);
|
ViewStackModel* model = view_get_model(&view_stack->view);
|
||||||
for(int i = MAX_VIEWS - 1; i >= 0; i--) {
|
for(int i = MAX_VIEWS - 1; i >= 0; i--) {
|
||||||
if(model->views[i] && view_input(model->views[i], event)) {
|
if(model->views[i] && view_input(model->views[i], event)) {
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view_commit_model(view_stack->view, false);
|
view_commit_model(&view_stack->view, false);
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
@@ -130,14 +127,14 @@ static bool view_stack_ascii(AsciiEvent* event, void* context) {
|
|||||||
ViewStack* view_stack = context;
|
ViewStack* view_stack = context;
|
||||||
|
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
ViewStackModel* model = view_get_model(view_stack->view);
|
ViewStackModel* model = view_get_model(&view_stack->view);
|
||||||
for(int i = MAX_VIEWS - 1; i >= 0; i--) {
|
for(int i = MAX_VIEWS - 1; i >= 0; i--) {
|
||||||
if(model->views[i] && view_ascii(model->views[i], event)) {
|
if(model->views[i] && view_ascii(model->views[i], event)) {
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view_commit_model(view_stack->view, false);
|
view_commit_model(&view_stack->view, false);
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
@@ -147,12 +144,12 @@ void view_stack_add_view(ViewStack* view_stack, View* view) {
|
|||||||
furi_assert(view);
|
furi_assert(view);
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
ViewStackModel* model = view_get_model(view_stack->view);
|
ViewStackModel* model = view_get_model(&view_stack->view);
|
||||||
for(int i = 0; i < MAX_VIEWS; ++i) {
|
for(int i = 0; i < MAX_VIEWS; ++i) {
|
||||||
if(!model->views[i]) {
|
if(!model->views[i]) {
|
||||||
model->views[i] = view;
|
model->views[i] = view;
|
||||||
view_set_update_callback(model->views[i], view_stack_update_callback);
|
view_set_update_callback(model->views[i], view_stack_update_callback);
|
||||||
view_set_update_callback_context(model->views[i], view_stack->view);
|
view_set_update_callback_context(model->views[i], &view_stack->view);
|
||||||
if(view->enter_callback) {
|
if(view->enter_callback) {
|
||||||
view->enter_callback(view->context);
|
view->enter_callback(view->context);
|
||||||
}
|
}
|
||||||
@@ -160,7 +157,7 @@ void view_stack_add_view(ViewStack* view_stack, View* view) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view_commit_model(view_stack->view, result);
|
view_commit_model(&view_stack->view, result);
|
||||||
furi_assert(result);
|
furi_assert(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +168,7 @@ void view_stack_remove_view(ViewStack* view_stack, View* view) {
|
|||||||
/* Removing view on-the-go is dangerous, but it is protected with
|
/* Removing view on-the-go is dangerous, but it is protected with
|
||||||
* Locking model, so system is consistent at any time. */
|
* Locking model, so system is consistent at any time. */
|
||||||
bool result = false;
|
bool result = false;
|
||||||
ViewStackModel* model = view_get_model(view_stack->view);
|
ViewStackModel* model = view_get_model(&view_stack->view);
|
||||||
for(int i = 0; i < MAX_VIEWS; ++i) {
|
for(int i = 0; i < MAX_VIEWS; ++i) {
|
||||||
if(model->views[i] == view) {
|
if(model->views[i] == view) {
|
||||||
if(view->exit_callback) {
|
if(view->exit_callback) {
|
||||||
@@ -184,11 +181,11 @@ void view_stack_remove_view(ViewStack* view_stack, View* view) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view_commit_model(view_stack->view, result);
|
view_commit_model(&view_stack->view, result);
|
||||||
furi_assert(result);
|
furi_assert(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
View* view_stack_get_view(ViewStack* view_stack) {
|
View* view_stack_get_view(ViewStack* view_stack) {
|
||||||
furi_assert(view_stack);
|
furi_assert(view_stack);
|
||||||
return view_stack->view;
|
return &view_stack->view;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ enum HidPushToTalkAppIndex {
|
|||||||
HidPushToTalkAppIndexFaceTime,
|
HidPushToTalkAppIndexFaceTime,
|
||||||
HidPushToTalkAppIndexGather,
|
HidPushToTalkAppIndexGather,
|
||||||
HidPushToTalkAppIndexGoogleMeet,
|
HidPushToTalkAppIndexGoogleMeet,
|
||||||
|
HidPushToTalkAppIndexGoogleMeetGlobal,
|
||||||
HidPushToTalkAppIndexGoogleHangouts,
|
HidPushToTalkAppIndexGoogleHangouts,
|
||||||
HidPushToTalkAppIndexJamulus,
|
HidPushToTalkAppIndexJamulus,
|
||||||
HidPushToTalkAppIndexSignal,
|
HidPushToTalkAppIndexSignal,
|
||||||
@@ -54,6 +55,7 @@ enum HidPushToTalkAppIndex {
|
|||||||
HidPushToTalkAppIndexTeamSpeak,
|
HidPushToTalkAppIndexTeamSpeak,
|
||||||
HidPushToTalkAppIndexWebex,
|
HidPushToTalkAppIndexWebex,
|
||||||
HidPushToTalkAppIndexZoom,
|
HidPushToTalkAppIndexZoom,
|
||||||
|
HidPushToTalkAppIndexZoomGlobal,
|
||||||
HidPushToTalkAppIndexSize,
|
HidPushToTalkAppIndexSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,6 +90,20 @@ static void hid_ptt_trigger_hand_linux_meet(HidPushToTalk* hid_ptt) {
|
|||||||
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT | HID_KEYBOARD_H);
|
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT | HID_KEYBOARD_H);
|
||||||
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT | HID_KEYBOARD_H);
|
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT | HID_KEYBOARD_H);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// meet global macos
|
||||||
|
static void hid_ptt_trigger_mute_macos_meet_global(HidPushToTalk* hid_ptt) {
|
||||||
|
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_7);
|
||||||
|
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_7);
|
||||||
|
}
|
||||||
|
static void hid_ptt_trigger_camera_macos_meet_global(HidPushToTalk* hid_ptt) {
|
||||||
|
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_8);
|
||||||
|
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_8);
|
||||||
|
}
|
||||||
|
static void hid_ptt_trigger_hand_macos_meet_global(HidPushToTalk* hid_ptt) {
|
||||||
|
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_9);
|
||||||
|
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_9);
|
||||||
|
}
|
||||||
static void hid_ptt_trigger_mute_macos_zoom(HidPushToTalk* hid_ptt) {
|
static void hid_ptt_trigger_mute_macos_zoom(HidPushToTalk* hid_ptt) {
|
||||||
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A);
|
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A);
|
||||||
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A);
|
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A);
|
||||||
@@ -109,6 +125,28 @@ static void hid_ptt_trigger_hand_zoom(HidPushToTalk* hid_ptt) {
|
|||||||
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y);
|
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// zoom global macos
|
||||||
|
static void hid_ptt_trigger_mute_macos_zoom_global(HidPushToTalk* hid_ptt) {
|
||||||
|
hid_hal_keyboard_press(
|
||||||
|
hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M);
|
||||||
|
hid_hal_keyboard_release(
|
||||||
|
hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hid_ptt_trigger_camera_macos_zoom_global(HidPushToTalk* hid_ptt) {
|
||||||
|
hid_hal_keyboard_press(
|
||||||
|
hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_U);
|
||||||
|
hid_hal_keyboard_release(
|
||||||
|
hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_U);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hid_ptt_trigger_hand_zoom_global(HidPushToTalk* hid_ptt) {
|
||||||
|
hid_hal_keyboard_press(
|
||||||
|
hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_Y);
|
||||||
|
hid_hal_keyboard_release(
|
||||||
|
hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_Y);
|
||||||
|
}
|
||||||
|
|
||||||
// this one is widely used across different apps
|
// this one is widely used across different apps
|
||||||
static void hid_ptt_trigger_cmd_shift_m(HidPushToTalk* hid_ptt) {
|
static void hid_ptt_trigger_cmd_shift_m(HidPushToTalk* hid_ptt) {
|
||||||
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M);
|
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M);
|
||||||
@@ -403,6 +441,13 @@ static void hid_ptt_menu_callback(
|
|||||||
model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom;
|
model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom;
|
||||||
model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom;
|
model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom;
|
||||||
break;
|
break;
|
||||||
|
case HidPushToTalkAppIndexGoogleMeetGlobal:
|
||||||
|
model->callback_trigger_mute = hid_ptt_trigger_mute_macos_meet_global;
|
||||||
|
model->callback_trigger_camera = hid_ptt_trigger_camera_macos_meet_global;
|
||||||
|
model->callback_trigger_hand = hid_ptt_trigger_hand_macos_meet_global;
|
||||||
|
model->callback_start_ptt = hid_ptt_trigger_mute_macos_meet_global;
|
||||||
|
model->callback_stop_ptt = hid_ptt_trigger_mute_macos_meet_global;
|
||||||
|
break;
|
||||||
case HidPushToTalkAppIndexJamulus:
|
case HidPushToTalkAppIndexJamulus:
|
||||||
model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus;
|
model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus;
|
||||||
model->callback_start_ptt = hid_ptt_trigger_mute_jamulus;
|
model->callback_start_ptt = hid_ptt_trigger_mute_jamulus;
|
||||||
@@ -457,6 +502,13 @@ static void hid_ptt_menu_callback(
|
|||||||
model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom;
|
model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom;
|
||||||
model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom;
|
model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom;
|
||||||
break;
|
break;
|
||||||
|
case HidPushToTalkAppIndexZoomGlobal:
|
||||||
|
model->callback_trigger_mute = hid_ptt_trigger_mute_macos_zoom_global;
|
||||||
|
model->callback_trigger_camera = hid_ptt_trigger_camera_macos_zoom_global;
|
||||||
|
model->callback_trigger_hand = hid_ptt_trigger_hand_zoom_global;
|
||||||
|
model->callback_start_ptt = hid_ptt_trigger_mute_macos_zoom_global;
|
||||||
|
model->callback_stop_ptt = hid_ptt_trigger_mute_macos_zoom_global;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else if(osIndex == HidPushToTalkLinux) {
|
} else if(osIndex == HidPushToTalkLinux) {
|
||||||
switch(appIndex) {
|
switch(appIndex) {
|
||||||
@@ -551,6 +603,14 @@ static void hid_ptt_menu_callback(
|
|||||||
"and may not work for Windows users who use their screen "
|
"and may not work for Windows users who use their screen "
|
||||||
"reader. In this situation, the spacebar performs a different action.\n\n";
|
"reader. In this situation, the spacebar performs a different action.\n\n";
|
||||||
break;
|
break;
|
||||||
|
case HidPushToTalkAppIndexGoogleMeetGlobal:
|
||||||
|
app_specific_help = "Google Meet (Global):\n"
|
||||||
|
"1. Install \"Google Meet - Global Shortcuts\" extension.\n"
|
||||||
|
"2. Open chrome://extensions/shortcuts.\n"
|
||||||
|
"3. Set 'Toggle microphone' to Cmd+Ctrl+7 and enable Global.\n"
|
||||||
|
"4. Set 'Toggle camera' to Cmd+Ctrl+8 and enable Global.\n"
|
||||||
|
"5. Set 'Raise hand' to Cmd+Ctrl+9 and enable Global.\n\n";
|
||||||
|
break;
|
||||||
case HidPushToTalkAppIndexDiscord:
|
case HidPushToTalkAppIndexDiscord:
|
||||||
app_specific_help =
|
app_specific_help =
|
||||||
"Discord:\n"
|
"Discord:\n"
|
||||||
@@ -569,6 +629,13 @@ static void hid_ptt_menu_callback(
|
|||||||
"Teams:\n"
|
"Teams:\n"
|
||||||
"Go to Settings > Privacy. Make sure Keyboard shortcut to unmute is toggled on.\n\n";
|
"Go to Settings > Privacy. Make sure Keyboard shortcut to unmute is toggled on.\n\n";
|
||||||
break;
|
break;
|
||||||
|
case HidPushToTalkAppIndexZoomGlobal:
|
||||||
|
app_specific_help = "Zoom (Global):\n"
|
||||||
|
"1. Go to Settings > Keyboard Shortcuts.\n"
|
||||||
|
"2. Find the 'Mute/Unmute' shortcut and click 'Edit'.\n"
|
||||||
|
"3. Press the Mute button in the app to bind it.\n"
|
||||||
|
"4. Check global checkbox.\n"
|
||||||
|
"5. Repeat for video and hand shortcuts.\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
FuriString* msg = furi_string_alloc();
|
FuriString* msg = furi_string_alloc();
|
||||||
@@ -876,6 +943,13 @@ HidPushToTalk* hid_ptt_alloc(Hid* hid) {
|
|||||||
HidPushToTalkAppIndexGoogleMeet,
|
HidPushToTalkAppIndexGoogleMeet,
|
||||||
hid_ptt_menu_callback,
|
hid_ptt_menu_callback,
|
||||||
hid_ptt);
|
hid_ptt);
|
||||||
|
ptt_menu_add_item_to_list(
|
||||||
|
hid->hid_ptt_menu,
|
||||||
|
HidPushToTalkMacOS,
|
||||||
|
"Google Meet Global",
|
||||||
|
HidPushToTalkAppIndexGoogleMeetGlobal,
|
||||||
|
hid_ptt_menu_callback,
|
||||||
|
hid_ptt);
|
||||||
ptt_menu_add_item_to_list(
|
ptt_menu_add_item_to_list(
|
||||||
hid->hid_ptt_menu,
|
hid->hid_ptt_menu,
|
||||||
HidPushToTalkMacOS,
|
HidPushToTalkMacOS,
|
||||||
@@ -1030,6 +1104,13 @@ HidPushToTalk* hid_ptt_alloc(Hid* hid) {
|
|||||||
HidPushToTalkAppIndexZoom,
|
HidPushToTalkAppIndexZoom,
|
||||||
hid_ptt_menu_callback,
|
hid_ptt_menu_callback,
|
||||||
hid_ptt);
|
hid_ptt);
|
||||||
|
ptt_menu_add_item_to_list(
|
||||||
|
hid->hid_ptt_menu,
|
||||||
|
HidPushToTalkMacOS,
|
||||||
|
"Zoom Global",
|
||||||
|
HidPushToTalkAppIndexZoomGlobal,
|
||||||
|
hid_ptt_menu_callback,
|
||||||
|
hid_ptt);
|
||||||
ptt_menu_add_item_to_list(
|
ptt_menu_add_item_to_list(
|
||||||
hid->hid_ptt_menu,
|
hid->hid_ptt_menu,
|
||||||
HidPushToTalkLinux,
|
HidPushToTalkLinux,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ env.Append(
|
|||||||
File("nfc_listener.h"),
|
File("nfc_listener.h"),
|
||||||
File("nfc_poller.h"),
|
File("nfc_poller.h"),
|
||||||
File("nfc_scanner.h"),
|
File("nfc_scanner.h"),
|
||||||
|
File("nfc_common.h"),
|
||||||
# Protocols
|
# Protocols
|
||||||
File("protocols/iso14443_3a/iso14443_3a.h"),
|
File("protocols/iso14443_3a/iso14443_3a.h"),
|
||||||
File("protocols/iso14443_3b/iso14443_3b.h"),
|
File("protocols/iso14443_3b/iso14443_3b.h"),
|
||||||
|
|||||||
@@ -42,26 +42,16 @@ FelicaData* felica_alloc(void) {
|
|||||||
FelicaData* data = malloc(sizeof(FelicaData));
|
FelicaData* data = malloc(sizeof(FelicaData));
|
||||||
furi_check(data);
|
furi_check(data);
|
||||||
|
|
||||||
data->services = simple_array_alloc(&felica_service_array_cfg);
|
data->systems = simple_array_alloc(&felica_system_array_cfg);
|
||||||
data->areas = simple_array_alloc(&felica_area_array_cfg);
|
furi_check(data->systems);
|
||||||
data->public_blocks = simple_array_alloc(&felica_public_block_array_cfg);
|
|
||||||
furi_check(data->services);
|
|
||||||
furi_check(data->areas);
|
|
||||||
furi_check(data->public_blocks);
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void felica_free(FelicaData* data) {
|
void felica_free(FelicaData* data) {
|
||||||
furi_check(data);
|
furi_check(data);
|
||||||
|
|
||||||
furi_check(data->services);
|
furi_check(data->systems);
|
||||||
simple_array_free(data->services);
|
simple_array_free(data->systems);
|
||||||
|
|
||||||
furi_check(data->areas);
|
|
||||||
simple_array_free(data->areas);
|
|
||||||
|
|
||||||
furi_check(data->public_blocks);
|
|
||||||
simple_array_free(data->public_blocks);
|
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
@@ -69,16 +59,8 @@ void felica_free(FelicaData* data) {
|
|||||||
void felica_reset(FelicaData* data) {
|
void felica_reset(FelicaData* data) {
|
||||||
furi_check(data);
|
furi_check(data);
|
||||||
|
|
||||||
if(data->services) {
|
if(data->systems) {
|
||||||
simple_array_reset(data->services);
|
simple_array_reset(data->systems);
|
||||||
}
|
|
||||||
|
|
||||||
if(data->areas) {
|
|
||||||
simple_array_reset(data->areas);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(data->public_blocks) {
|
|
||||||
simple_array_reset(data->public_blocks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data->blocks_read = 0;
|
data->blocks_read = 0;
|
||||||
@@ -102,9 +84,7 @@ void felica_copy(FelicaData* data, const FelicaData* other) {
|
|||||||
data->data = other->data;
|
data->data = other->data;
|
||||||
data->workflow_type = other->workflow_type;
|
data->workflow_type = other->workflow_type;
|
||||||
|
|
||||||
simple_array_copy(data->services, other->services);
|
simple_array_copy(data->systems, other->systems);
|
||||||
simple_array_copy(data->areas, other->areas);
|
|
||||||
simple_array_copy(data->public_blocks, other->public_blocks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool felica_verify(FelicaData* data, const FuriString* device_type) {
|
bool felica_verify(FelicaData* data, const FuriString* device_type) {
|
||||||
@@ -175,99 +155,125 @@ bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
|||||||
} while(false);
|
} while(false);
|
||||||
break;
|
break;
|
||||||
case FelicaStandard:
|
case FelicaStandard:
|
||||||
// Areas
|
uint32_t systems_total = 0;
|
||||||
do {
|
if(!flipper_format_read_uint32(ff, "System found", &systems_total, 1)) break;
|
||||||
uint32_t area_count = 0;
|
simple_array_init(data->systems, systems_total);
|
||||||
if(!flipper_format_read_uint32(ff, "Area found", &area_count, 1)) break;
|
|
||||||
simple_array_init(data->areas, area_count);
|
for(uint8_t sys_idx = 0; sys_idx < systems_total; sys_idx++) {
|
||||||
|
FelicaSystem* system = simple_array_get(data->systems, sys_idx);
|
||||||
|
uint16_t system_code = 0;
|
||||||
|
|
||||||
furi_string_reset(str_key_buffer);
|
furi_string_reset(str_key_buffer);
|
||||||
furi_string_reset(str_data_buffer);
|
furi_string_reset(str_data_buffer);
|
||||||
for(uint16_t i = 0; i < area_count; i++) {
|
furi_string_printf(str_key_buffer, "System %02X", sys_idx);
|
||||||
furi_string_printf(str_key_buffer, "Area %03X", i);
|
if(!flipper_format_read_string(
|
||||||
if(!flipper_format_read_string(
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
||||||
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
FelicaArea* area = simple_array_get(data->areas, i);
|
|
||||||
if(sscanf(
|
|
||||||
furi_string_get_cstr(str_data_buffer),
|
|
||||||
"| Code %04hX | Services #%03hX-#%03hX |",
|
|
||||||
&area->code,
|
|
||||||
&area->first_idx,
|
|
||||||
&area->last_idx) != 3) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while(false);
|
|
||||||
|
|
||||||
// Services
|
|
||||||
do {
|
|
||||||
uint32_t service_count = 0;
|
|
||||||
if(!flipper_format_read_uint32(ff, "Service found", &service_count, 1)) break;
|
|
||||||
simple_array_init(data->services, service_count);
|
|
||||||
|
|
||||||
furi_string_reset(str_key_buffer);
|
|
||||||
furi_string_reset(str_data_buffer);
|
|
||||||
for(uint16_t i = 0; i < service_count; i++) {
|
|
||||||
furi_string_printf(str_key_buffer, "Service %03X", i);
|
|
||||||
if(!flipper_format_read_string(
|
|
||||||
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
FelicaService* service = simple_array_get(data->services, i);
|
|
||||||
|
|
||||||
// all unread in the beginning. reserved for future block load
|
|
||||||
if(!sscanf(
|
|
||||||
furi_string_get_cstr(str_data_buffer), "| Code %04hX |", &service->code)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
service->attr = service->code & 0x3F;
|
|
||||||
}
|
|
||||||
} while(false);
|
|
||||||
|
|
||||||
// Public blocks
|
|
||||||
do {
|
|
||||||
furi_string_reset(str_data_buffer);
|
|
||||||
furi_string_reset(str_key_buffer);
|
|
||||||
uint32_t public_block_count = 0;
|
|
||||||
if(!flipper_format_read_uint32(ff, "Public blocks read", &public_block_count, 1))
|
|
||||||
break;
|
break;
|
||||||
simple_array_init(data->public_blocks, public_block_count);
|
|
||||||
for(uint16_t i = 0; i < public_block_count; i++) {
|
|
||||||
furi_string_printf(str_key_buffer, "Block %04X", i);
|
|
||||||
if(!flipper_format_read_string(
|
|
||||||
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
|
if(!sscanf(furi_string_get_cstr(str_data_buffer), "%04hX", &system_code)) {
|
||||||
if(sscanf(
|
break;
|
||||||
furi_string_get_cstr(str_data_buffer),
|
|
||||||
"| Service code %04hX | Block index %02hhX |",
|
|
||||||
&public_block->service_code,
|
|
||||||
&public_block->block_idx) != 2) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t needle = furi_string_search_str(str_data_buffer, "Data: ");
|
|
||||||
if(needle == FURI_STRING_FAILURE) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
needle += 6; // length of "Data: " = 6
|
|
||||||
furi_string_mid(str_data_buffer, needle, 3 * FELICA_DATA_BLOCK_SIZE);
|
|
||||||
furi_string_replace_all(str_data_buffer, " ", "");
|
|
||||||
if(!hex_chars_to_uint8(
|
|
||||||
furi_string_get_cstr(str_data_buffer), public_block->block.data)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_string_reset(str_data_buffer);
|
|
||||||
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
|
||||||
furi_string_cat_printf(str_data_buffer, "%02X ", public_block->block.data[j]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} while(false);
|
|
||||||
|
system->system_code = system_code;
|
||||||
|
system->system_code_idx = sys_idx;
|
||||||
|
|
||||||
|
// Areas
|
||||||
|
do {
|
||||||
|
uint32_t area_count = 0;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Area found", &area_count, 1)) break;
|
||||||
|
simple_array_init(system->areas, area_count);
|
||||||
|
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
for(uint16_t i = 0; i < area_count; i++) {
|
||||||
|
furi_string_printf(str_key_buffer, "Area %03X", i);
|
||||||
|
if(!flipper_format_read_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FelicaArea* area = simple_array_get(system->areas, i);
|
||||||
|
if(sscanf(
|
||||||
|
furi_string_get_cstr(str_data_buffer),
|
||||||
|
"| Code %04hX | Services #%03hX-#%03hX |",
|
||||||
|
&area->code,
|
||||||
|
&area->first_idx,
|
||||||
|
&area->last_idx) != 3) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
// Services
|
||||||
|
do {
|
||||||
|
uint32_t service_count = 0;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Service found", &service_count, 1)) break;
|
||||||
|
simple_array_init(system->services, service_count);
|
||||||
|
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
for(uint16_t i = 0; i < service_count; i++) {
|
||||||
|
furi_string_printf(str_key_buffer, "Service %03X", i);
|
||||||
|
if(!flipper_format_read_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FelicaService* service = simple_array_get(system->services, i);
|
||||||
|
|
||||||
|
// all unread in the beginning. reserved for future block load
|
||||||
|
if(!sscanf(
|
||||||
|
furi_string_get_cstr(str_data_buffer),
|
||||||
|
"| Code %04hX |",
|
||||||
|
&service->code)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
service->attr = service->code & 0x3F;
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
// Public blocks
|
||||||
|
do {
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
uint32_t public_block_count = 0;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Public blocks read", &public_block_count, 1))
|
||||||
|
break;
|
||||||
|
simple_array_init(system->public_blocks, public_block_count);
|
||||||
|
for(uint16_t i = 0; i < public_block_count; i++) {
|
||||||
|
furi_string_printf(str_key_buffer, "Block %04X", i);
|
||||||
|
if(!flipper_format_read_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i);
|
||||||
|
if(sscanf(
|
||||||
|
furi_string_get_cstr(str_data_buffer),
|
||||||
|
"| Service code %04hX | Block index %02hhX |",
|
||||||
|
&public_block->service_code,
|
||||||
|
&public_block->block_idx) != 2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t needle = furi_string_search_str(str_data_buffer, "Data: ");
|
||||||
|
if(needle == FURI_STRING_FAILURE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
needle += 6; // length of "Data: " = 6
|
||||||
|
furi_string_mid(str_data_buffer, needle, 3 * FELICA_DATA_BLOCK_SIZE);
|
||||||
|
furi_string_replace_all(str_data_buffer, " ", "");
|
||||||
|
if(!hex_chars_to_uint8(
|
||||||
|
furi_string_get_cstr(str_data_buffer), public_block->block.data)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str_data_buffer, "%02X ", public_block->block.data[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -334,88 +340,107 @@ bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
|||||||
|
|
||||||
case FelicaStandard:
|
case FelicaStandard:
|
||||||
if(!flipper_format_write_comment_cstr(ff, "Felica Standard specific data")) break;
|
if(!flipper_format_write_comment_cstr(ff, "Felica Standard specific data")) break;
|
||||||
|
uint32_t systems_count = simple_array_get_count(data->systems);
|
||||||
|
if(!flipper_format_write_uint32(ff, "System found", &systems_count, 1)) break;
|
||||||
|
for(uint32_t sys_idx = 0; sys_idx < systems_count; sys_idx++) {
|
||||||
|
FelicaSystem* system = simple_array_get(data->systems, sys_idx);
|
||||||
|
|
||||||
do {
|
|
||||||
uint32_t area_count = simple_array_get_count(data->areas);
|
|
||||||
uint32_t service_count = simple_array_get_count(data->services);
|
|
||||||
// Note: The theoretical max area/service count is 2^10
|
|
||||||
// So uint16_t is already enough for practical usage
|
|
||||||
// The following key index print will use %03X because 12 bits are enough to cover 0-1023
|
|
||||||
|
|
||||||
// Area count
|
|
||||||
if(!flipper_format_write_uint32(ff, "Area found", &area_count, 1)) break;
|
|
||||||
|
|
||||||
// Area data
|
|
||||||
furi_string_reset(str_data_buffer);
|
furi_string_reset(str_data_buffer);
|
||||||
furi_string_reset(str_key_buffer);
|
furi_string_reset(str_key_buffer);
|
||||||
for(uint16_t i = 0; i < area_count; i++) {
|
furi_string_printf(str_key_buffer, "\n\nSystem %02X", (uint8_t)sys_idx);
|
||||||
FelicaArea* area = simple_array_get(data->areas, i);
|
furi_string_printf(str_data_buffer, "%04X", system->system_code);
|
||||||
furi_string_printf(str_key_buffer, "Area %03X", i);
|
if(!flipper_format_write_string(
|
||||||
furi_string_printf(
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
||||||
str_data_buffer,
|
|
||||||
"| Code %04X | Services #%03X-#%03X |",
|
|
||||||
area->code,
|
|
||||||
area->first_idx,
|
|
||||||
area->last_idx);
|
|
||||||
if(!flipper_format_write_string(
|
|
||||||
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!flipper_format_write_empty_line(ff)) break;
|
|
||||||
|
|
||||||
// Service count
|
|
||||||
if(!flipper_format_write_uint32(ff, "Service found", &service_count, 1)) break;
|
|
||||||
|
|
||||||
// Service data
|
|
||||||
furi_string_reset(str_data_buffer);
|
|
||||||
furi_string_reset(str_key_buffer);
|
|
||||||
for(uint16_t i = 0; i < service_count; i++) {
|
|
||||||
FelicaService* service = simple_array_get(data->services, i);
|
|
||||||
furi_string_printf(str_key_buffer, "Service %03X", i);
|
|
||||||
furi_string_printf(
|
|
||||||
str_data_buffer, "| Code %04X | Attrib. %02X ", service->code, service->attr);
|
|
||||||
felica_service_get_attribute_string(service, str_data_buffer);
|
|
||||||
if(!flipper_format_write_string(
|
|
||||||
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!flipper_format_write_empty_line(ff)) break;
|
|
||||||
|
|
||||||
// Directory tree
|
|
||||||
furi_string_reset(str_data_buffer);
|
|
||||||
furi_string_reset(str_key_buffer);
|
|
||||||
furi_string_printf(
|
|
||||||
str_data_buffer, "\n::: ... are public services\n||| ... are private services");
|
|
||||||
felica_write_directory_tree(data, str_data_buffer);
|
|
||||||
furi_string_replace_all(str_data_buffer, ":", "+");
|
|
||||||
// We use a clearer marker in saved text files
|
|
||||||
if(!flipper_format_write_string(ff, "Directory Tree", str_data_buffer)) break;
|
|
||||||
} while(false);
|
|
||||||
|
|
||||||
// Public blocks
|
|
||||||
do {
|
|
||||||
uint32_t public_block_count = simple_array_get_count(data->public_blocks);
|
|
||||||
if(!flipper_format_write_uint32(ff, "Public blocks read", &public_block_count, 1))
|
|
||||||
break;
|
break;
|
||||||
furi_string_reset(str_data_buffer);
|
if(!flipper_format_write_empty_line(ff)) break;
|
||||||
furi_string_reset(str_key_buffer);
|
|
||||||
for(uint16_t i = 0; i < public_block_count; i++) {
|
do {
|
||||||
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
|
uint32_t area_count = simple_array_get_count(system->areas);
|
||||||
furi_string_printf(str_key_buffer, "Block %04X", i);
|
uint32_t service_count = simple_array_get_count(system->services);
|
||||||
|
// Note: The theoretical max area/service count is 2^10
|
||||||
|
// So uint16_t is already enough for practical usage
|
||||||
|
// The following key index print will use %03X because 12 bits are enough to cover 0-1023
|
||||||
|
|
||||||
|
// Area count
|
||||||
|
if(!flipper_format_write_uint32(ff, "Area found", &area_count, 1)) break;
|
||||||
|
|
||||||
|
// Area data
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
for(uint16_t i = 0; i < area_count; i++) {
|
||||||
|
FelicaArea* area = simple_array_get(system->areas, i);
|
||||||
|
furi_string_printf(str_key_buffer, "Area %03X", i);
|
||||||
|
furi_string_printf(
|
||||||
|
str_data_buffer,
|
||||||
|
"| Code %04X | Services #%03X-#%03X |",
|
||||||
|
area->code,
|
||||||
|
area->first_idx,
|
||||||
|
area->last_idx);
|
||||||
|
if(!flipper_format_write_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_write_empty_line(ff)) break;
|
||||||
|
|
||||||
|
// Service count
|
||||||
|
if(!flipper_format_write_uint32(ff, "Service found", &service_count, 1)) break;
|
||||||
|
|
||||||
|
// Service data
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
for(uint16_t i = 0; i < service_count; i++) {
|
||||||
|
FelicaService* service = simple_array_get(system->services, i);
|
||||||
|
furi_string_printf(str_key_buffer, "Service %03X", i);
|
||||||
|
furi_string_printf(
|
||||||
|
str_data_buffer,
|
||||||
|
"| Code %04X | Attrib. %02X ",
|
||||||
|
service->code,
|
||||||
|
service->attr);
|
||||||
|
felica_service_get_attribute_string(service, str_data_buffer);
|
||||||
|
if(!flipper_format_write_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_write_empty_line(ff)) break;
|
||||||
|
|
||||||
|
// Directory tree
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
furi_string_printf(
|
furi_string_printf(
|
||||||
str_data_buffer,
|
str_data_buffer,
|
||||||
"| Service code %04X | Block index %02X | Data: ",
|
"\n::: ... are public services\n||| ... are private services");
|
||||||
public_block->service_code,
|
felica_write_directory_tree(system, str_data_buffer);
|
||||||
public_block->block_idx);
|
furi_string_replace_all(str_data_buffer, ":", "+");
|
||||||
for(uint8_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
// We use a clearer marker in saved text files
|
||||||
furi_string_cat_printf(str_data_buffer, "%02X ", public_block->block.data[j]);
|
if(!flipper_format_write_string(ff, "Directory Tree", str_data_buffer)) break;
|
||||||
}
|
} while(false);
|
||||||
furi_string_cat_printf(str_data_buffer, "|");
|
|
||||||
if(!flipper_format_write_string(
|
// Public blocks
|
||||||
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
do {
|
||||||
|
uint32_t public_block_count = simple_array_get_count(system->public_blocks);
|
||||||
|
if(!flipper_format_write_uint32(ff, "Public blocks read", &public_block_count, 1))
|
||||||
break;
|
break;
|
||||||
}
|
furi_string_reset(str_data_buffer);
|
||||||
} while(false);
|
furi_string_reset(str_key_buffer);
|
||||||
|
for(uint16_t i = 0; i < public_block_count; i++) {
|
||||||
|
FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i);
|
||||||
|
furi_string_printf(str_key_buffer, "Block %04X", i);
|
||||||
|
furi_string_printf(
|
||||||
|
str_data_buffer,
|
||||||
|
"| Service code %04X | Block index %02X | Data: ",
|
||||||
|
public_block->service_code,
|
||||||
|
public_block->block_idx);
|
||||||
|
for(uint8_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str_data_buffer, "%02X ", public_block->block.data[j]);
|
||||||
|
}
|
||||||
|
furi_string_cat_printf(str_data_buffer, "|");
|
||||||
|
if(!flipper_format_write_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -436,9 +461,7 @@ bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
|
|||||||
memcmp(data->pmm.data, other->pmm.data, sizeof(FelicaPMm)) == 0 &&
|
memcmp(data->pmm.data, other->pmm.data, sizeof(FelicaPMm)) == 0 &&
|
||||||
data->blocks_total == other->blocks_total && data->blocks_read == other->blocks_read &&
|
data->blocks_total == other->blocks_total && data->blocks_read == other->blocks_read &&
|
||||||
memcmp(&data->data, &other->data, sizeof(data->data)) == 0 &&
|
memcmp(&data->data, &other->data, sizeof(data->data)) == 0 &&
|
||||||
simple_array_is_equal(data->services, other->services) &&
|
simple_array_is_equal(data->systems, other->systems);
|
||||||
simple_array_is_equal(data->areas, other->areas) &&
|
|
||||||
simple_array_is_equal(data->public_blocks, other->public_blocks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) {
|
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) {
|
||||||
@@ -640,8 +663,8 @@ void felica_calculate_mac_write(
|
|||||||
felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac);
|
felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
void felica_write_directory_tree(const FelicaData* data, FuriString* str) {
|
void felica_write_directory_tree(const FelicaSystem* system, FuriString* str) {
|
||||||
furi_check(data);
|
furi_check(system);
|
||||||
furi_check(str);
|
furi_check(str);
|
||||||
|
|
||||||
furi_string_cat_str(str, "\n");
|
furi_string_cat_str(str, "\n");
|
||||||
@@ -650,12 +673,12 @@ void felica_write_directory_tree(const FelicaData* data, FuriString* str) {
|
|||||||
uint8_t depth = 0;
|
uint8_t depth = 0;
|
||||||
|
|
||||||
size_t area_iter = 0;
|
size_t area_iter = 0;
|
||||||
const size_t area_count = simple_array_get_count(data->areas);
|
const size_t area_count = simple_array_get_count(system->areas);
|
||||||
const size_t service_count = simple_array_get_count(data->services);
|
const size_t service_count = simple_array_get_count(system->services);
|
||||||
|
|
||||||
for(size_t svc_idx = 0; svc_idx < service_count; ++svc_idx) {
|
for(size_t svc_idx = 0; svc_idx < service_count; ++svc_idx) {
|
||||||
while(area_iter < area_count) {
|
while(area_iter < area_count) {
|
||||||
const FelicaArea* next_area = simple_array_get(data->areas, area_iter);
|
const FelicaArea* next_area = simple_array_get(system->areas, area_iter);
|
||||||
if(next_area->first_idx != svc_idx) break;
|
if(next_area->first_idx != svc_idx) break;
|
||||||
|
|
||||||
for(uint8_t i = 0; i < depth - 1; ++i)
|
for(uint8_t i = 0; i < depth - 1; ++i)
|
||||||
@@ -667,7 +690,7 @@ void felica_write_directory_tree(const FelicaData* data, FuriString* str) {
|
|||||||
area_iter++;
|
area_iter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FelicaService* service = simple_array_get(data->services, svc_idx);
|
const FelicaService* service = simple_array_get(system->services, svc_idx);
|
||||||
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0;
|
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0;
|
||||||
|
|
||||||
for(uint8_t i = 0; i < depth - 1; ++i)
|
for(uint8_t i = 0; i < depth - 1; ++i)
|
||||||
|
|||||||
@@ -50,8 +50,10 @@ extern "C" {
|
|||||||
#define FELICA_TIME_SLOT_8 (0x07U)
|
#define FELICA_TIME_SLOT_8 (0x07U)
|
||||||
#define FELICA_TIME_SLOT_16 (0x0FU)
|
#define FELICA_TIME_SLOT_16 (0x0FU)
|
||||||
|
|
||||||
#define FELICA_CMD_LIST_SERVICE_CODE 0x0A
|
#define FELICA_CMD_LIST_SERVICE_CODE 0x0A
|
||||||
#define FELICA_CMD_LIST_SERVICE_CODE_RESP 0x0B
|
#define FELICA_CMD_LIST_SERVICE_CODE_RESP 0x0B
|
||||||
|
#define FELICA_CMD_REQUEST_SYSTEM_CODE 0x0C
|
||||||
|
#define FELICA_CMD_REQUEST_SYSTEM_CODE_RESP 0x0D
|
||||||
|
|
||||||
#define FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ (0b000001)
|
#define FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ (0b000001)
|
||||||
#define FELICA_SERVICE_ATTRIBUTE_READ_ONLY (0b000010)
|
#define FELICA_SERVICE_ATTRIBUTE_READ_ONLY (0b000010)
|
||||||
@@ -182,6 +184,14 @@ typedef struct {
|
|||||||
uint8_t block_idx;
|
uint8_t block_idx;
|
||||||
} FelicaPublicBlock;
|
} FelicaPublicBlock;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t system_code_idx;
|
||||||
|
uint16_t system_code;
|
||||||
|
SimpleArray* services;
|
||||||
|
SimpleArray* areas;
|
||||||
|
SimpleArray* public_blocks;
|
||||||
|
} FelicaSystem;
|
||||||
|
|
||||||
/** @brief Structure used to store Felica data and additional values about reading */
|
/** @brief Structure used to store Felica data and additional values about reading */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FelicaIDm idm;
|
FelicaIDm idm;
|
||||||
@@ -190,9 +200,8 @@ typedef struct {
|
|||||||
uint8_t blocks_read;
|
uint8_t blocks_read;
|
||||||
FelicaFSUnion data;
|
FelicaFSUnion data;
|
||||||
|
|
||||||
SimpleArray* services;
|
SimpleArray* systems;
|
||||||
SimpleArray* areas;
|
|
||||||
SimpleArray* public_blocks;
|
|
||||||
FelicaWorkflowType workflow_type;
|
FelicaWorkflowType workflow_type;
|
||||||
} FelicaData;
|
} FelicaData;
|
||||||
|
|
||||||
@@ -248,6 +257,12 @@ typedef struct {
|
|||||||
uint8_t data[];
|
uint8_t data[];
|
||||||
} FelicaListServiceCommandResponse;
|
} FelicaListServiceCommandResponse;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FelicaCommandHeaderRaw header;
|
||||||
|
uint8_t system_count;
|
||||||
|
uint8_t system_code[];
|
||||||
|
} FelicaListSystemCodeCommandResponse;
|
||||||
|
|
||||||
typedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse;
|
typedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse;
|
||||||
|
|
||||||
typedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse;
|
typedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse;
|
||||||
@@ -309,7 +324,7 @@ void felica_calculate_mac_write(
|
|||||||
const uint8_t* data,
|
const uint8_t* data,
|
||||||
uint8_t* mac);
|
uint8_t* mac);
|
||||||
|
|
||||||
void felica_write_directory_tree(const FelicaData* data, FuriString* str);
|
void felica_write_directory_tree(const FelicaSystem* system, FuriString* str);
|
||||||
|
|
||||||
void felica_get_workflow_type(FelicaData* data);
|
void felica_get_workflow_type(FelicaData* data);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,36 @@
|
|||||||
#include "felica_i.h"
|
#include "felica_i.h"
|
||||||
|
|
||||||
|
void felica_system_init(FelicaSystem* system) {
|
||||||
|
system->system_code = 0;
|
||||||
|
system->system_code_idx = 0;
|
||||||
|
system->services = simple_array_alloc(&felica_service_array_cfg);
|
||||||
|
system->areas = simple_array_alloc(&felica_area_array_cfg);
|
||||||
|
system->public_blocks = simple_array_alloc(&felica_public_block_array_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_system_reset(FelicaSystem* system) {
|
||||||
|
furi_check(system);
|
||||||
|
system->system_code = 0;
|
||||||
|
system->system_code_idx = 0;
|
||||||
|
furi_check(system->services);
|
||||||
|
furi_check(system->areas);
|
||||||
|
furi_check(system->public_blocks);
|
||||||
|
simple_array_free(system->services);
|
||||||
|
simple_array_free(system->areas);
|
||||||
|
simple_array_free(system->public_blocks);
|
||||||
|
memset(system, 0, sizeof(FelicaSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_system_copy(FelicaSystem* system, const FelicaSystem* other) {
|
||||||
|
furi_check(system);
|
||||||
|
furi_check(other);
|
||||||
|
system->system_code = other->system_code;
|
||||||
|
system->system_code_idx = other->system_code_idx;
|
||||||
|
simple_array_copy(system->services, other->services);
|
||||||
|
simple_array_copy(system->areas, other->areas);
|
||||||
|
simple_array_copy(system->public_blocks, other->public_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
const SimpleArrayConfig felica_service_array_cfg = {
|
const SimpleArrayConfig felica_service_array_cfg = {
|
||||||
.init = NULL,
|
.init = NULL,
|
||||||
.copy = NULL,
|
.copy = NULL,
|
||||||
@@ -20,3 +51,10 @@ const SimpleArrayConfig felica_public_block_array_cfg = {
|
|||||||
.reset = NULL,
|
.reset = NULL,
|
||||||
.type_size = sizeof(FelicaPublicBlock),
|
.type_size = sizeof(FelicaPublicBlock),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SimpleArrayConfig felica_system_array_cfg = {
|
||||||
|
.init = (SimpleArrayInit)felica_system_init,
|
||||||
|
.copy = (SimpleArrayCopy)felica_system_copy,
|
||||||
|
.reset = (SimpleArrayReset)felica_system_reset,
|
||||||
|
.type_size = sizeof(FelicaSystem),
|
||||||
|
};
|
||||||
|
|||||||
@@ -8,3 +8,8 @@
|
|||||||
extern const SimpleArrayConfig felica_service_array_cfg;
|
extern const SimpleArrayConfig felica_service_array_cfg;
|
||||||
extern const SimpleArrayConfig felica_area_array_cfg;
|
extern const SimpleArrayConfig felica_area_array_cfg;
|
||||||
extern const SimpleArrayConfig felica_public_block_array_cfg;
|
extern const SimpleArrayConfig felica_public_block_array_cfg;
|
||||||
|
extern const SimpleArrayConfig felica_system_array_cfg;
|
||||||
|
|
||||||
|
void felica_system_init(FelicaSystem* system);
|
||||||
|
void felica_system_reset(FelicaSystem* system);
|
||||||
|
void felica_system_copy(FelicaSystem* system, const FelicaSystem* other);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
ARRAY_DEF(felica_service_array, FelicaService, M_POD_OPLIST); // -V658
|
ARRAY_DEF(felica_service_array, FelicaService, M_POD_OPLIST); // -V658
|
||||||
ARRAY_DEF(felica_area_array, FelicaArea, M_POD_OPLIST); // -V658
|
ARRAY_DEF(felica_area_array, FelicaArea, M_POD_OPLIST); // -V658
|
||||||
ARRAY_DEF(felica_public_block_array, FelicaPublicBlock, M_POD_OPLIST); // -V658
|
ARRAY_DEF(felica_public_block_array, FelicaPublicBlock, M_POD_OPLIST); // -V658
|
||||||
|
ARRAY_DEF(felica_system_array, FelicaSystem, M_POD_OPLIST); // -V658
|
||||||
|
|
||||||
typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance);
|
typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance);
|
||||||
|
|
||||||
@@ -43,6 +44,9 @@ static FelicaPoller* felica_poller_alloc(Nfc* nfc) {
|
|||||||
instance->general_event.event_data = &instance->felica_event;
|
instance->general_event.event_data = &instance->felica_event;
|
||||||
instance->general_event.instance = instance;
|
instance->general_event.instance = instance;
|
||||||
|
|
||||||
|
instance->systems_read = 0;
|
||||||
|
instance->systems_total = 0;
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +98,7 @@ NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) {
|
|||||||
|
|
||||||
switch(instance->data->workflow_type) {
|
switch(instance->data->workflow_type) {
|
||||||
case FelicaStandard:
|
case FelicaStandard:
|
||||||
instance->state = FelicaPollerStateTraverseStandardSystem;
|
instance->state = FelicaPollerStateListSystem;
|
||||||
break;
|
break;
|
||||||
case FelicaLite:
|
case FelicaLite:
|
||||||
instance->state = FelicaPollerStateReadLiteBlocks;
|
instance->state = FelicaPollerStateReadLiteBlocks;
|
||||||
@@ -117,6 +121,44 @@ NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) {
|
|||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_list_system(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "List System");
|
||||||
|
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
|
||||||
|
FelicaListSystemCodeCommandResponse* response_system_code;
|
||||||
|
FelicaError error = felica_poller_list_system_code(instance, &response_system_code);
|
||||||
|
|
||||||
|
instance->systems_total = response_system_code->system_count;
|
||||||
|
simple_array_init(instance->data->systems, instance->systems_total);
|
||||||
|
uint8_t* system_codes = response_system_code->system_code;
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < instance->systems_total; i++) {
|
||||||
|
FelicaSystem* system = simple_array_get(instance->data->systems, i);
|
||||||
|
system->system_code = system_codes[i * 2] << 8 | system_codes[i * 2 + 1];
|
||||||
|
system->system_code_idx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(error == FelicaErrorNone) {
|
||||||
|
instance->state = FelicaPollerStateSelectSystemIndex;
|
||||||
|
} else if(error != FelicaErrorTimeout) {
|
||||||
|
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||||
|
instance->felica_event_data.error = error;
|
||||||
|
instance->state = FelicaPollerStateReadFailed;
|
||||||
|
}
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_select_system_idx(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Select System Index %d", instance->systems_read);
|
||||||
|
uint8_t system_index_mask = instance->systems_read << 4;
|
||||||
|
instance->data->idm.data[0] &= 0x0F;
|
||||||
|
instance->data->idm.data[0] |= system_index_mask;
|
||||||
|
instance->state = FelicaPollerStateTraverseStandardSystem;
|
||||||
|
|
||||||
|
return NfcCommandContinue;
|
||||||
|
}
|
||||||
|
|
||||||
NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {
|
NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {
|
||||||
FURI_LOG_D(TAG, "Auth Internal");
|
FURI_LOG_D(TAG, "Auth Internal");
|
||||||
|
|
||||||
@@ -274,6 +316,8 @@ NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* in
|
|||||||
service->code = code_begin;
|
service->code = code_begin;
|
||||||
service->attr = (uint8_t)(code_begin & 0x3F);
|
service->attr = (uint8_t)(code_begin & 0x3F);
|
||||||
|
|
||||||
|
FURI_LOG_D(TAG, "Service %04X", service->code);
|
||||||
|
|
||||||
if(felica_area_array_size(area_buffer)) {
|
if(felica_area_array_size(area_buffer)) {
|
||||||
FelicaArea* current_area = felica_area_array_back(area_buffer);
|
FelicaArea* current_area = felica_area_array_back(area_buffer);
|
||||||
current_area->last_idx = (uint16_t)(felica_service_array_size(service_buffer) - 1);
|
current_area->last_idx = (uint16_t)(felica_service_array_size(service_buffer) - 1);
|
||||||
@@ -284,31 +328,30 @@ NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* in
|
|||||||
const size_t service_num = felica_service_array_size(service_buffer);
|
const size_t service_num = felica_service_array_size(service_buffer);
|
||||||
const size_t area_num = felica_area_array_size(area_buffer);
|
const size_t area_num = felica_area_array_size(area_buffer);
|
||||||
|
|
||||||
|
FelicaSystem* system = simple_array_get(instance->data->systems, instance->systems_read);
|
||||||
if(service_num) {
|
if(service_num) {
|
||||||
simple_array_init(instance->data->services, (uint32_t)service_num);
|
simple_array_init(system->services, (uint32_t)service_num);
|
||||||
memcpy(
|
memcpy(
|
||||||
simple_array_get(instance->data->services, 0),
|
simple_array_get(system->services, 0),
|
||||||
service_buffer->ptr,
|
service_buffer->ptr,
|
||||||
service_num * sizeof(FelicaService));
|
service_num * sizeof(FelicaService));
|
||||||
} else {
|
} else {
|
||||||
simple_array_reset(instance->data->services);
|
simple_array_reset(system->services);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(area_num) {
|
if(area_num) {
|
||||||
simple_array_init(instance->data->areas, (uint32_t)area_num);
|
simple_array_init(system->areas, (uint32_t)area_num);
|
||||||
memcpy(
|
memcpy(
|
||||||
simple_array_get(instance->data->areas, 0),
|
simple_array_get(system->areas, 0), area_buffer->ptr, area_num * sizeof(FelicaArea));
|
||||||
area_buffer->ptr,
|
|
||||||
area_num * sizeof(FelicaArea));
|
|
||||||
} else {
|
} else {
|
||||||
simple_array_reset(instance->data->areas);
|
simple_array_reset(system->areas);
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(
|
||||||
TAG,
|
TAG,
|
||||||
"Services found: %lu, Areas found: %lu",
|
"Services found: %lu, Areas found: %lu",
|
||||||
simple_array_get_count(instance->data->services),
|
simple_array_get_count(system->services),
|
||||||
simple_array_get_count(instance->data->areas));
|
simple_array_get_count(system->areas));
|
||||||
|
|
||||||
felica_service_array_clear(service_buffer);
|
felica_service_array_clear(service_buffer);
|
||||||
felica_area_array_clear(area_buffer);
|
felica_area_array_clear(area_buffer);
|
||||||
@@ -320,22 +363,22 @@ NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* in
|
|||||||
NfcCommand felica_poller_state_handler_read_standard_blocks(FelicaPoller* instance) {
|
NfcCommand felica_poller_state_handler_read_standard_blocks(FelicaPoller* instance) {
|
||||||
FURI_LOG_D(TAG, "Read Standard Blocks");
|
FURI_LOG_D(TAG, "Read Standard Blocks");
|
||||||
|
|
||||||
const uint32_t service_count = simple_array_get_count(instance->data->services);
|
FelicaSystem* system = simple_array_get(instance->data->systems, instance->systems_read);
|
||||||
|
const uint32_t service_count = simple_array_get_count(system->services);
|
||||||
|
|
||||||
felica_public_block_array_t public_block_buffer;
|
felica_public_block_array_t public_block_buffer;
|
||||||
felica_public_block_array_init(public_block_buffer);
|
felica_public_block_array_init(public_block_buffer);
|
||||||
|
|
||||||
instance->state = FelicaPollerStateReadSuccess;
|
|
||||||
bool have_read_anything = false;
|
bool have_read_anything = false;
|
||||||
|
FelicaError error = FelicaErrorNone;
|
||||||
|
|
||||||
for(uint32_t i = 0; i < service_count; i++) {
|
for(uint32_t i = 0; i < service_count; i++) {
|
||||||
const FelicaService* service = simple_array_get(instance->data->services, i);
|
const FelicaService* service = simple_array_get(system->services, i);
|
||||||
|
|
||||||
if((service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 0) continue;
|
if((service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 0) continue;
|
||||||
|
|
||||||
uint8_t block_count = 1;
|
uint8_t block_count = 1;
|
||||||
uint8_t block_list[1] = {0};
|
uint8_t block_list[1] = {0};
|
||||||
FelicaError error = FelicaErrorNone;
|
|
||||||
FelicaPollerReadCommandResponse* response;
|
FelicaPollerReadCommandResponse* response;
|
||||||
do {
|
do {
|
||||||
error = felica_poller_read_blocks(
|
error = felica_poller_read_blocks(
|
||||||
@@ -369,11 +412,20 @@ NfcCommand felica_poller_state_handler_read_standard_blocks(FelicaPoller* instan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(error == FelicaErrorNone) {
|
||||||
|
instance->systems_read++;
|
||||||
|
if(instance->systems_read == instance->systems_total) {
|
||||||
|
instance->state = FelicaPollerStateReadSuccess;
|
||||||
|
} else {
|
||||||
|
instance->state = FelicaPollerStateSelectSystemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(have_read_anything) {
|
if(have_read_anything) {
|
||||||
const size_t n = felica_public_block_array_size(public_block_buffer);
|
const size_t n = felica_public_block_array_size(public_block_buffer);
|
||||||
simple_array_init(instance->data->public_blocks, (uint32_t)n);
|
simple_array_init(system->public_blocks, (uint32_t)n);
|
||||||
memcpy(
|
memcpy(
|
||||||
simple_array_get(instance->data->public_blocks, 0),
|
simple_array_get(system->public_blocks, 0),
|
||||||
public_block_buffer->ptr,
|
public_block_buffer->ptr,
|
||||||
n * sizeof(FelicaPublicBlock));
|
n * sizeof(FelicaPublicBlock));
|
||||||
}
|
}
|
||||||
@@ -434,9 +486,12 @@ NfcCommand felica_poller_state_handler_read_lite_blocks(FelicaPoller* instance)
|
|||||||
NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) {
|
NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) {
|
||||||
FURI_LOG_D(TAG, "Read Success");
|
FURI_LOG_D(TAG, "Read Success");
|
||||||
|
|
||||||
if(!instance->auth.context.auth_status.internal ||
|
if((!instance->auth.context.auth_status.internal ||
|
||||||
!instance->auth.context.auth_status.external) {
|
!instance->auth.context.auth_status.external) &&
|
||||||
instance->data->blocks_read--;
|
instance->data->workflow_type == FelicaLite) {
|
||||||
|
if(instance->data->blocks_read != 0) {
|
||||||
|
instance->data->blocks_read--;
|
||||||
|
}
|
||||||
instance->felica_event.type = FelicaPollerEventTypeIncomplete;
|
instance->felica_event.type = FelicaPollerEventTypeIncomplete;
|
||||||
} else {
|
} else {
|
||||||
memcpy(
|
memcpy(
|
||||||
@@ -446,6 +501,8 @@ NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) {
|
|||||||
instance->felica_event.type = FelicaPollerEventTypeReady;
|
instance->felica_event.type = FelicaPollerEventTypeReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance->data->idm.data[0] &= 0x0F;
|
||||||
|
|
||||||
instance->felica_event_data.error = FelicaErrorNone;
|
instance->felica_event_data.error = FelicaErrorNone;
|
||||||
return instance->callback(instance->general_event, instance->context);
|
return instance->callback(instance->general_event, instance->context);
|
||||||
}
|
}
|
||||||
@@ -453,6 +510,7 @@ NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) {
|
|||||||
NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) {
|
NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) {
|
||||||
FURI_LOG_D(TAG, "Read Fail");
|
FURI_LOG_D(TAG, "Read Fail");
|
||||||
instance->callback(instance->general_event, instance->context);
|
instance->callback(instance->general_event, instance->context);
|
||||||
|
instance->data->idm.data[0] &= 0x0F;
|
||||||
|
|
||||||
return NfcCommandStop;
|
return NfcCommandStop;
|
||||||
}
|
}
|
||||||
@@ -460,6 +518,8 @@ NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) {
|
|||||||
static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum] = {
|
static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum] = {
|
||||||
[FelicaPollerStateIdle] = felica_poller_state_handler_idle,
|
[FelicaPollerStateIdle] = felica_poller_state_handler_idle,
|
||||||
[FelicaPollerStateActivated] = felica_poller_state_handler_activate,
|
[FelicaPollerStateActivated] = felica_poller_state_handler_activate,
|
||||||
|
[FelicaPollerStateListSystem] = felica_poller_state_handler_list_system,
|
||||||
|
[FelicaPollerStateSelectSystemIndex] = felica_poller_state_handler_select_system_idx,
|
||||||
[FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,
|
[FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,
|
||||||
[FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external,
|
[FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external,
|
||||||
[FelicaPollerStateTraverseStandardSystem] =
|
[FelicaPollerStateTraverseStandardSystem] =
|
||||||
|
|||||||
@@ -233,7 +233,9 @@ static void felica_poller_prepare_tx_buffer_raw(
|
|||||||
cmd.length = sizeof(FelicaCommandHeaderRaw) + data_length;
|
cmd.length = sizeof(FelicaCommandHeaderRaw) + data_length;
|
||||||
bit_buffer_reset(instance->tx_buffer);
|
bit_buffer_reset(instance->tx_buffer);
|
||||||
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeaderRaw));
|
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeaderRaw));
|
||||||
bit_buffer_append_bytes(instance->tx_buffer, data, data_length);
|
if(data_length > 0) {
|
||||||
|
bit_buffer_append_bytes(instance->tx_buffer, data, data_length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FelicaError felica_poller_list_service_by_cursor(
|
FelicaError felica_poller_list_service_by_cursor(
|
||||||
@@ -264,3 +266,31 @@ FelicaError felica_poller_list_service_by_cursor(
|
|||||||
*response_ptr = (FelicaListServiceCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
|
*response_ptr = (FelicaListServiceCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FelicaError felica_poller_list_system_code(
|
||||||
|
FelicaPoller* instance,
|
||||||
|
FelicaListSystemCodeCommandResponse** const response_ptr) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(response_ptr);
|
||||||
|
|
||||||
|
uint8_t data[] = {0};
|
||||||
|
|
||||||
|
felica_poller_prepare_tx_buffer_raw(instance, FELICA_CMD_REQUEST_SYSTEM_CODE, data, 0);
|
||||||
|
|
||||||
|
bit_buffer_reset(instance->rx_buffer);
|
||||||
|
|
||||||
|
FelicaError error = felica_poller_frame_exchange(
|
||||||
|
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
|
||||||
|
if(error != FelicaErrorNone) {
|
||||||
|
FURI_LOG_E(TAG, "Request system code failed with error: %d", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rx_len = bit_buffer_get_size_bytes(instance->rx_buffer);
|
||||||
|
if(rx_len < sizeof(FelicaCommandHeaderRaw) + 3) return FelicaErrorProtocol;
|
||||||
|
// at least 1 system code + the count being 0x01
|
||||||
|
|
||||||
|
// error is known to be FelicaErrorNone here
|
||||||
|
*response_ptr = (FelicaListSystemCodeCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ extern "C" {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
FelicaPollerStateIdle,
|
FelicaPollerStateIdle,
|
||||||
FelicaPollerStateActivated,
|
FelicaPollerStateActivated,
|
||||||
|
FelicaPollerStateListSystem,
|
||||||
|
FelicaPollerStateSelectSystemIndex,
|
||||||
FelicaPollerStateAuthenticateInternal,
|
FelicaPollerStateAuthenticateInternal,
|
||||||
FelicaPollerStateAuthenticateExternal,
|
FelicaPollerStateAuthenticateExternal,
|
||||||
FelicaPollerStateTraverseStandardSystem,
|
FelicaPollerStateTraverseStandardSystem,
|
||||||
@@ -42,6 +44,8 @@ struct FelicaPoller {
|
|||||||
FelicaPollerEventData felica_event_data;
|
FelicaPollerEventData felica_event_data;
|
||||||
NfcGenericCallback callback;
|
NfcGenericCallback callback;
|
||||||
uint8_t block_index;
|
uint8_t block_index;
|
||||||
|
uint8_t systems_read;
|
||||||
|
uint8_t systems_total;
|
||||||
void* context;
|
void* context;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -116,6 +120,10 @@ FelicaError felica_poller_list_service_by_cursor(
|
|||||||
uint16_t cursor,
|
uint16_t cursor,
|
||||||
FelicaListServiceCommandResponse** response_ptr);
|
FelicaListServiceCommandResponse** response_ptr);
|
||||||
|
|
||||||
|
FelicaError felica_poller_list_system_code(
|
||||||
|
FelicaPoller* instance,
|
||||||
|
FelicaListSystemCodeCommandResponse** response_ptr);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -566,16 +566,12 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in
|
|||||||
instance->error = mf_ultralight_poller_read_page(instance, start_page, &data);
|
instance->error = mf_ultralight_poller_read_page(instance, start_page, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regression review
|
|
||||||
const uint8_t read_cnt = instance->data->type == MfUltralightTypeMfulC ? 1 : 4;
|
|
||||||
if(instance->error == MfUltralightErrorNone) {
|
if(instance->error == MfUltralightErrorNone) {
|
||||||
for(size_t i = 0; i < read_cnt; i++) {
|
if(start_page < instance->pages_total) {
|
||||||
if(start_page + i < instance->pages_total) {
|
FURI_LOG_D(TAG, "Read page %d success", start_page);
|
||||||
FURI_LOG_D(TAG, "Read page %d success", start_page + i);
|
instance->data->page[start_page] = data.page[0];
|
||||||
instance->data->page[start_page + i] = data.page[i];
|
instance->pages_read++;
|
||||||
instance->pages_read++;
|
instance->data->pages_read = instance->pages_read;
|
||||||
instance->data->pages_read = instance->pages_read;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(instance->pages_read == instance->pages_total) {
|
if(instance->pages_read == instance->pages_total) {
|
||||||
|
|||||||
@@ -275,14 +275,27 @@ static bool subghz_protocol_alutech_at_4n_gen_data(
|
|||||||
instance->generic.serial = (uint32_t)(data >> 24) & 0xFFFFFFFF;
|
instance->generic.serial = (uint32_t)(data >> 24) & 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
} else {
|
||||||
instance->generic.cnt = 0;
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
crc = subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)(instance->generic.cnt & 0xFF));
|
crc = subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)(instance->generic.cnt & 0xFF));
|
||||||
data = (uint64_t)crc << 56 | (uint64_t)instance->generic.serial << 24 |
|
data = (uint64_t)crc << 56 | (uint64_t)instance->generic.serial << 24 |
|
||||||
|
|||||||
@@ -187,14 +187,27 @@ static void subghz_protocol_encoder_came_atomo_get_upload(
|
|||||||
|
|
||||||
uint8_t pack[8] = {};
|
uint8_t pack[8] = {};
|
||||||
|
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
} else {
|
||||||
instance->generic.cnt = 0;
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save original button for later use
|
// Save original button for later use
|
||||||
@@ -420,8 +433,11 @@ void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t
|
|||||||
ManchesterEvent event = ManchesterEventReset;
|
ManchesterEvent event = ManchesterEventReset;
|
||||||
switch(instance->decoder.parser_step) {
|
switch(instance->decoder.parser_step) {
|
||||||
case CameAtomoDecoderStepReset:
|
case CameAtomoDecoderStepReset:
|
||||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) <
|
// There are two known options for the header: 72K us (TOP42R, TOP44R) or 12k us (found on TOP44RBN)
|
||||||
subghz_protocol_came_atomo_const.te_delta * 40)) {
|
if((!level) && ((DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 10) <
|
||||||
|
subghz_protocol_came_atomo_const.te_delta * 20) ||
|
||||||
|
(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) <
|
||||||
|
subghz_protocol_came_atomo_const.te_delta * 40))) {
|
||||||
//Found header CAME
|
//Found header CAME
|
||||||
instance->decoder.parser_step = CameAtomoDecoderStepDecoderData;
|
instance->decoder.parser_step = CameAtomoDecoderStepDecoderData;
|
||||||
instance->decoder.decode_data = 0;
|
instance->decoder.decode_data = 0;
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst
|
|||||||
data_prg[0] = 0x00;
|
data_prg[0] = 0x00;
|
||||||
|
|
||||||
if(allow_zero_seed || (instance->generic.seed != 0x0)) {
|
if(allow_zero_seed || (instance->generic.seed != 0x0)) {
|
||||||
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) {
|
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFE)) {
|
||||||
if(instance->generic.cnt < 0xFFFFF) {
|
if(instance->generic.cnt < 0xFFFFF) {
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >
|
||||||
0xFFFFF) {
|
0xFFFFF) {
|
||||||
@@ -160,7 +160,7 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(temp_counter_backup != 0x0) {
|
if(temp_counter_backup != 0x0) {
|
||||||
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) {
|
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFE)) {
|
||||||
if(temp_counter_backup < 0xFFFFF) {
|
if(temp_counter_backup < 0xFFFFF) {
|
||||||
if((temp_counter_backup + furi_hal_subghz_get_rolling_counter_mult()) >
|
if((temp_counter_backup + furi_hal_subghz_get_rolling_counter_mult()) >
|
||||||
0xFFFFF) {
|
0xFFFFF) {
|
||||||
@@ -242,7 +242,7 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(allow_zero_seed || (instance->generic.seed != 0x0)) {
|
if(allow_zero_seed || (instance->generic.seed != 0x0)) {
|
||||||
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) {
|
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFE)) {
|
||||||
if(instance->generic.cnt < 0xFFFFF) {
|
if(instance->generic.cnt < 0xFFFFF) {
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >
|
||||||
0xFFFFF) {
|
0xFFFFF) {
|
||||||
|
|||||||
@@ -147,17 +147,28 @@ static void subghz_protocol_encoder_hay21_get_upload(SubGhzProtocolEncoderHay21*
|
|||||||
instance->generic.btn = subghz_protocol_hay21_get_btn_code();
|
instance->generic.btn = subghz_protocol_hay21_get_btn_code();
|
||||||
|
|
||||||
// Counter increment
|
// Counter increment
|
||||||
if(instance->generic.cnt < 0xF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
if(furi_hal_subghz_get_rolling_counter_mult() >= 0xF) {
|
||||||
|
instance->generic.cnt = 0xF;
|
||||||
|
}
|
||||||
|
} else if(instance->generic.cnt >= 0xF) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if((instance->generic.cnt + 0x1) > 0xF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xE) {
|
||||||
|
instance->generic.cnt = 0xE;
|
||||||
} else {
|
} else {
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
instance->generic.cnt++;
|
||||||
}
|
}
|
||||||
if(furi_hal_subghz_get_rolling_counter_mult() >= 0xF) {
|
|
||||||
instance->generic.cnt = 0xF;
|
|
||||||
}
|
|
||||||
} else if(instance->generic.cnt >= 0xF) {
|
|
||||||
instance->generic.cnt = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconstruction of the data
|
// Reconstruction of the data
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
#include "honeywell.h"
|
#include "honeywell.h"
|
||||||
#include <lib/toolbox/manchester_decoder.h>
|
|
||||||
|
|
||||||
//Created by HTotoo 2023-10-30
|
#include <lib/toolbox/manchester_decoder.h>
|
||||||
//Got a lot of help from LiQuiDz.
|
#include <lib/toolbox/manchester_encoder.h>
|
||||||
//Protocol decoding help from: https://github.com/merbanan/rtl_433/blob/master/src/devices/honeywell.c
|
#include "../blocks/const.h"
|
||||||
|
#include "../blocks/decoder.h"
|
||||||
|
#include "../blocks/encoder.h"
|
||||||
|
#include "../blocks/generic.h"
|
||||||
|
#include "../blocks/math.h"
|
||||||
|
|
||||||
|
// Created by HTotoo 2023-10-30
|
||||||
|
// Got a lot of help from LiQuiDz.
|
||||||
|
// Protocol decoding help from: https://github.com/merbanan/rtl_433/blob/master/src/devices/honeywell.c
|
||||||
|
// Fixes and style changes to use similar codebase as other protocols by @xMasterX 2025-10
|
||||||
|
|
||||||
/*
|
/*
|
||||||
64 bit packets, repeated multiple times per open/close event.
|
64 bit packets, repeated multiple times per open/close event.
|
||||||
@@ -23,6 +31,95 @@ Data layout:
|
|||||||
|
|
||||||
#define TAG "SubGhzProtocolHoneywell"
|
#define TAG "SubGhzProtocolHoneywell"
|
||||||
|
|
||||||
|
static const SubGhzBlockConst subghz_protocol_honeywell_const = {
|
||||||
|
.te_long = 280,
|
||||||
|
.te_short = 143,
|
||||||
|
.te_delta = 51,
|
||||||
|
.min_count_bit_for_found = 64,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SubGhzProtocolDecoderHoneywell {
|
||||||
|
SubGhzProtocolDecoderBase base;
|
||||||
|
SubGhzBlockGeneric generic;
|
||||||
|
SubGhzBlockDecoder decoder;
|
||||||
|
ManchesterState manchester_saved_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SubGhzProtocolEncoderHoneywell {
|
||||||
|
SubGhzProtocolEncoderBase base;
|
||||||
|
SubGhzBlockGeneric generic;
|
||||||
|
SubGhzProtocolBlockEncoder encoder;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocolDecoder subghz_protocol_honeywell_decoder = {
|
||||||
|
.alloc = subghz_protocol_decoder_honeywell_alloc,
|
||||||
|
.free = subghz_protocol_decoder_honeywell_free,
|
||||||
|
.feed = subghz_protocol_decoder_honeywell_feed,
|
||||||
|
.reset = subghz_protocol_decoder_honeywell_reset,
|
||||||
|
.get_hash_data = NULL,
|
||||||
|
.get_hash_data_long = subghz_protocol_decoder_honeywell_get_hash_data,
|
||||||
|
.serialize = subghz_protocol_decoder_honeywell_serialize,
|
||||||
|
.deserialize = subghz_protocol_decoder_honeywell_deserialize,
|
||||||
|
.get_string = subghz_protocol_decoder_honeywell_get_string,
|
||||||
|
.get_string_brief = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocolEncoder subghz_protocol_honeywell_encoder = {
|
||||||
|
.alloc = subghz_protocol_encoder_honeywell_alloc,
|
||||||
|
.free = subghz_protocol_encoder_honeywell_free,
|
||||||
|
.deserialize = subghz_protocol_encoder_honeywell_deserialize,
|
||||||
|
.stop = subghz_protocol_encoder_honeywell_stop,
|
||||||
|
.yield = subghz_protocol_encoder_honeywell_yield,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SubGhzProtocol subghz_protocol_honeywell = {
|
||||||
|
.name = SUBGHZ_PROTOCOL_HONEYWELL_NAME,
|
||||||
|
.type = SubGhzProtocolTypeStatic,
|
||||||
|
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||||
|
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||||
|
.encoder = &subghz_protocol_honeywell_encoder,
|
||||||
|
.decoder = &subghz_protocol_honeywell_decoder,
|
||||||
|
.filter = SubGhzProtocolFilter_Sensors,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static void subghz_protocol_decoder_honeywell_addbit(void* context, bool data);
|
||||||
|
|
||||||
|
void* subghz_protocol_decoder_honeywell_alloc(SubGhzEnvironment* environment) {
|
||||||
|
UNUSED(environment);
|
||||||
|
SubGhzProtocolDecoderHoneywell* instance = malloc(sizeof(SubGhzProtocolDecoderHoneywell));
|
||||||
|
instance->base.protocol = &subghz_protocol_honeywell;
|
||||||
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* subghz_protocol_encoder_honeywell_alloc(SubGhzEnvironment* environment) {
|
||||||
|
UNUSED(environment);
|
||||||
|
SubGhzProtocolEncoderHoneywell* instance = malloc(sizeof(SubGhzProtocolEncoderHoneywell));
|
||||||
|
|
||||||
|
instance->base.protocol = &subghz_protocol_honeywell;
|
||||||
|
instance->generic.protocol_name = instance->base.protocol->name;
|
||||||
|
|
||||||
|
instance->encoder.repeat = 4;
|
||||||
|
instance->encoder.size_upload = 64 * 2 + 10;
|
||||||
|
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||||
|
instance->encoder.is_running = false;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_protocol_encoder_honeywell_free(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzProtocolEncoderHoneywell* instance = context;
|
||||||
|
free(instance->encoder.upload);
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_protocol_decoder_honeywell_free(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzProtocolDecoderHoneywell* instance = context;
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t subghz_protocol_honeywell_crc16(
|
uint16_t subghz_protocol_honeywell_crc16(
|
||||||
uint8_t const message[],
|
uint8_t const message[],
|
||||||
unsigned nBytes,
|
unsigned nBytes,
|
||||||
@@ -44,195 +141,6 @@ uint16_t subghz_protocol_honeywell_crc16(
|
|||||||
return remainder;
|
return remainder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_protocol_decoder_honeywell_free(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = context;
|
|
||||||
free(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
void subghz_protocol_decoder_honeywell_reset(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = context;
|
|
||||||
instance->decoder.decode_data = 0;
|
|
||||||
instance->decoder.decode_count_bit = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) {
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = context;
|
|
||||||
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;
|
|
||||||
instance->decoder.decode_count_bit++;
|
|
||||||
|
|
||||||
if(instance->decoder.decode_count_bit < 62) return;
|
|
||||||
|
|
||||||
uint16_t preamble = (instance->decoder.decode_data >> 48) & 0xFFFF;
|
|
||||||
//can be multiple, since flipper can't read it well..
|
|
||||||
if(preamble == 0b0011111111111110 || preamble == 0b0111111111111110 ||
|
|
||||||
preamble == 0b1111111111111110) {
|
|
||||||
uint8_t datatocrc[4];
|
|
||||||
datatocrc[0] = (instance->decoder.decode_data >> 40) & 0xFFFF;
|
|
||||||
datatocrc[1] = (instance->decoder.decode_data >> 32) & 0xFFFF;
|
|
||||||
datatocrc[2] = (instance->decoder.decode_data >> 24) & 0xFFFF;
|
|
||||||
datatocrc[3] = (instance->decoder.decode_data >> 16) & 0xFFFF;
|
|
||||||
uint8_t channel = (instance->decoder.decode_data >> 44) & 0xF;
|
|
||||||
uint16_t crc_calc = 0;
|
|
||||||
if(channel == 0x2 || channel == 0x4 || channel == 0xA) {
|
|
||||||
// 2GIG brand
|
|
||||||
crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8050, 0);
|
|
||||||
} else if(channel == 0x8) {
|
|
||||||
crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8005, 0);
|
|
||||||
} else {
|
|
||||||
instance->decoder.decode_data = 0;
|
|
||||||
instance->decoder.decode_count_bit = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint16_t crc = instance->decoder.decode_data & 0xFFFF;
|
|
||||||
if(crc == crc_calc) {
|
|
||||||
//the data is good. process it.
|
|
||||||
instance->generic.data = instance->decoder.decode_data;
|
|
||||||
instance->generic.data_count_bit =
|
|
||||||
instance->decoder
|
|
||||||
.decode_count_bit; //maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it
|
|
||||||
if(instance->base.callback)
|
|
||||||
instance->base.callback(&instance->base, instance->base.context);
|
|
||||||
instance->decoder.decode_data = 0;
|
|
||||||
instance->decoder.decode_count_bit = 0;
|
|
||||||
} else {
|
|
||||||
instance->decoder.decode_data = 0;
|
|
||||||
instance->decoder.decode_count_bit = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if(instance->decoder.decode_count_bit >= 64) {
|
|
||||||
instance->decoder.decode_data = 0;
|
|
||||||
instance->decoder.decode_count_bit = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t duration) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = context;
|
|
||||||
|
|
||||||
ManchesterEvent event = ManchesterEventReset;
|
|
||||||
if(!level) {
|
|
||||||
if(DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_short) <
|
|
||||||
subghz_protocol_honeywell_const.te_delta) {
|
|
||||||
event = ManchesterEventShortLow;
|
|
||||||
} else if(
|
|
||||||
DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_long) <
|
|
||||||
subghz_protocol_honeywell_const.te_delta * 2) {
|
|
||||||
event = ManchesterEventLongLow;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_short) <
|
|
||||||
subghz_protocol_honeywell_const.te_delta) {
|
|
||||||
event = ManchesterEventShortHigh;
|
|
||||||
} else if(
|
|
||||||
DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_long) <
|
|
||||||
subghz_protocol_honeywell_const.te_delta * 2) {
|
|
||||||
event = ManchesterEventLongHigh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(event != ManchesterEventReset) {
|
|
||||||
bool data;
|
|
||||||
bool data_ok = manchester_advance(
|
|
||||||
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
|
|
||||||
if(data_ok) {
|
|
||||||
subghz_protocol_decoder_honeywell_addbit(instance, data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
instance->decoder.decode_data = 0;
|
|
||||||
instance->decoder.decode_count_bit = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t subghz_protocol_decoder_honeywell_get_hash_data(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = context;
|
|
||||||
return subghz_protocol_blocks_get_hash_data_long(
|
|
||||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SubGhzProtocolStatus subghz_protocol_decoder_honeywell_serialize(
|
|
||||||
void* context,
|
|
||||||
FlipperFormat* flipper_format,
|
|
||||||
SubGhzRadioPreset* preset) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = context;
|
|
||||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
|
||||||
}
|
|
||||||
|
|
||||||
SubGhzProtocolStatus
|
|
||||||
subghz_protocol_decoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = context;
|
|
||||||
return subghz_block_generic_deserialize_check_count_bit(
|
|
||||||
&instance->generic,
|
|
||||||
flipper_format,
|
|
||||||
subghz_protocol_honeywell_const.min_count_bit_for_found);
|
|
||||||
}
|
|
||||||
|
|
||||||
void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* output) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = context;
|
|
||||||
|
|
||||||
// Parse here and not in decode to avoid visual glitches when loading from file
|
|
||||||
instance->generic.serial = (instance->generic.data >> 24) & 0xFFFFF;
|
|
||||||
instance->generic.btn = (instance->generic.data >> 16) &
|
|
||||||
0xFF; //not exactly button, but can contain btn data too.
|
|
||||||
|
|
||||||
uint8_t channel = (instance->generic.data >> 44) & 0xF;
|
|
||||||
uint8_t contact = (instance->generic.btn & 0x80) >> 7;
|
|
||||||
uint8_t tamper = (instance->generic.btn & 0x40) >> 6;
|
|
||||||
uint8_t reed = (instance->generic.btn & 0x20) >> 5;
|
|
||||||
uint8_t alarm = (instance->generic.btn & 0x10) >> 4;
|
|
||||||
uint8_t battery_low = (instance->generic.btn & 0x08) >> 3;
|
|
||||||
uint8_t heartbeat = (instance->generic.btn & 0x04) >> 2;
|
|
||||||
|
|
||||||
furi_string_cat_printf(
|
|
||||||
output,
|
|
||||||
"%s\r\n%dbit "
|
|
||||||
"Sn:%07lu\r\nCh:%u Bat:%d Hb: %d\r\n"
|
|
||||||
"L1: %u, L2: %u, L3: %u, L4: %u\r\n",
|
|
||||||
instance->generic.protocol_name,
|
|
||||||
instance->generic.data_count_bit,
|
|
||||||
instance->generic.serial,
|
|
||||||
channel,
|
|
||||||
battery_low,
|
|
||||||
heartbeat,
|
|
||||||
contact,
|
|
||||||
reed,
|
|
||||||
alarm,
|
|
||||||
tamper);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* subghz_protocol_decoder_honeywell_alloc(SubGhzEnvironment* environment) {
|
|
||||||
UNUSED(environment);
|
|
||||||
SubGhzProtocolDecoderHoneywell* instance = malloc(sizeof(SubGhzProtocolDecoderHoneywell));
|
|
||||||
instance->base.protocol = &subghz_protocol_honeywell;
|
|
||||||
instance->generic.protocol_name = instance->base.protocol->name;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* subghz_protocol_encoder_honeywell_alloc(SubGhzEnvironment* environment) {
|
|
||||||
UNUSED(environment);
|
|
||||||
SubGhzProtocolEncoderHoneywell* instance = malloc(sizeof(SubGhzProtocolEncoderHoneywell));
|
|
||||||
|
|
||||||
instance->base.protocol = &subghz_protocol_honeywell;
|
|
||||||
instance->generic.protocol_name = instance->base.protocol->name;
|
|
||||||
|
|
||||||
instance->encoder.repeat = 3;
|
|
||||||
instance->encoder.size_upload = 64 * 2 + 10;
|
|
||||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
|
||||||
instance->encoder.is_running = false;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void subghz_protocol_encoder_honeywell_free(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhzProtocolEncoderHoneywell* instance = context;
|
|
||||||
free(instance->encoder.upload);
|
|
||||||
free(instance);
|
|
||||||
}
|
|
||||||
static LevelDuration
|
static LevelDuration
|
||||||
subghz_protocol_encoder_honeywell_add_duration_to_upload(ManchesterEncoderResult result) {
|
subghz_protocol_encoder_honeywell_add_duration_to_upload(ManchesterEncoderResult result) {
|
||||||
LevelDuration data = {.duration = 0, .level = 0};
|
LevelDuration data = {.duration = 0, .level = 0};
|
||||||
@@ -286,6 +194,10 @@ static void
|
|||||||
if(level_duration_get_level(instance->encoder.upload[index])) {
|
if(level_duration_get_level(instance->encoder.upload[index])) {
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
//Send delay
|
||||||
|
instance->encoder.upload[index++] =
|
||||||
|
level_duration_make(false, (uint32_t)subghz_protocol_honeywell_const.te_long * 300);
|
||||||
|
|
||||||
instance->encoder.size_upload = index;
|
instance->encoder.size_upload = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,35 +252,188 @@ LevelDuration subghz_protocol_encoder_honeywell_yield(void* context) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SubGhzProtocolDecoder subghz_protocol_honeywell_decoder = {
|
void subghz_protocol_decoder_honeywell_reset(void* context) {
|
||||||
.alloc = subghz_protocol_decoder_honeywell_alloc,
|
furi_assert(context);
|
||||||
.free = subghz_protocol_decoder_honeywell_free,
|
SubGhzProtocolDecoderHoneywell* instance = context;
|
||||||
.feed = subghz_protocol_decoder_honeywell_feed,
|
instance->decoder.decode_data = 0;
|
||||||
.reset = subghz_protocol_decoder_honeywell_reset,
|
instance->decoder.decode_count_bit = 0;
|
||||||
.get_hash_data = NULL,
|
}
|
||||||
.get_hash_data_long = subghz_protocol_decoder_honeywell_get_hash_data,
|
|
||||||
.serialize = subghz_protocol_decoder_honeywell_serialize,
|
|
||||||
.deserialize = subghz_protocol_decoder_honeywell_deserialize,
|
|
||||||
.get_string = subghz_protocol_decoder_honeywell_get_string,
|
|
||||||
.get_string_brief = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SubGhzProtocolEncoder subghz_protocol_honeywell_encoder = {
|
void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t duration) {
|
||||||
.alloc = subghz_protocol_encoder_honeywell_alloc,
|
furi_assert(context);
|
||||||
.free = subghz_protocol_encoder_honeywell_free,
|
SubGhzProtocolDecoderHoneywell* instance = context;
|
||||||
.deserialize = subghz_protocol_encoder_honeywell_deserialize,
|
|
||||||
.stop = subghz_protocol_encoder_honeywell_stop,
|
|
||||||
.yield = subghz_protocol_encoder_honeywell_yield,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SubGhzProtocol subghz_protocol_honeywell = {
|
ManchesterEvent event = ManchesterEventReset;
|
||||||
.name = SUBGHZ_PROTOCOL_HONEYWELL_NAME,
|
if(!level) {
|
||||||
.type = SubGhzProtocolTypeStatic,
|
if(DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_short) <
|
||||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
subghz_protocol_honeywell_const.te_delta) {
|
||||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
|
event = ManchesterEventShortLow;
|
||||||
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
} else if(
|
||||||
.encoder = &subghz_protocol_honeywell_encoder,
|
DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_long) <
|
||||||
.decoder = &subghz_protocol_honeywell_decoder,
|
subghz_protocol_honeywell_const.te_delta * 2) {
|
||||||
|
event = ManchesterEventLongLow;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_short) <
|
||||||
|
subghz_protocol_honeywell_const.te_delta) {
|
||||||
|
event = ManchesterEventShortHigh;
|
||||||
|
} else if(
|
||||||
|
DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_long) <
|
||||||
|
subghz_protocol_honeywell_const.te_delta * 2) {
|
||||||
|
event = ManchesterEventLongHigh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(event != ManchesterEventReset) {
|
||||||
|
bool data;
|
||||||
|
bool data_ok = manchester_advance(
|
||||||
|
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
|
||||||
|
if(data_ok) {
|
||||||
|
subghz_protocol_decoder_honeywell_addbit(instance, data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.filter = SubGhzProtocolFilter_Sensors,
|
static void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) {
|
||||||
};
|
SubGhzProtocolDecoderHoneywell* instance = context;
|
||||||
|
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;
|
||||||
|
instance->decoder.decode_count_bit++;
|
||||||
|
|
||||||
|
if(instance->decoder.decode_count_bit < 62) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t preamble = (instance->decoder.decode_data >> 48) & 0xFFFF;
|
||||||
|
//can be multiple, since flipper can't read it well.. (it can, but the sensors are not that good, there are multiple of variations seen)
|
||||||
|
if(preamble == 0b0011111111111110 || preamble == 0b0111111111111110 ||
|
||||||
|
preamble == 0b1111111111111110) {
|
||||||
|
uint8_t datatocrc[4];
|
||||||
|
datatocrc[0] = (instance->decoder.decode_data >> 40) & 0xFF;
|
||||||
|
datatocrc[1] = (instance->decoder.decode_data >> 32) & 0xFF;
|
||||||
|
datatocrc[2] = (instance->decoder.decode_data >> 24) & 0xFF;
|
||||||
|
datatocrc[3] = (instance->decoder.decode_data >> 16) & 0xFF;
|
||||||
|
uint8_t channel = (instance->decoder.decode_data >> 44) & 0xF;
|
||||||
|
uint16_t crc_calc = 0;
|
||||||
|
if(channel == 0x2 || channel == 0x4 || channel == 0xA) {
|
||||||
|
// 2GIG brand
|
||||||
|
crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8050, 0);
|
||||||
|
} else if(channel == 0x8) {
|
||||||
|
crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8005, 0);
|
||||||
|
} else {
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16_t crc = instance->decoder.decode_data & 0xFFFF;
|
||||||
|
if(crc == crc_calc) {
|
||||||
|
// Removing possible artifacts from higher bits and setting header to FF FE
|
||||||
|
instance->generic.data =
|
||||||
|
((((((0xFF << 16) | ((instance->decoder.decode_data >> 40) & 0xFFFF)) << 16) |
|
||||||
|
((instance->decoder.decode_data >> 24) & 0xFFFF))
|
||||||
|
<< 16) |
|
||||||
|
((instance->decoder.decode_data >> 8) & 0xFFFF))
|
||||||
|
<< 8 |
|
||||||
|
(instance->decoder.decode_data & 0xFF);
|
||||||
|
instance->generic.data_count_bit = 64;
|
||||||
|
if(instance->base.callback)
|
||||||
|
instance->base.callback(&instance->base, instance->base.context);
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
} else {
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if(instance->decoder.decode_count_bit >= 64) {
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t subghz_protocol_decoder_honeywell_get_hash_data(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzProtocolDecoderHoneywell* instance = context;
|
||||||
|
return subghz_protocol_blocks_get_hash_data_long(
|
||||||
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzProtocolStatus subghz_protocol_decoder_honeywell_serialize(
|
||||||
|
void* context,
|
||||||
|
FlipperFormat* flipper_format,
|
||||||
|
SubGhzRadioPreset* preset) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzProtocolDecoderHoneywell* instance = context;
|
||||||
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubGhzProtocolStatus
|
||||||
|
subghz_protocol_decoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzProtocolDecoderHoneywell* instance = context;
|
||||||
|
|
||||||
|
SubGhzProtocolStatus res = SubGhzProtocolStatusError;
|
||||||
|
res = subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||||
|
if(res != SubGhzProtocolStatusOk) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(instance->generic.data_count_bit != 64) {
|
||||||
|
if(instance->generic.data_count_bit < 62) {
|
||||||
|
return SubGhzProtocolStatusErrorValueBitCount;
|
||||||
|
}
|
||||||
|
// Removing possible artifacts from higher bits and setting header to FF FE
|
||||||
|
instance->generic.data =
|
||||||
|
((((((0xFF << 16) | ((instance->generic.data >> 40) & 0xFFFF)) << 16) |
|
||||||
|
((instance->generic.data >> 24) & 0xFFFF))
|
||||||
|
<< 16) |
|
||||||
|
((instance->generic.data >> 8) & 0xFFFF))
|
||||||
|
<< 8 |
|
||||||
|
(instance->generic.data & 0xFF);
|
||||||
|
instance->generic.data_count_bit = 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* output) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzProtocolDecoderHoneywell* instance = context;
|
||||||
|
|
||||||
|
uint32_t code_found_hi = instance->generic.data >> 32;
|
||||||
|
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||||
|
|
||||||
|
instance->generic.serial = (instance->generic.data >> 24) & 0xFFFFF;
|
||||||
|
uint8_t sensor_status = (instance->generic.data >> 16) & 0xFF;
|
||||||
|
|
||||||
|
uint8_t channel = (instance->generic.data >> 44) & 0xF;
|
||||||
|
uint8_t contact = (sensor_status & 0x80) >> 7;
|
||||||
|
uint8_t tamper = (sensor_status & 0x40) >> 6;
|
||||||
|
uint8_t reed = (sensor_status & 0x20) >> 5;
|
||||||
|
uint8_t alarm = (sensor_status & 0x10) >> 4;
|
||||||
|
uint8_t battery_low = (sensor_status & 0x08) >> 3;
|
||||||
|
uint8_t heartbeat = (sensor_status & 0x04) >> 2;
|
||||||
|
|
||||||
|
furi_string_cat_printf(
|
||||||
|
output,
|
||||||
|
"%s\r\n%dbit "
|
||||||
|
"Sn:%07lu Ch:%u\r\n"
|
||||||
|
"LowBat:%d HB: %d Cont: %s\r\n"
|
||||||
|
"Key:%08lX%08lX\r\n"
|
||||||
|
"State: L1:%u L2:%u L3:%u L4:%u",
|
||||||
|
instance->generic.protocol_name,
|
||||||
|
instance->generic.data_count_bit,
|
||||||
|
instance->generic.serial,
|
||||||
|
channel,
|
||||||
|
battery_low,
|
||||||
|
heartbeat,
|
||||||
|
contact ? "open" : "closed",
|
||||||
|
code_found_hi,
|
||||||
|
code_found_lo,
|
||||||
|
contact,
|
||||||
|
reed,
|
||||||
|
alarm,
|
||||||
|
tamper);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <lib/subghz/protocols/base.h>
|
|
||||||
|
|
||||||
#include <lib/subghz/blocks/const.h>
|
|
||||||
#include <lib/subghz/blocks/decoder.h>
|
|
||||||
#include <lib/subghz/blocks/encoder.h>
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "../blocks/generic.h"
|
|
||||||
#include <lib/subghz/blocks/math.h>
|
|
||||||
#include <lib/toolbox/manchester_decoder.h>
|
|
||||||
#include <lib/toolbox/manchester_encoder.h>
|
|
||||||
|
|
||||||
#define SUBGHZ_PROTOCOL_HONEYWELL_NAME "Honeywell Sec"
|
#define SUBGHZ_PROTOCOL_HONEYWELL_NAME "Honeywell Sec"
|
||||||
|
|
||||||
@@ -20,42 +11,99 @@ extern const SubGhzProtocolDecoder subghz_protocol_honeywell_decoder;
|
|||||||
extern const SubGhzProtocolEncoder subghz_protocol_honeywell_encoder;
|
extern const SubGhzProtocolEncoder subghz_protocol_honeywell_encoder;
|
||||||
extern const SubGhzProtocol subghz_protocol_honeywell;
|
extern const SubGhzProtocol subghz_protocol_honeywell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate SubGhzProtocolEncoderHoneywell.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return SubGhzProtocolEncoderHoneywell* pointer to a SubGhzProtocolEncoderHoneywell instance
|
||||||
|
*/
|
||||||
|
void* subghz_protocol_encoder_honeywell_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free SubGhzProtocolEncoderHoneywell.
|
||||||
|
* @param context Pointer to a SubGhzProtocolEncoderHoneywell instance
|
||||||
|
*/
|
||||||
|
void subghz_protocol_encoder_honeywell_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize and generating an upload to send.
|
||||||
|
* @param context Pointer to a SubGhzProtocolEncoderHoneywell instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
SubGhzProtocolStatus
|
||||||
|
subghz_protocol_encoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forced transmission stop.
|
||||||
|
* @param context Pointer to a SubGhzProtocolEncoderHoneywell instance
|
||||||
|
*/
|
||||||
|
void subghz_protocol_encoder_honeywell_stop(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the level and duration of the upload to be loaded into DMA.
|
||||||
|
* @param context Pointer to a SubGhzProtocolEncoderHoneywell instance
|
||||||
|
* @return LevelDuration
|
||||||
|
*/
|
||||||
|
LevelDuration subghz_protocol_encoder_honeywell_yield(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate SubGhzProtocolDecoderHoneywell.
|
||||||
|
* @param environment Pointer to a SubGhzEnvironment instance
|
||||||
|
* @return SubGhzProtocolDecoderHoneywell* pointer to a SubGhzProtocolDecoderHoneywell instance
|
||||||
|
*/
|
||||||
void* subghz_protocol_decoder_honeywell_alloc(SubGhzEnvironment* environment);
|
void* subghz_protocol_decoder_honeywell_alloc(SubGhzEnvironment* environment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free SubGhzProtocolDecoderHoneywell.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderHoneywell instance
|
||||||
|
*/
|
||||||
void subghz_protocol_decoder_honeywell_free(void* context);
|
void subghz_protocol_decoder_honeywell_free(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset decoder SubGhzProtocolDecoderHoneywell.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderHoneywell instance
|
||||||
|
*/
|
||||||
void subghz_protocol_decoder_honeywell_reset(void* context);
|
void subghz_protocol_decoder_honeywell_reset(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a raw sequence of levels and durations received from the air.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderHoneywell instance
|
||||||
|
* @param level Signal level true-high false-low
|
||||||
|
* @param duration Duration of this level in, us
|
||||||
|
*/
|
||||||
void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t duration);
|
void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting the hash sum of the last randomly received parcel.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderHoneywell instance
|
||||||
|
* @return hash Hash sum
|
||||||
|
*/
|
||||||
uint32_t subghz_protocol_decoder_honeywell_get_hash_data(void* context);
|
uint32_t subghz_protocol_decoder_honeywell_get_hash_data(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize data SubGhzProtocolDecoderHoneywell.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderHoneywell instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
SubGhzProtocolStatus subghz_protocol_decoder_honeywell_serialize(
|
SubGhzProtocolStatus subghz_protocol_decoder_honeywell_serialize(
|
||||||
void* context,
|
void* context,
|
||||||
FlipperFormat* flipper_format,
|
FlipperFormat* flipper_format,
|
||||||
SubGhzRadioPreset* preset);
|
SubGhzRadioPreset* preset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize data SubGhzProtocolDecoderHoneywell.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderHoneywell instance
|
||||||
|
* @param flipper_format Pointer to a FlipperFormat instance
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
SubGhzProtocolStatus
|
SubGhzProtocolStatus
|
||||||
subghz_protocol_decoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format);
|
subghz_protocol_decoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting a textual representation of the received data.
|
||||||
|
* @param context Pointer to a SubGhzProtocolDecoderHoneywell instance
|
||||||
|
* @param output Resulting text
|
||||||
|
*/
|
||||||
void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* output);
|
void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* output);
|
||||||
|
|
||||||
static const SubGhzBlockConst subghz_protocol_honeywell_const = {
|
|
||||||
.te_long = 280,
|
|
||||||
.te_short = 143,
|
|
||||||
.te_delta = 51,
|
|
||||||
.min_count_bit_for_found = 62,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SubGhzProtocolDecoderHoneywell {
|
|
||||||
SubGhzProtocolDecoderBase base;
|
|
||||||
SubGhzBlockGeneric generic;
|
|
||||||
SubGhzBlockDecoder decoder;
|
|
||||||
ManchesterState manchester_saved_state;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SubGhzProtocolEncoderHoneywell {
|
|
||||||
SubGhzProtocolEncoderBase base;
|
|
||||||
SubGhzBlockGeneric generic;
|
|
||||||
SubGhzProtocolBlockEncoder encoder;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -184,18 +184,29 @@ static bool subghz_protocol_keeloq_gen_data(
|
|||||||
if(counter_up && prog_mode == PROG_MODE_OFF) {
|
if(counter_up && prog_mode == PROG_MODE_OFF) {
|
||||||
// Counter increment conditions
|
// Counter increment conditions
|
||||||
|
|
||||||
// If counter is 0xFFFF we will reset it to 0
|
// Check for OFEX (overflow experimental) mode
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
// Increase counter with value set in global settings (mult)
|
// If counter is 0xFFFF we will reset it to 0
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
// Increase counter with value set in global settings (mult)
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if(
|
} else {
|
||||||
(instance->generic.cnt >= 0xFFFF) &&
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
instance->generic.cnt = 0;
|
||||||
instance->generic.cnt = 0;
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(prog_mode == PROG_MODE_OFF) {
|
if(prog_mode == PROG_MODE_OFF) {
|
||||||
|
|||||||
@@ -157,14 +157,27 @@ static bool subghz_protocol_kinggates_stylo_4k_gen_data(
|
|||||||
}
|
}
|
||||||
instance->generic.cnt = decrypt & 0xFFFF;
|
instance->generic.cnt = decrypt & 0xFFFF;
|
||||||
|
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
} else {
|
||||||
instance->generic.cnt = 0;
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instance->generic.btn = (fix >> 17) & 0x0F;
|
instance->generic.btn = (fix >> 17) & 0x0F;
|
||||||
|
|||||||
@@ -155,15 +155,29 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload(
|
|||||||
instance->encoder.size_upload = size_upload;
|
instance->encoder.size_upload = size_upload;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
} else {
|
||||||
instance->generic.cnt = 0;
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt;
|
uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt;
|
||||||
uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt(decrypt, file_name);
|
uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt(decrypt, file_name);
|
||||||
|
|
||||||
|
|||||||
@@ -254,14 +254,27 @@ static bool
|
|||||||
btn = subghz_protocol_phoenix_v2_get_btn_code();
|
btn = subghz_protocol_phoenix_v2_get_btn_code();
|
||||||
|
|
||||||
// Reconstruction of the data
|
// Reconstruction of the data
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
} else {
|
||||||
instance->generic.cnt = 0;
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t local_data_rev = subghz_protocol_blocks_reverse_key(
|
uint64_t local_data_rev = subghz_protocol_blocks_reverse_key(
|
||||||
|
|||||||
@@ -132,14 +132,27 @@ static bool
|
|||||||
instance->generic.cnt = (data >> 24) & 0xFFFF;
|
instance->generic.cnt = (data >> 24) & 0xFFFF;
|
||||||
instance->generic.serial = data & 0xFFFFFF;
|
instance->generic.serial = data & 0xFFFFFF;
|
||||||
|
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
} else {
|
||||||
instance->generic.cnt = 0;
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t frame[10];
|
uint8_t frame[10];
|
||||||
|
|||||||
@@ -126,14 +126,27 @@ static bool subghz_protocol_somfy_telis_gen_data(
|
|||||||
|
|
||||||
btn = subghz_protocol_somfy_telis_get_btn_code();
|
btn = subghz_protocol_somfy_telis_get_btn_code();
|
||||||
|
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
} else {
|
||||||
instance->generic.cnt = 0;
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t frame[7];
|
uint8_t frame[7];
|
||||||
|
|||||||
@@ -132,14 +132,27 @@ void subghz_protocol_encoder_star_line_free(void* context) {
|
|||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) {
|
subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) {
|
||||||
if(instance->generic.cnt < 0xFFFF) {
|
// Check for OFEX (overflow experimental) mode
|
||||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) {
|
||||||
|
if(instance->generic.cnt < 0xFFFF) {
|
||||||
|
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
}
|
||||||
|
} else if(
|
||||||
|
(instance->generic.cnt >= 0xFFFF) &&
|
||||||
|
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
||||||
instance->generic.cnt = 0;
|
instance->generic.cnt = 0;
|
||||||
} else {
|
|
||||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
|
||||||
}
|
}
|
||||||
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
|
} else {
|
||||||
instance->generic.cnt = 0;
|
if((instance->generic.cnt + 0x1) > 0xFFFF) {
|
||||||
|
instance->generic.cnt = 0;
|
||||||
|
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
|
||||||
|
instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult();
|
||||||
|
} else {
|
||||||
|
instance->generic.cnt++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uint32_t fix = btn << 24 | instance->generic.serial;
|
uint32_t fix = btn << 24 | instance->generic.serial;
|
||||||
uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt;
|
uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt;
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ Header,+,lib/nfc/helpers/iso14443_crc.h,,
|
|||||||
Header,+,lib/nfc/helpers/nfc_data_generator.h,,
|
Header,+,lib/nfc/helpers/nfc_data_generator.h,,
|
||||||
Header,+,lib/nfc/helpers/nfc_util.h,,
|
Header,+,lib/nfc/helpers/nfc_util.h,,
|
||||||
Header,+,lib/nfc/nfc.h,,
|
Header,+,lib/nfc/nfc.h,,
|
||||||
|
Header,+,lib/nfc/nfc_common.h,,
|
||||||
Header,+,lib/nfc/nfc_device.h,,
|
Header,+,lib/nfc/nfc_device.h,,
|
||||||
Header,+,lib/nfc/nfc_listener.h,,
|
Header,+,lib/nfc/nfc_listener.h,,
|
||||||
Header,+,lib/nfc/nfc_poller.h,,
|
Header,+,lib/nfc/nfc_poller.h,,
|
||||||
@@ -648,8 +649,8 @@ Function,+,archive_favorites_handle_setting_pin_unpin,void,"const char*, const c
|
|||||||
Function,+,args_char_to_hex,_Bool,"char, char, uint8_t*"
|
Function,+,args_char_to_hex,_Bool,"char, char, uint8_t*"
|
||||||
Function,+,args_get_first_word_length,size_t,FuriString*
|
Function,+,args_get_first_word_length,size_t,FuriString*
|
||||||
Function,+,args_length,size_t,FuriString*
|
Function,+,args_length,size_t,FuriString*
|
||||||
Function,+,args_read_float_and_trim,_Bool,"FuriString*, float*"
|
|
||||||
Function,+,args_read_duration,_Bool,"FuriString*, uint32_t*, const char*"
|
Function,+,args_read_duration,_Bool,"FuriString*, uint32_t*, const char*"
|
||||||
|
Function,+,args_read_float_and_trim,_Bool,"FuriString*, float*"
|
||||||
Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t"
|
Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t"
|
||||||
Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*"
|
Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*"
|
||||||
Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*"
|
Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*"
|
||||||
@@ -1144,7 +1145,7 @@ Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*"
|
|||||||
Function,+,felica_service_get_attribute_string,void,"const FelicaService*, FuriString*"
|
Function,+,felica_service_get_attribute_string,void,"const FelicaService*, FuriString*"
|
||||||
Function,+,felica_set_uid,_Bool,"FelicaData*, const uint8_t*, size_t"
|
Function,+,felica_set_uid,_Bool,"FelicaData*, const uint8_t*, size_t"
|
||||||
Function,+,felica_verify,_Bool,"FelicaData*, const FuriString*"
|
Function,+,felica_verify,_Bool,"FelicaData*, const FuriString*"
|
||||||
Function,+,felica_write_directory_tree,void,"const FelicaData*, FuriString*"
|
Function,+,felica_write_directory_tree,void,"const FelicaSystem*, FuriString*"
|
||||||
Function,-,feof,int,FILE*
|
Function,-,feof,int,FILE*
|
||||||
Function,-,feof_unlocked,int,FILE*
|
Function,-,feof_unlocked,int,FILE*
|
||||||
Function,-,ferror,int,FILE*
|
Function,-,ferror,int,FILE*
|
||||||
|
|||||||
|
Reference in New Issue
Block a user