Merge branch 'dev' into astra/3746-mfp-detect
3
.vscode/example/clangd/extensions.json
vendored
@ -8,7 +8,8 @@
|
||||
"amiralizadeh9480.cpp-helper",
|
||||
"marus25.cortex-debug",
|
||||
"zxh404.vscode-proto3",
|
||||
"augustocdias.tasks-shell-input"
|
||||
"augustocdias.tasks-shell-input",
|
||||
"rioj7.command-variable"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
|
@ -2,7 +2,7 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Win32",
|
||||
"compilerPath": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gcc.exe",
|
||||
"compilerPath": "${workspaceFolder}/toolchain/current/bin/arm-none-eabi-gcc.exe",
|
||||
"intelliSenseMode": "gcc-arm",
|
||||
"compileCommands": "${workspaceFolder}/build/latest/compile_commands.json",
|
||||
"cStandard": "gnu23",
|
||||
@ -10,7 +10,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"compilerPath": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gcc",
|
||||
"compilerPath": "${workspaceFolder}/toolchain/current/bin/arm-none-eabi-gcc",
|
||||
"intelliSenseMode": "gcc-arm",
|
||||
"compileCommands": "${workspaceFolder}/build/latest/compile_commands.json",
|
||||
"cStandard": "gnu23",
|
||||
@ -18,7 +18,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Mac",
|
||||
"compilerPath": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gcc",
|
||||
"compilerPath": "${workspaceFolder}/toolchain/current/bin/arm-none-eabi-gcc",
|
||||
"intelliSenseMode": "gcc-arm",
|
||||
"compileCommands": "${workspaceFolder}/build/latest/compile_commands.json",
|
||||
"cStandard": "gnu23",
|
||||
|
@ -17,6 +17,5 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm)
|
||||
ADD_SCENE(ibutton, delete_success, DeleteSuccess)
|
||||
ADD_SCENE(ibutton, retry_confirm, RetryConfirm)
|
||||
ADD_SCENE(ibutton, exit_confirm, ExitConfirm)
|
||||
ADD_SCENE(ibutton, read_exit_confirm, ReadExitConfirm)
|
||||
ADD_SCENE(ibutton, view_data, ViewData)
|
||||
ADD_SCENE(ibutton, rpc, Rpc)
|
||||
|
@ -1,59 +0,0 @@
|
||||
#include "../ibutton_i.h"
|
||||
|
||||
static void ibutton_scene_read_exit_confirm_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
iButton* ibutton = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void ibutton_scene_read_exit_confirm_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
widget_add_button_element(
|
||||
widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Exit",
|
||||
ibutton_scene_read_exit_confirm_widget_callback,
|
||||
ibutton);
|
||||
widget_add_button_element(
|
||||
widget,
|
||||
GuiButtonTypeRight,
|
||||
"Stay",
|
||||
ibutton_scene_read_exit_confirm_widget_callback,
|
||||
ibutton);
|
||||
widget_add_string_element(
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?");
|
||||
widget_add_string_element(
|
||||
widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
}
|
||||
|
||||
bool ibutton_scene_read_exit_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
SceneManager* scene_manager = ibutton->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true; // Ignore Back button presses
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
scene_manager_search_and_switch_to_previous_scene(scene_manager, iButtonSceneRead);
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void ibutton_scene_read_exit_confirm_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
widget_reset(ibutton->widget);
|
||||
}
|
@ -44,7 +44,7 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadExitConfirm);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == GuiButtonTypeRight) {
|
||||
|
@ -15,7 +15,7 @@ void ibutton_scene_retry_confirm_on_enter(void* context) {
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Exit", ibutton_scene_retry_confirm_widget_callback, ibutton);
|
||||
widget, GuiButtonTypeLeft, "Retry", ibutton_scene_retry_confirm_widget_callback, ibutton);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "Stay", ibutton_scene_retry_confirm_widget_callback, ibutton);
|
||||
widget_add_string_element(
|
||||
|
@ -826,3 +826,35 @@ type: raw
|
||||
frequency: 38000
|
||||
duty_cycle: 0.330000
|
||||
data: 3302 1640 404 423 407 420 410 1212 437 390 440 1234 405 395 435 392 438 415 415 1207 432 1242 407 420 410 391 439 414 405 1243 406 1241 408 392 438 415 415 386 433 393 437 390 440 414 405 396 434 419 411 389 441 412 407 420 410 390 440 387 432 1242 407 393 437 390 440 414 405 395 435 392 438 389 430 396 434 1240 409 417 413 414 405 395 435 419 411 1237 412 389 430 396 434 393 437 416 414 387 432 394 436 1212 437 389 441 1234 405 1217 432 1241 408 1213 436 1212 437 1210 439
|
||||
#
|
||||
# Model: Toshiba RAS-2518D
|
||||
#
|
||||
name: Dh
|
||||
type: raw
|
||||
frequency: 38000
|
||||
duty_cycle: 0.330000
|
||||
data: 4349 4437 549 1615 551 1614 551 1614 551 1617 549 531 550 530 551 1615 550 531 550 532 549 530 551 531 550 530 551 1615 550 1614 551 531 550 1615 551 529 552 531 550 530 551 533 548 530 551 530 551 1616 549 1615 550 1616 550 1615 550 1614 551 1616 550 1615 551 1615 550 531 550 531 550 530 551 529 552 530 551 530 551 529 552 530 551 531 550 1615 550 532 549 1615 550 1616 550 531 550 531 550 530 551 530 551 529 552 532 549 530 551 530 551 531 550 529 552 531 550 1615 551 530 551 530 551 530 551 531 550 530 551 531 550 530 551 531 550 531 550 531 550 1616 550 1618 547 532 549 529 552 530 551 1615 551 1615 550 5379 4350 4436 550 1616 549 1615 551 1614 552 1615 550 529 552 530 551 1614 552 530 551 529 552 531 550 531 550 531 550 1614 552 1614 551 530 551 1615 550 530 551 530 551 530 551 530 551 531 550 532 549 1616 549 1615 551 1614 552 1615 550 1614 551 1616 550 1614 552 1615 550 529 552 530 551 530 551 530 551 531 550 531 550 530 551 530 551 531 550 1615 550 530 551 1615 550 1615 551 530 551 530 551 530 551 530 551 530 551 530 551 529 552 530 551 531 550 532 549 530 551 1615 551 531 550 530 551 530 551 530 551 530 551 531 550 531 550 531 550 530 551 531 550 1615 551 1615 551 532 549 531 550 531 550 1616 549 1614 552
|
||||
#
|
||||
name: Cool_hi
|
||||
type: raw
|
||||
frequency: 38000
|
||||
duty_cycle: 0.330000
|
||||
data: 4350 4438 549 1615 551 1614 552 1616 549 1616 550 530 551 531 550 1615 551 530 551 529 552 531 550 530 551 531 550 1614 551 1616 550 531 550 1616 549 530 551 531 550 530 551 529 552 530 551 531 550 1616 549 1616 550 1616 549 1616 550 1615 551 1614 551 1614 552 1615 551 530 551 531 550 530 551 531 550 531 550 529 552 532 549 531 550 530 551 1613 552 530 551 531 550 529 552 532 549 530 551 530 551 531 550 531 550 530 551 530 551 530 551 531 550 530 551 531 550 531 550 1615 551 529 552 530 551 530 551 530 551 530 551 530 551 530 551 530 551 532 549 531 550 531 550 532 549 531 550 531 550 530 551 530 551 5132 4351 4435 552 1616 550 1615 550 1615 551 1613 553 531 550 530 551 1615 550 530 551 531 550 531 550 530 551 532 549 1616 550 1616 549 530 551 1615 551 530 551 531 550 530 551 530 551 530 551 531 550 1615 551 1615 551 1614 551 1615 550 1615 551 1615 550 1615 550 1616 550 530 551 530 551 531 550 532 549 530 551 530 551 531 550 531 550 531 550 1615 550 530 551 530 551 530 551 529 552 531 550 530 551 531 550 531 550 530 551 530 551 531 550 530 551 530 551 530 551 531 550 1616 550 530 551 529 552 530 551 531 550 532 549 530 551 530 551 529 552 531 550 529 552 530 551 530 551 531 550 531 550 529 552 531 550
|
||||
#
|
||||
name: Cool_lo
|
||||
type: raw
|
||||
frequency: 38000
|
||||
duty_cycle: 0.330000
|
||||
data: 4350 4436 550 1617 549 1615 550 1615 550 1617 548 530 551 531 550 1615 551 531 550 531 550 530 551 530 551 531 550 1614 552 1615 550 530 551 1614 551 531 550 531 550 531 550 529 552 532 549 530 551 1617 549 1616 549 1615 551 1619 547 1615 550 1615 550 1616 549 1616 550 530 551 531 550 530 551 530 551 531 550 530 551 529 552 529 552 530 551 1617 548 533 548 1615 551 1613 552 530 551 531 550 531 550 530 551 530 551 532 549 531 550 531 550 530 551 531 550 531 550 531 550 1615 551 531 550 531 550 532 549 531 550 530 551 531 550 533 548 531 550 530 551 1617 548 1616 549 530 551 531 550 532 549 532 549 532 549 5200 4349 4436 550 1615 551 1615 551 1615 550 1616 550 531 550 530 551 1615 551 531 550 530 551 530 551 530 551 530 551 1616 549 1615 551 530 551 1615 551 531 550 531 550 530 551 531 550 531 550 531 550 1615 551 1616 550 1616 550 1615 550 1617 548 1616 549 1616 550 1615 550 531 550 530 551 531 550 531 550 532 549 530 551 531 550 531 550 532 549 1616 550 531 550 1616 550 1615 550 531 550 530 551 531 550 531 550 531 550 531 550 531 550 532 549 532 549 531 550 532 549 531 550 1616 550 531 550 530 551 532 549 532 549 530 551 532 549 531 550 532 549 531 550 1616 549 1617 549 531 550 530 551 531 550 532 549 532 549
|
||||
#
|
||||
name: Heat_hi
|
||||
type: raw
|
||||
frequency: 38000
|
||||
duty_cycle: 0.330000
|
||||
data: 4350 4437 547 1618 548 1620 546 1620 546 1619 547 534 547 535 546 1619 547 534 547 536 545 536 545 535 546 535 546 1619 547 1620 545 534 523 1644 546 535 522 559 546 535 546 534 547 535 546 535 545 1620 546 1620 546 1620 546 1619 547 1619 546 1619 547 1620 545 1620 546 535 546 534 547 537 520 558 523 558 547 534 547 536 521 559 522 559 522 1644 546 535 546 535 522 560 545 536 521 559 522 559 522 558 523 559 522 560 521 559 522 559 522 560 521 559 522 561 520 1644 521 1645 520 559 522 559 522 559 522 559 522 559 522 560 521 560 521 560 521 561 520 559 522 560 521 559 522 559 522 559 522 1644 522 559 522 5341 4349 4439 520 1645 521 1645 521 1646 519 1645 521 560 521 561 520 1645 521 560 521 560 521 559 522 560 521 561 520 1646 520 1645 521 561 520 1645 521 561 520 560 521 560 521 560 521 560 521 561 520 1644 522 1644 522 1645 520 1645 521 1645 521 1645 520 1646 520 1644 522 561 520 560 521 560 521 561 520 560 521 561 520 561 520 561 520 560 521 1646 520 562 519 561 520 561 520 562 519 560 521 560 521 561 520 561 520 560 521 560 521 561 520 560 521 560 521 562 519 1646 520 1645 521 561 520 561 520 561 520 560 521 560 521 561 520 560 521 559 522 560 521 561 520 561 520 560 521 562 519 559 522 1645 521 561 520
|
||||
#
|
||||
name: Heat_lo
|
||||
type: raw
|
||||
frequency: 38000
|
||||
duty_cycle: 0.330000
|
||||
data: 4348 4439 520 1646 520 1646 520 1646 519 1646 520 561 520 561 520 1646 519 561 520 561 520 562 519 562 519 561 520 1646 520 1647 518 563 518 1646 519 562 519 561 520 561 520 562 519 562 519 561 520 1648 517 1647 519 1646 519 1647 519 1646 520 1646 520 1645 520 1647 519 561 520 561 520 562 519 562 519 562 519 562 519 561 520 562 519 561 520 1646 520 562 519 1647 518 1646 520 562 519 560 521 561 520 561 520 561 520 562 519 562 519 560 521 562 519 562 519 560 521 1646 520 1646 520 561 520 562 519 561 520 562 519 561 520 561 520 561 520 561 520 561 520 1647 518 1646 520 562 519 562 519 561 520 1646 520 561 520 5409 4348 4440 519 1645 521 1646 519 1645 521 1645 521 561 520 561 520 1644 522 561 520 561 520 561 520 560 521 562 519 1646 520 1646 520 562 519 1644 522 561 520 561 520 561 520 561 520 561 520 561 520 1646 520 1645 520 1646 520 1645 521 1646 520 1646 520 1644 522 1645 521 560 521 560 521 561 520 561 520 560 521 560 521 561 520 561 520 561 520 1645 521 562 519 1645 521 1645 520 561 520 562 519 561 520 561 520 561 520 560 521 560 521 560 521 560 521 561 520 560 521 1646 520 1646 520 561 520 560 521 559 522 560 521 561 520 561 520 560 521 560 521 560 521 1646 520 1645 520 561 520 560 521 560 521 1645 521 561 520
|
||||
|
@ -506,7 +506,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
|
||||
if(data_block.valid_from_date == 0 || data_block.valid_to_date == 0) {
|
||||
furi_string_cat(result, "\e#No ticket");
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
//remaining_trips
|
||||
furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "../iso14443_4a/iso14443_4a_render.h"
|
||||
|
||||
#define MF_DESFIRE_RENDER_MAX_RECORD_SIZE (256U)
|
||||
|
||||
void nfc_render_mf_desfire_info(
|
||||
const MfDesfireData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
@ -212,8 +214,6 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
uint32_t record_count = 1;
|
||||
uint32_t record_size = 0;
|
||||
|
||||
const uint32_t total_size = simple_array_get_count(data->data);
|
||||
|
||||
switch(settings->type) {
|
||||
case MfDesfireFileTypeStandard:
|
||||
case MfDesfireFileTypeBackup:
|
||||
@ -257,17 +257,14 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
return;
|
||||
}
|
||||
|
||||
for(uint32_t rec = 0; rec < record_count; rec++) {
|
||||
const uint32_t size_offset = rec * record_size;
|
||||
const uint32_t size_remaining = total_size > size_offset ? total_size - size_offset : 0;
|
||||
// Limit record size
|
||||
bool trim_data = record_size > MF_DESFIRE_RENDER_MAX_RECORD_SIZE;
|
||||
if(trim_data) {
|
||||
record_size = MF_DESFIRE_RENDER_MAX_RECORD_SIZE;
|
||||
}
|
||||
|
||||
if(size_remaining < record_size) {
|
||||
furi_string_cat_printf(
|
||||
str, "record %lu (partial %lu of %lu)\n", rec, size_remaining, record_size);
|
||||
record_size = size_remaining;
|
||||
} else {
|
||||
furi_string_cat_printf(str, "record %lu\n", rec);
|
||||
}
|
||||
for(uint32_t rec = 0; rec < record_count; rec++) {
|
||||
furi_string_cat_printf(str, "record %lu\n", rec);
|
||||
|
||||
for(uint32_t ch = 0; ch < record_size; ch += 4) {
|
||||
furi_string_cat_printf(str, "%03lx|", ch);
|
||||
@ -296,6 +293,9 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
|
||||
furi_string_push_back(str, '\n');
|
||||
}
|
||||
if(trim_data) {
|
||||
furi_string_cat_str(str, "...");
|
||||
}
|
||||
|
||||
furi_string_push_back(str, '\n');
|
||||
}
|
||||
|
@ -83,6 +83,20 @@ static const MfClassicKeyPair troika_4k_keys[] = {
|
||||
{.a = 0xBB52F8CCE07F, .b = 0x6B6119752C70}, //40
|
||||
};
|
||||
|
||||
static void troika_render_section_header(
|
||||
FuriString* str,
|
||||
const char* name,
|
||||
uint8_t prefix_separator_cnt,
|
||||
uint8_t suffix_separator_cnt) {
|
||||
for(uint8_t i = 0; i < prefix_separator_cnt; i++) {
|
||||
furi_string_cat_printf(str, ":");
|
||||
}
|
||||
furi_string_cat_printf(str, "[ %s ]", name);
|
||||
for(uint8_t i = 0; i < suffix_separator_cnt; i++) {
|
||||
furi_string_cat_printf(str, ":");
|
||||
}
|
||||
}
|
||||
|
||||
static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) {
|
||||
bool success = true;
|
||||
|
||||
@ -204,18 +218,19 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
bool result3 = mosgortrans_parse_transport_block(&data->block[16], tat_result);
|
||||
|
||||
furi_string_cat_printf(parsed_data, "\e#Troyka card\n");
|
||||
if(result1) {
|
||||
furi_string_cat_printf(
|
||||
parsed_data, "\e#Metro\n%s\n", furi_string_get_cstr(metro_result));
|
||||
if(result1 && !furi_string_empty(metro_result)) {
|
||||
troika_render_section_header(parsed_data, "Metro", 22, 21);
|
||||
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(metro_result));
|
||||
}
|
||||
|
||||
if(result2) {
|
||||
furi_string_cat_printf(
|
||||
parsed_data, "\n\e#Ediniy\n%s\n", furi_string_get_cstr(ground_result));
|
||||
if(result2 && !furi_string_empty(ground_result)) {
|
||||
troika_render_section_header(parsed_data, "Ediny", 22, 22);
|
||||
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(ground_result));
|
||||
}
|
||||
|
||||
if(result3) {
|
||||
furi_string_cat_printf(parsed_data, "\n\e#TAT\n%s", furi_string_get_cstr(tat_result));
|
||||
if(result3 && !furi_string_empty(tat_result)) {
|
||||
troika_render_section_header(parsed_data, "TAT", 24, 23);
|
||||
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(tat_result));
|
||||
}
|
||||
|
||||
furi_string_free(tat_result);
|
||||
|
@ -61,11 +61,11 @@ static void desktop_clock_update(Desktop* desktop) {
|
||||
furi_hal_rtc_get_datetime(&curr_dt);
|
||||
bool time_format_12 = locale_get_time_format() == LocaleTimeFormat12h;
|
||||
|
||||
if(desktop->time_hour != curr_dt.hour || desktop->time_minute != curr_dt.minute ||
|
||||
desktop->time_format_12 != time_format_12) {
|
||||
desktop->time_format_12 = time_format_12;
|
||||
desktop->time_hour = curr_dt.hour;
|
||||
desktop->time_minute = curr_dt.minute;
|
||||
if(desktop->clock.hour != curr_dt.hour || desktop->clock.minute != curr_dt.minute ||
|
||||
desktop->clock.format_12 != time_format_12) {
|
||||
desktop->clock.format_12 = time_format_12;
|
||||
desktop->clock.hour = curr_dt.hour;
|
||||
desktop->clock.minute = curr_dt.minute;
|
||||
view_port_update(desktop->clock_viewport);
|
||||
}
|
||||
}
|
||||
@ -92,8 +92,8 @@ static void desktop_clock_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
uint8_t hour = desktop->time_hour;
|
||||
if(desktop->time_format_12) {
|
||||
uint8_t hour = desktop->clock.hour;
|
||||
if(desktop->clock.format_12) {
|
||||
if(hour > 12) {
|
||||
hour -= 12;
|
||||
}
|
||||
@ -103,11 +103,11 @@ static void desktop_clock_draw_callback(Canvas* canvas, void* context) {
|
||||
}
|
||||
|
||||
char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute);
|
||||
snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->clock.minute);
|
||||
|
||||
view_port_set_width(
|
||||
desktop->clock_viewport,
|
||||
canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1));
|
||||
canvas_string_width(canvas, buffer) - 1 + (desktop->clock.minute % 10 == 1));
|
||||
|
||||
canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignBottom, buffer);
|
||||
}
|
||||
@ -139,7 +139,7 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
|
||||
desktop_auto_lock_arm(desktop);
|
||||
return true;
|
||||
case DesktopGlobalAutoLock:
|
||||
if(!loader_is_locked(desktop->loader)) {
|
||||
if(!loader_is_locked(desktop->loader) && !desktop->locked) {
|
||||
desktop_lock(desktop);
|
||||
}
|
||||
return true;
|
||||
@ -215,6 +215,8 @@ static void desktop_clock_timer_callback(void* context) {
|
||||
}
|
||||
|
||||
void desktop_lock(Desktop* desktop) {
|
||||
furi_assert(!desktop->locked);
|
||||
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||
|
||||
if(desktop->settings.pin_code.length) {
|
||||
@ -230,9 +232,13 @@ void desktop_lock(Desktop* desktop) {
|
||||
|
||||
DesktopStatus status = {.locked = true};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
|
||||
desktop->locked = true;
|
||||
}
|
||||
|
||||
void desktop_unlock(Desktop* desktop) {
|
||||
furi_assert(desktop->locked);
|
||||
|
||||
view_port_enabled_set(desktop->lock_icon_viewport, false);
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_set_lockdown(gui, false);
|
||||
@ -251,6 +257,8 @@ void desktop_unlock(Desktop* desktop) {
|
||||
|
||||
DesktopStatus status = {.locked = false};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
|
||||
desktop->locked = false;
|
||||
}
|
||||
|
||||
void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) {
|
||||
@ -298,7 +306,7 @@ Desktop* desktop_alloc(void) {
|
||||
|
||||
desktop->lock_menu = desktop_lock_menu_alloc();
|
||||
desktop->debug_view = desktop_debug_alloc();
|
||||
desktop->hw_mismatch_popup = popup_alloc();
|
||||
desktop->popup = popup_alloc();
|
||||
desktop->locked_view = desktop_view_locked_alloc();
|
||||
desktop->pin_input_view = desktop_view_pin_input_alloc();
|
||||
desktop->pin_timeout_view = desktop_view_pin_timeout_alloc();
|
||||
@ -334,9 +342,7 @@ Desktop* desktop_alloc(void) {
|
||||
view_dispatcher_add_view(
|
||||
desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view));
|
||||
view_dispatcher_add_view(
|
||||
desktop->view_dispatcher,
|
||||
DesktopViewIdHwMismatch,
|
||||
popup_get_view(desktop->hw_mismatch_popup));
|
||||
desktop->view_dispatcher, DesktopViewIdPopup, popup_get_view(desktop->popup));
|
||||
view_dispatcher_add_view(
|
||||
desktop->view_dispatcher,
|
||||
DesktopViewIdPinTimeout,
|
||||
@ -476,6 +482,17 @@ int32_t desktop_srv(void* p) {
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault);
|
||||
}
|
||||
|
||||
uint8_t keys_total, keys_valid;
|
||||
if(!furi_hal_crypto_enclave_verify(&keys_total, &keys_valid)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Secure Enclave verification failed: total %hhu, valid %hhu",
|
||||
keys_total,
|
||||
keys_valid);
|
||||
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneSecureEnclave);
|
||||
}
|
||||
|
||||
// Special case: autostart application is already running
|
||||
if(loader_is_locked(desktop->loader) &&
|
||||
animation_manager_is_animation_loaded(desktop->animation_manager)) {
|
||||
|
@ -28,13 +28,19 @@ typedef enum {
|
||||
DesktopViewIdLockMenu,
|
||||
DesktopViewIdLocked,
|
||||
DesktopViewIdDebug,
|
||||
DesktopViewIdHwMismatch,
|
||||
DesktopViewIdPopup,
|
||||
DesktopViewIdPinInput,
|
||||
DesktopViewIdPinTimeout,
|
||||
DesktopViewIdSlideshow,
|
||||
DesktopViewIdTotal,
|
||||
} DesktopViewId;
|
||||
|
||||
typedef struct {
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
bool format_12; // 1 - 12 hour, 0 - 24H
|
||||
} DesktopClock;
|
||||
|
||||
struct Desktop {
|
||||
// Scene
|
||||
FuriThread* scene_thread;
|
||||
@ -43,7 +49,7 @@ struct Desktop {
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
|
||||
Popup* hw_mismatch_popup;
|
||||
Popup* popup;
|
||||
DesktopLockMenuView* lock_menu;
|
||||
DesktopDebugView* debug_view;
|
||||
DesktopViewLocked* locked_view;
|
||||
@ -75,11 +81,10 @@ struct Desktop {
|
||||
|
||||
FuriPubSub* status_pubsub;
|
||||
|
||||
uint8_t time_hour;
|
||||
uint8_t time_minute;
|
||||
bool time_format_12 : 1; // 1 - 12 hour, 0 - 24H
|
||||
DesktopClock clock;
|
||||
|
||||
bool in_transition : 1;
|
||||
bool locked : 1;
|
||||
|
||||
FuriSemaphore* animation_semaphore;
|
||||
};
|
||||
|
@ -7,3 +7,4 @@ ADD_SCENE(desktop, locked, Locked)
|
||||
ADD_SCENE(desktop, pin_input, PinInput)
|
||||
ADD_SCENE(desktop, pin_timeout, PinTimeout)
|
||||
ADD_SCENE(desktop, slideshow, Slideshow)
|
||||
ADD_SCENE(desktop, secure_enclave, SecureEnclave)
|
@ -12,20 +12,21 @@ void desktop_scene_fault_callback(void* context) {
|
||||
void desktop_scene_fault_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
|
||||
Popup* popup = desktop->hw_mismatch_popup;
|
||||
Popup* popup = desktop->popup;
|
||||
popup_set_context(popup, desktop);
|
||||
popup_set_header(
|
||||
popup,
|
||||
"Flipper crashed\n and was rebooted",
|
||||
60,
|
||||
64,
|
||||
14 + STATUS_BAR_Y_SHIFT,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
|
||||
char* message = (char*)furi_hal_rtc_get_fault_data();
|
||||
popup_set_text(popup, message, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup_set_text(popup, message, 64, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup_set_callback(popup, desktop_scene_fault_callback);
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdHwMismatch);
|
||||
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);
|
||||
}
|
||||
|
||||
bool desktop_scene_fault_on_event(void* context, SceneManagerEvent event) {
|
||||
@ -47,6 +48,11 @@ bool desktop_scene_fault_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void desktop_scene_fault_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
|
||||
Popup* popup = desktop->popup;
|
||||
popup_reset(popup);
|
||||
|
||||
furi_hal_rtc_set_fault_data(0);
|
||||
}
|
||||
|
@ -4,17 +4,15 @@
|
||||
#include "desktop_scene.h"
|
||||
#include "../desktop_i.h"
|
||||
|
||||
#define HW_MISMATCH_BACK_EVENT (0UL)
|
||||
|
||||
void desktop_scene_hw_mismatch_callback(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, HW_MISMATCH_BACK_EVENT);
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopHwMismatchExit);
|
||||
}
|
||||
|
||||
void desktop_scene_hw_mismatch_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
Popup* popup = desktop->hw_mismatch_popup;
|
||||
Popup* popup = desktop->popup;
|
||||
|
||||
char* text_buffer = malloc(256);
|
||||
scene_manager_set_scene_state(
|
||||
@ -28,10 +26,10 @@ void desktop_scene_hw_mismatch_on_enter(void* context) {
|
||||
version_get_target(NULL));
|
||||
popup_set_context(popup, desktop);
|
||||
popup_set_header(
|
||||
popup, "!!!! HW Mismatch !!!!", 60, 14 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup_set_text(popup, text_buffer, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup, "!!!! HW Mismatch !!!!", 64, 12 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, text_buffer, 64, 33 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup_set_callback(popup, desktop_scene_hw_mismatch_callback);
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdHwMismatch);
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);
|
||||
}
|
||||
|
||||
bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) {
|
||||
@ -40,11 +38,10 @@ bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case HW_MISMATCH_BACK_EVENT:
|
||||
case DesktopHwMismatchExit:
|
||||
scene_manager_previous_scene(desktop->scene_manager);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -55,11 +52,10 @@ bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event)
|
||||
void desktop_scene_hw_mismatch_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
Popup* popup = desktop->hw_mismatch_popup;
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
popup_set_callback(popup, NULL);
|
||||
popup_set_context(popup, NULL);
|
||||
|
||||
Popup* popup = desktop->popup;
|
||||
popup_reset(popup);
|
||||
|
||||
char* text_buffer =
|
||||
(char*)scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneHwMismatch);
|
||||
free(text_buffer);
|
||||
|
@ -0,0 +1,57 @@
|
||||
#include <gui/scene_manager.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include "desktop_scene.h"
|
||||
#include "../desktop_i.h"
|
||||
|
||||
void desktop_scene_secure_enclave_callback(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopEnclaveExit);
|
||||
}
|
||||
|
||||
void desktop_scene_secure_enclave_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
|
||||
Popup* popup = desktop->popup;
|
||||
popup_set_context(popup, desktop);
|
||||
popup_set_header(
|
||||
popup, "No Factory Keys Found", 64, 12 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignBottom);
|
||||
popup_set_text(
|
||||
popup,
|
||||
"Secure Enclave is damaged.\n"
|
||||
"Some apps will not work.",
|
||||
64,
|
||||
33 + STATUS_BAR_Y_SHIFT,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
popup_set_callback(popup, desktop_scene_secure_enclave_callback);
|
||||
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);
|
||||
}
|
||||
|
||||
bool desktop_scene_secure_enclave_on_event(void* context, SceneManagerEvent event) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DesktopEnclaveExit:
|
||||
scene_manager_previous_scene(desktop->scene_manager);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void desktop_scene_secure_enclave_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
|
||||
Popup* popup = desktop->popup;
|
||||
popup_reset(popup);
|
||||
}
|
@ -46,6 +46,10 @@ typedef enum {
|
||||
DesktopSlideshowCompleted,
|
||||
DesktopSlideshowPoweroff,
|
||||
|
||||
DesktopHwMismatchExit,
|
||||
|
||||
DesktopEnclaveExit,
|
||||
|
||||
// Global events
|
||||
DesktopGlobalBeforeAppStarted,
|
||||
DesktopGlobalAfterAppFinished,
|
||||
|
BIN
assets/dolphin/external/L1_Akira_128x64/frame_0.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_1.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_10.png
vendored
Executable file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_11.png
vendored
Executable file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_12.png
vendored
Executable file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_13.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_14.png
vendored
Executable file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_15.png
vendored
Executable file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_16.png
vendored
Executable file
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_17.png
vendored
Executable file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_18.png
vendored
Executable file
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_19.png
vendored
Executable file
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_2.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_20.png
vendored
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_21.png
vendored
Executable file
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_22.png
vendored
Executable file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_23.png
vendored
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_24.png
vendored
Executable file
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_25.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_26.png
vendored
Executable file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_27.png
vendored
Executable file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_28.png
vendored
Executable file
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_29.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_3.png
vendored
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_30.png
vendored
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_31.png
vendored
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_32.png
vendored
Executable file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_33.png
vendored
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_34.png
vendored
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_35.png
vendored
Executable file
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_4.png
vendored
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_5.png
vendored
Executable file
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_6.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_7.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_8.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/dolphin/external/L1_Akira_128x64/frame_9.png
vendored
Executable file
After Width: | Height: | Size: 1.8 KiB |
14
assets/dolphin/external/L1_Akira_128x64/meta.txt
vendored
Executable file
@ -0,0 +1,14 @@
|
||||
Filetype: Flipper Animation
|
||||
Version: 1
|
||||
|
||||
Width: 128
|
||||
Height: 64
|
||||
Passive frames: 15
|
||||
Active frames: 21
|
||||
Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
||||
Active cycles: 1
|
||||
Frame rate: 2
|
||||
Duration: 3600
|
||||
Active cooldown: 7
|
||||
|
||||
Bubble slots: 0
|
7
assets/dolphin/external/manifest.txt
vendored
@ -189,3 +189,10 @@ Max butthurt: 12
|
||||
Min level: 3
|
||||
Max level: 3
|
||||
Weight: 5
|
||||
|
||||
Name: L1_Akira_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 8
|
||||
Min level: 1
|
||||
Max level: 3
|
||||
Weight: 5
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@ -381,7 +381,7 @@ static GapConfig template_config = {
|
||||
.conn_param =
|
||||
{
|
||||
.conn_int_min = 0x18, // AN5289: 4.7, we need at least 25ms + advertisement, which is 30 ms
|
||||
.conn_int_max = 0x18, // 30 ms
|
||||
.conn_int_max = 0x24, // 45 ms
|
||||
.slave_latency = 0,
|
||||
.supervisor_timeout = 0,
|
||||
},
|
||||
|
@ -202,6 +202,7 @@ __attribute__((unused)) static const char* elf_reloc_type_to_str(int symt) {
|
||||
STRCASE(R_ARM_NONE)
|
||||
STRCASE(R_ARM_TARGET1)
|
||||
STRCASE(R_ARM_ABS32)
|
||||
STRCASE(R_ARM_REL32)
|
||||
STRCASE(R_ARM_THM_PC22)
|
||||
STRCASE(R_ARM_THM_JUMP24)
|
||||
default:
|
||||
@ -329,6 +330,10 @@ static bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf3
|
||||
*((uint32_t*)relAddr) += symAddr;
|
||||
FURI_LOG_D(TAG, " R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
|
||||
break;
|
||||
case R_ARM_REL32:
|
||||
*((uint32_t*)relAddr) += symAddr - relAddr;
|
||||
FURI_LOG_D(TAG, " R_ARM_REL32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
|
||||
break;
|
||||
case R_ARM_THM_PC22:
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_THM_JUMP24:
|
||||
|
@ -218,19 +218,15 @@ void dallas_ds1971_render_uid(FuriString* result, const iButtonProtocolData* pro
|
||||
|
||||
void dallas_ds1971_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {
|
||||
const DS1971ProtocolData* data = protocol_data;
|
||||
FuriString* data_string = furi_string_alloc();
|
||||
|
||||
furi_string_cat_printf(result, "\e#Memory Data\n--------------------\n");
|
||||
|
||||
pretty_format_bytes_hex_canonical(
|
||||
data_string,
|
||||
result,
|
||||
DS1971_DATA_BYTE_COUNT,
|
||||
PRETTY_FORMAT_FONT_MONOSPACE,
|
||||
data->eeprom_data,
|
||||
DS1971_EEPROM_DATA_SIZE);
|
||||
|
||||
furi_string_cat_printf(result, "\e#Memory Data\n--------------------\n");
|
||||
furi_string_cat_printf(result, "%s", furi_string_get_cstr(data_string));
|
||||
|
||||
furi_string_free(data_string);
|
||||
}
|
||||
|
||||
void dallas_ds1971_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {
|
||||
|
@ -191,19 +191,15 @@ void dallas_ds1992_render_uid(FuriString* result, const iButtonProtocolData* pro
|
||||
|
||||
void dallas_ds1992_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {
|
||||
const DS1992ProtocolData* data = protocol_data;
|
||||
FuriString* data_string = furi_string_alloc();
|
||||
|
||||
furi_string_cat_printf(result, "\e#Memory Data\n--------------------\n");
|
||||
|
||||
pretty_format_bytes_hex_canonical(
|
||||
data_string,
|
||||
result,
|
||||
DS1992_DATA_BYTE_COUNT,
|
||||
PRETTY_FORMAT_FONT_MONOSPACE,
|
||||
data->sram_data,
|
||||
DS1992_SRAM_DATA_SIZE);
|
||||
|
||||
furi_string_cat_printf(result, "\e#Memory Data\n--------------------\n");
|
||||
furi_string_cat_printf(result, "%s", furi_string_get_cstr(data_string));
|
||||
|
||||
furi_string_free(data_string);
|
||||
}
|
||||
|
||||
void dallas_ds1992_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {
|
||||
|
@ -217,19 +217,14 @@ void dallas_ds1996_render_uid(FuriString* result, const iButtonProtocolData* pro
|
||||
void dallas_ds1996_render_data(FuriString* result, const iButtonProtocolData* protocol_data) {
|
||||
const DS1996ProtocolData* data = protocol_data;
|
||||
|
||||
FuriString* data_string = furi_string_alloc();
|
||||
furi_string_cat_printf(result, "\e#Memory Data\n--------------------\n");
|
||||
|
||||
pretty_format_bytes_hex_canonical(
|
||||
data_string,
|
||||
result,
|
||||
DS1996_DATA_BYTE_COUNT,
|
||||
PRETTY_FORMAT_FONT_MONOSPACE,
|
||||
data->sram_data,
|
||||
DS1996_SRAM_DATA_SIZE);
|
||||
|
||||
furi_string_cat_printf(result, "\e#Memory Data\n--------------------\n");
|
||||
furi_string_cat_printf(result, "%s", furi_string_get_cstr(data_string));
|
||||
|
||||
furi_string_free(data_string);
|
||||
}
|
||||
|
||||
void dallas_ds1996_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) {
|
||||
|
@ -325,7 +325,7 @@ static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void protocol_cyfral_render_uid(FuriString* result, ProtocolCyfral* proto) {
|
||||
static void protocol_cyfral_render_uid(ProtocolCyfral* proto, FuriString* result) {
|
||||
furi_string_cat_printf(result, "ID: ");
|
||||
for(size_t i = 0; i < CYFRAL_DATA_SIZE; ++i) {
|
||||
furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]);
|
||||
@ -333,10 +333,7 @@ static void protocol_cyfral_render_uid(FuriString* result, ProtocolCyfral* proto
|
||||
}
|
||||
|
||||
static void protocol_cyfral_render_brief_data(ProtocolCyfral* proto, FuriString* result) {
|
||||
furi_string_cat_printf(result, "ID: ");
|
||||
for(size_t i = 0; i < CYFRAL_DATA_SIZE; ++i) {
|
||||
furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]);
|
||||
}
|
||||
protocol_cyfral_render_uid(proto, result);
|
||||
}
|
||||
|
||||
const ProtocolBase ibutton_protocol_misc_cyfral = {
|
||||
|
@ -200,6 +200,16 @@ static bool ibutton_protocol_group_misc_load(
|
||||
}
|
||||
}
|
||||
|
||||
static void ibutton_protocol_group_misc_render_uid(
|
||||
iButtonProtocolGroupMisc* group,
|
||||
const iButtonProtocolData* data,
|
||||
iButtonProtocolLocalId id,
|
||||
FuriString* result) {
|
||||
const size_t data_size = protocol_dict_get_data_size(group->dict, id);
|
||||
protocol_dict_set_data(group->dict, id, data, data_size);
|
||||
protocol_dict_render_uid(group->dict, result, id);
|
||||
}
|
||||
|
||||
static void ibutton_protocol_group_misc_render_data(
|
||||
iButtonProtocolGroupMisc* group,
|
||||
const iButtonProtocolData* data,
|
||||
@ -283,6 +293,7 @@ const iButtonProtocolGroupBase ibutton_protocol_group_misc = {
|
||||
.save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_misc_save,
|
||||
.load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_misc_load,
|
||||
|
||||
.render_uid = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_uid,
|
||||
.render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_data,
|
||||
.render_brief_data =
|
||||
(iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_brief_data,
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "lfrfid_protocols.h"
|
||||
#include "protocol_em4100.h"
|
||||
#include "protocol_electra.h"
|
||||
#include "protocol_h10301.h"
|
||||
#include "protocol_idteck.h"
|
||||
#include "protocol_indala26.h"
|
||||
@ -22,6 +23,7 @@ const ProtocolBase* lfrfid_protocols[] = {
|
||||
[LFRFIDProtocolEM4100] = &protocol_em4100,
|
||||
[LFRFIDProtocolEM410032] = &protocol_em4100_32,
|
||||
[LFRFIDProtocolEM410016] = &protocol_em4100_16,
|
||||
[LFRFIDProtocolElectra] = &protocol_electra,
|
||||
[LFRFIDProtocolH10301] = &protocol_h10301,
|
||||
[LFRFIDProtocolIdteck] = &protocol_idteck,
|
||||
[LFRFIDProtocolIndala26] = &protocol_indala26,
|
||||
|
@ -11,6 +11,7 @@ typedef enum {
|
||||
LFRFIDProtocolEM4100,
|
||||
LFRFIDProtocolEM410032,
|
||||
LFRFIDProtocolEM410016,
|
||||
LFRFIDProtocolElectra,
|
||||
LFRFIDProtocolH10301,
|
||||
LFRFIDProtocolIdteck,
|
||||
LFRFIDProtocolIndala26,
|
||||
|
439
lib/lfrfid/protocols/protocol_electra.c
Normal file
@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Electra intercom rfid protocol (Romania)
|
||||
*
|
||||
* Based on EM4100 protocol implementation from https://github.com/flipperdevices/flipperzero-firmware/blob/dev/lib/lfrfid/protocols/protocol_em4100.c
|
||||
*
|
||||
* Copyright 2024 Leptoptilos <leptoptilos@icloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ------------------------------------------------------------------------------------------------------------------------------
|
||||
* PROTOCOL DESCRIPTION:
|
||||
* ------------------------------------------------------------------------------------------------------------------------------
|
||||
* Electra intercom 125 kHz protocol based on 64-bit clock EM4100, but includes some extra data after base EM4100 data (epilogue)
|
||||
*
|
||||
* Epilogue size is 64 bits, but only first 16 bits matter. Rest 6 bytes - some filler data,
|
||||
* that arbitrary change is not validated by the Electra intercoms
|
||||
*
|
||||
* There are curently three known types of epilogue:
|
||||
* - 0x7E71AAAAAAAAAAAA (AA filler)
|
||||
* - 0x7E71000000000000 (00 filler)
|
||||
* - 0x0030AAAAAAAAAAAA
|
||||
*
|
||||
* First two epilogue bytes may be interpreted as EM4100 data continuation
|
||||
* Nevertheless, these bytes have correct row parity bits, but have not correct collumn parity
|
||||
|
||||
* For example: 0x7E71AAAAAAAAAAAA epilogue:
|
||||
*
|
||||
* In binary: | 0b01111110 | 01110001 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 |
|
||||
* In hex: | 0x7E | 71 | AA | AA | AA | AA | AA | AA |
|
||||
*
|
||||
* As EM4100 data:
|
||||
* 0111 1 // 7
|
||||
* 1100 0 // C
|
||||
* 0111 1 // 7
|
||||
* 1101 0 // here epilogue filler starts (from second bit)
|
||||
* 1010 1 // there is no correct raw parity bits anymore
|
||||
* 0101 0
|
||||
* 1010 1
|
||||
* 0101 0
|
||||
* 1010 // and no correct column parity
|
||||
*/
|
||||
|
||||
#include "bit_lib/bit_lib.h"
|
||||
#include <furi.h>
|
||||
#include <stdlib.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <toolbox/manchester_decoder.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
#define TAG "ELECTRA"
|
||||
|
||||
typedef uint64_t ElectraDecodedData;
|
||||
|
||||
#define EM_HEADER_POS (55)
|
||||
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
|
||||
|
||||
#define EM_FIRST_ROW_POS (50)
|
||||
|
||||
#define EM_ROW_COUNT (10)
|
||||
#define EM_COLUMN_COUNT (4)
|
||||
#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
|
||||
|
||||
#define EM_COLUMN_POS (4)
|
||||
#define ELECTRA_STOP_POS (0)
|
||||
#define ELECTRA_STOP_MASK (0x1LLU << ELECTRA_STOP_POS)
|
||||
|
||||
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | ELECTRA_STOP_MASK)
|
||||
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
|
||||
|
||||
#define ELECTRA_DECODED_BASE_DATA_SIZE (5)
|
||||
#define ELECTRA_ENCODED_BASE_DATA_SIZE (sizeof(ElectraDecodedData))
|
||||
|
||||
#define ELECTRA_DECODED_EPILOGUE_SIZE (3)
|
||||
#define ELECTRA_ENCODED_EPILOGUE_SIZE (sizeof(ElectraDecodedData))
|
||||
|
||||
#define ELECTRA_DECODED_DATA_SIZE (ELECTRA_DECODED_BASE_DATA_SIZE + ELECTRA_DECODED_EPILOGUE_SIZE)
|
||||
#define ELECTRA_ENCODED_DATA_SIZE (ELECTRA_ENCODED_BASE_DATA_SIZE + ELECTRA_ENCODED_EPILOGUE_SIZE)
|
||||
|
||||
#define ELECTRA_DECODED_DATA_EPILOGUE_START_POS (ELECTRA_DECODED_BASE_DATA_SIZE)
|
||||
|
||||
#define ELECTRA_CLOCK_PER_BIT (64)
|
||||
|
||||
#define ELECTRA_READ_SHORT_TIME (256)
|
||||
#define ELECTRA_READ_LONG_TIME (512)
|
||||
#define ELECTRA_READ_JITTER_TIME (100)
|
||||
|
||||
#define ELECTRA_READ_SHORT_TIME_LOW (ELECTRA_READ_SHORT_TIME - ELECTRA_READ_JITTER_TIME)
|
||||
#define ELECTRA_READ_SHORT_TIME_HIGH (ELECTRA_READ_SHORT_TIME + ELECTRA_READ_JITTER_TIME)
|
||||
#define ELECTRA_READ_LONG_TIME_LOW (ELECTRA_READ_LONG_TIME - ELECTRA_READ_JITTER_TIME)
|
||||
#define ELECTRA_READ_LONG_TIME_HIGH (ELECTRA_READ_LONG_TIME + ELECTRA_READ_JITTER_TIME)
|
||||
|
||||
#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL)
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[ELECTRA_DECODED_DATA_SIZE];
|
||||
|
||||
ElectraDecodedData encoded_base_data;
|
||||
ElectraDecodedData encoded_epilogue;
|
||||
|
||||
uint8_t encoded_data_index;
|
||||
bool encoded_polarity;
|
||||
|
||||
ManchesterState decoder_manchester_state;
|
||||
} ProtocolElectra;
|
||||
|
||||
ProtocolElectra* protocol_electra_alloc(void) {
|
||||
ProtocolElectra* proto = malloc(sizeof(ProtocolElectra));
|
||||
return (void*)proto;
|
||||
};
|
||||
|
||||
void protocol_electra_free(ProtocolElectra* proto) {
|
||||
free(proto);
|
||||
};
|
||||
|
||||
uint8_t* protocol_electra_get_data(ProtocolElectra* proto) {
|
||||
return proto->data;
|
||||
};
|
||||
|
||||
static void electra_decode(
|
||||
const uint8_t* encoded_base_data,
|
||||
const uint8_t encoded_base_data_size,
|
||||
const uint8_t* encoded_epilogue,
|
||||
const uint8_t encoded_epilogue_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= ELECTRA_DECODED_DATA_SIZE);
|
||||
furi_check(encoded_base_data_size >= ELECTRA_ENCODED_BASE_DATA_SIZE);
|
||||
furi_check(encoded_epilogue_size >= ELECTRA_ENCODED_EPILOGUE_SIZE);
|
||||
|
||||
uint8_t decoded_data_index = 0;
|
||||
ElectraDecodedData base_data = *((ElectraDecodedData*)(encoded_base_data));
|
||||
//ElectraDecodedData epilogue = *((ElectraDecodedData*)(encoded_epilogue));
|
||||
|
||||
// clean result
|
||||
memset(decoded_data, 0, decoded_data_size);
|
||||
|
||||
// header
|
||||
for(uint8_t i = 0; i < 9; i++) {
|
||||
base_data = base_data << 1;
|
||||
}
|
||||
|
||||
// nibbles
|
||||
uint8_t value = 0;
|
||||
for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
|
||||
uint8_t nibble = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
if(i < 4) nibble = (nibble << 1) | (base_data & (1LLU << 63) ? 1 : 0);
|
||||
base_data = base_data << 1;
|
||||
}
|
||||
value = (value << 4) | nibble;
|
||||
if(r % 2) {
|
||||
decoded_data[decoded_data_index] |= value;
|
||||
decoded_data_index++;
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// copy first 3 bytes of encoded epilogue to decoded data
|
||||
decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS] =
|
||||
encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 1];
|
||||
decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 1] =
|
||||
encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 2];
|
||||
decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 2] =
|
||||
encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 3];
|
||||
}
|
||||
|
||||
static bool electra_can_be_decoded(
|
||||
const uint8_t* encoded_base_data,
|
||||
const uint8_t encoded_base_data_size,
|
||||
const uint8_t* encoded_epilogue_data,
|
||||
const uint8_t encoded_epilogue_data_size) {
|
||||
furi_check(encoded_base_data_size >= ELECTRA_ENCODED_BASE_DATA_SIZE);
|
||||
furi_check(encoded_epilogue_data_size >= ELECTRA_ENCODED_EPILOGUE_SIZE);
|
||||
const ElectraDecodedData* base_data = (ElectraDecodedData*)encoded_base_data;
|
||||
const ElectraDecodedData* epilogue = (ElectraDecodedData*)encoded_epilogue_data;
|
||||
|
||||
// check electra epilogue. if em4100 header - break
|
||||
if((*epilogue & EM_ENCODED_DATA_HEADER) == EM_ENCODED_DATA_HEADER) return false;
|
||||
|
||||
// check header and stop bit
|
||||
if((*base_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
|
||||
|
||||
// check row parity
|
||||
for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
|
||||
uint8_t parity_sum = 0;
|
||||
|
||||
for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {
|
||||
parity_sum += (*base_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;
|
||||
}
|
||||
|
||||
if((parity_sum % 2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check columns parity
|
||||
for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {
|
||||
uint8_t parity_sum = 0;
|
||||
|
||||
for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
|
||||
parity_sum += (*base_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;
|
||||
}
|
||||
|
||||
if((parity_sum % 2)) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Unexpected column parity found. EM4100 data: %016llX",
|
||||
bit_lib_bytes_to_num_be(encoded_base_data, encoded_base_data_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// encoded_epilogue_data lsb encoded
|
||||
uint8_t epilogue_filler = encoded_epilogue_data[(ELECTRA_ENCODED_EPILOGUE_SIZE - 1) - 2];
|
||||
|
||||
for(uint8_t i = 0; i < ((ELECTRA_ENCODED_EPILOGUE_SIZE - 1) - 2); i++)
|
||||
if(encoded_epilogue_data[i] != epilogue_filler) {
|
||||
FURI_LOG_D(TAG, "Unexpected epilogue filler found: %016llX", *epilogue);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void protocol_electra_decoder_start(ProtocolElectra* proto) {
|
||||
memset(proto->data, 0, ELECTRA_DECODED_DATA_SIZE);
|
||||
proto->encoded_base_data = 0;
|
||||
proto->encoded_epilogue = 0;
|
||||
|
||||
manchester_advance(
|
||||
proto->decoder_manchester_state,
|
||||
ManchesterEventReset,
|
||||
&proto->decoder_manchester_state,
|
||||
NULL);
|
||||
};
|
||||
|
||||
bool protocol_electra_decoder_feed(ProtocolElectra* proto, bool level, uint32_t duration) {
|
||||
bool result = false;
|
||||
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
|
||||
if(duration > ELECTRA_READ_SHORT_TIME_LOW && duration < ELECTRA_READ_SHORT_TIME_HIGH) {
|
||||
if(!level) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else {
|
||||
event = ManchesterEventShortLow;
|
||||
}
|
||||
} else if(duration > ELECTRA_READ_LONG_TIME_LOW && duration < ELECTRA_READ_LONG_TIME_HIGH) {
|
||||
if(!level) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
event = ManchesterEventLongLow;
|
||||
}
|
||||
}
|
||||
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = manchester_advance(
|
||||
proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
/*
|
||||
EM 4100 BASE DATA (64 bit) ELECTRA EPILOGUE (64 bit)
|
||||
_________________________________ _________________________________
|
||||
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | <- new data bit
|
||||
--------------------------------- ---------------------------------
|
||||
<- epilogue msb is carry bit to base data
|
||||
*/
|
||||
bool carry = proto->encoded_epilogue >> 63 & 0b1;
|
||||
|
||||
proto->encoded_base_data = (proto->encoded_base_data << 1) | carry;
|
||||
proto->encoded_epilogue = (proto->encoded_epilogue << 1) | data;
|
||||
|
||||
if(electra_can_be_decoded(
|
||||
(uint8_t*)&proto->encoded_base_data,
|
||||
ELECTRA_ENCODED_BASE_DATA_SIZE,
|
||||
(uint8_t*)&proto->encoded_epilogue,
|
||||
ELECTRA_ENCODED_EPILOGUE_SIZE)) {
|
||||
electra_decode(
|
||||
(uint8_t*)&proto->encoded_base_data,
|
||||
ELECTRA_ENCODED_BASE_DATA_SIZE,
|
||||
(uint8_t*)&proto->encoded_epilogue,
|
||||
ELECTRA_ENCODED_EPILOGUE_SIZE,
|
||||
proto->data,
|
||||
ELECTRA_DECODED_DATA_SIZE);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void em_write_nibble(bool low_nibble, uint8_t data, ElectraDecodedData* encoded_base_data) {
|
||||
uint8_t parity_sum = 0;
|
||||
uint8_t start = 0;
|
||||
if(!low_nibble) start = 4;
|
||||
|
||||
for(int8_t i = (start + 3); i >= start; i--) {
|
||||
parity_sum += (data >> i) & 1;
|
||||
*encoded_base_data = (*encoded_base_data << 1) | ((data >> i) & 1);
|
||||
}
|
||||
|
||||
*encoded_base_data = (*encoded_base_data << 1) | ((parity_sum % 2) & 1);
|
||||
}
|
||||
|
||||
bool protocol_electra_encoder_start(ProtocolElectra* proto) {
|
||||
// header
|
||||
proto->encoded_base_data = 0b111111111;
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < ELECTRA_DECODED_BASE_DATA_SIZE; i++) {
|
||||
em_write_nibble(false, proto->data[i], &proto->encoded_base_data);
|
||||
em_write_nibble(true, proto->data[i], &proto->encoded_base_data);
|
||||
}
|
||||
|
||||
// column parity and stop bit
|
||||
uint8_t parity_sum;
|
||||
|
||||
for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
|
||||
parity_sum = 0;
|
||||
for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {
|
||||
uint8_t parity_bit = (proto->encoded_base_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
|
||||
parity_sum += parity_bit;
|
||||
}
|
||||
proto->encoded_base_data = (proto->encoded_base_data << 1) | ((parity_sum % 2) & 1);
|
||||
}
|
||||
|
||||
// stop bit
|
||||
proto->encoded_base_data = (proto->encoded_base_data << 1) | 0;
|
||||
|
||||
proto->encoded_data_index = 0;
|
||||
proto->encoded_polarity = true;
|
||||
|
||||
// epilogue
|
||||
proto->encoded_epilogue = (proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS]);
|
||||
proto->encoded_epilogue <<= 8;
|
||||
proto->encoded_epilogue |= (proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 1]);
|
||||
|
||||
//fill bytes 2-7 by epilogue filler
|
||||
for(uint8_t i = 2; i < ELECTRA_ENCODED_EPILOGUE_SIZE; i++) {
|
||||
proto->encoded_epilogue <<= 8;
|
||||
proto->encoded_epilogue |= proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 2];
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
LevelDuration protocol_electra_encoder_yield(ProtocolElectra* proto) {
|
||||
bool level;
|
||||
if(proto->encoded_data_index < 64)
|
||||
level = (proto->encoded_base_data >> (63 - proto->encoded_data_index)) & 1;
|
||||
else
|
||||
level = (proto->encoded_epilogue >> (63 - (proto->encoded_data_index - 64))) & 1;
|
||||
|
||||
uint32_t duration = ELECTRA_CLOCK_PER_BIT / 2;
|
||||
|
||||
if(proto->encoded_polarity) {
|
||||
proto->encoded_polarity = false;
|
||||
} else {
|
||||
level = !level;
|
||||
|
||||
proto->encoded_polarity = true;
|
||||
proto->encoded_data_index++;
|
||||
if(proto->encoded_data_index >= 128) {
|
||||
proto->encoded_data_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return level_duration_make(level, duration);
|
||||
};
|
||||
|
||||
bool protocol_electra_write_data(ProtocolElectra* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
// Correct protocol data by redecoding
|
||||
protocol_electra_encoder_start(protocol);
|
||||
electra_decode(
|
||||
(uint8_t*)&protocol->encoded_base_data,
|
||||
sizeof(ElectraDecodedData),
|
||||
(uint8_t*)&protocol->encoded_epilogue,
|
||||
sizeof(ElectraDecodedData),
|
||||
protocol->data,
|
||||
ELECTRA_DECODED_DATA_SIZE);
|
||||
|
||||
protocol_electra_encoder_start(protocol);
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
request->t5577.block[0] =
|
||||
(LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_64 |
|
||||
(4 << LFRFID_T5577_MAXBLOCK_SHIFT));
|
||||
request->t5577.block[1] = protocol->encoded_base_data >> 32;
|
||||
request->t5577.block[2] = protocol->encoded_base_data & 0xFFFFFFFF;
|
||||
request->t5577.block[3] = protocol->encoded_epilogue >> 32;
|
||||
request->t5577.block[4] = protocol->encoded_epilogue & 0xFFFFFFFF;
|
||||
request->t5577.blocks_to_write = 5;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
void protocol_electra_render_data(ProtocolElectra* protocol, FuriString* result) {
|
||||
furi_string_printf(result, "Epilogue: %016llX", protocol->encoded_epilogue);
|
||||
};
|
||||
|
||||
const ProtocolBase protocol_electra = {
|
||||
.name = "Electra",
|
||||
.manufacturer = "Electra Group",
|
||||
.data_size = ELECTRA_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeatureASK | LFRFIDFeaturePSK,
|
||||
.validate_count = 3,
|
||||
.alloc = (ProtocolAlloc)protocol_electra_alloc,
|
||||
.free = (ProtocolFree)protocol_electra_free,
|
||||
.get_data = (ProtocolGetData)protocol_electra_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_electra_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_electra_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_electra_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_electra_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_electra_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_electra_render_data,
|
||||
.write_data = (ProtocolWriteData)protocol_electra_write_data,
|
||||
};
|
4
lib/lfrfid/protocols/protocol_electra.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_electra;
|
@ -4,6 +4,7 @@
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
typedef uint64_t EM4100DecodedData;
|
||||
typedef uint64_t EM4100Epilogue;
|
||||
|
||||
#define EM_HEADER_POS (55)
|
||||
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
|
||||
@ -28,10 +29,13 @@ typedef uint64_t EM4100DecodedData;
|
||||
#define EM_READ_LONG_TIME_BASE (512)
|
||||
#define EM_READ_JITTER_TIME_BASE (100)
|
||||
|
||||
#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL)
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[EM4100_DECODED_DATA_SIZE];
|
||||
|
||||
EM4100DecodedData encoded_data;
|
||||
EM4100Epilogue encoded_epilogue;
|
||||
uint8_t encoded_data_index;
|
||||
bool encoded_polarity;
|
||||
|
||||
@ -147,9 +151,16 @@ static void em4100_decode(
|
||||
}
|
||||
}
|
||||
|
||||
static bool em4100_can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
|
||||
static bool em4100_can_be_decoded(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
const uint8_t* encoded_epilogue) {
|
||||
furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE);
|
||||
const EM4100DecodedData* card_data = (EM4100DecodedData*)encoded_data;
|
||||
const EM4100Epilogue* epilogue = (EM4100Epilogue*)encoded_epilogue;
|
||||
|
||||
// check first 9 bytes on epilogue (to prevent conflict with Electra protocol)
|
||||
if((*epilogue & EM_ENCODED_DATA_HEADER) != EM_ENCODED_DATA_HEADER) return false;
|
||||
|
||||
// check header and stop bit
|
||||
if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
|
||||
@ -221,9 +232,15 @@ bool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t du
|
||||
proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
proto->encoded_data = (proto->encoded_data << 1) | data;
|
||||
bool carry = proto->encoded_epilogue >> 63 & 0b1;
|
||||
|
||||
if(em4100_can_be_decoded((uint8_t*)&proto->encoded_data, sizeof(EM4100DecodedData))) {
|
||||
proto->encoded_data = (proto->encoded_data << 1) | carry;
|
||||
proto->encoded_epilogue = (proto->encoded_epilogue << 1) | data;
|
||||
|
||||
if(em4100_can_be_decoded(
|
||||
(uint8_t*)&proto->encoded_data,
|
||||
sizeof(EM4100DecodedData),
|
||||
(uint8_t*)&proto->encoded_epilogue)) {
|
||||
em4100_decode(
|
||||
(uint8_t*)&proto->encoded_data,
|
||||
sizeof(EM4100DecodedData),
|
||||
|
@ -75,10 +75,10 @@ MfDesfireError mf_desfire_send_chunks(
|
||||
const size_t rx_capacity_remaining =
|
||||
bit_buffer_get_capacity_bytes(rx_buffer) - bit_buffer_get_size_bytes(rx_buffer);
|
||||
|
||||
if(rx_size <= rx_capacity_remaining) {
|
||||
if(rx_size <= rx_capacity_remaining + 1) {
|
||||
bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size);
|
||||
FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1);
|
||||
}
|
||||
}
|
||||
} while(false);
|
||||
@ -327,36 +327,63 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi(
|
||||
return error;
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_read_file_data(
|
||||
static MfDesfireError mf_desfire_poller_read_file(
|
||||
MfDesfirePoller* instance,
|
||||
MfDesfireFileId id,
|
||||
uint8_t read_cmd,
|
||||
uint32_t offset,
|
||||
size_t size,
|
||||
MfDesfireFileData* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA);
|
||||
bit_buffer_append_byte(instance->input_buffer, id);
|
||||
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3);
|
||||
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3);
|
||||
MfDesfireError error = MfDesfireErrorNone;
|
||||
simple_array_init(data->data, size);
|
||||
|
||||
MfDesfireError error;
|
||||
size_t buffer_capacity = bit_buffer_get_capacity_bytes(instance->result_buffer);
|
||||
uint32_t current_offset = offset;
|
||||
uint32_t bytes_read = 0;
|
||||
|
||||
while(bytes_read < size) {
|
||||
size_t bytes_to_read = MIN(buffer_capacity, size - bytes_read);
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, read_cmd);
|
||||
bit_buffer_append_byte(instance->input_buffer, id);
|
||||
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)¤t_offset, 3);
|
||||
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&bytes_to_read, 3);
|
||||
|
||||
do {
|
||||
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
|
||||
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
|
||||
size_t bytes_received = bit_buffer_get_size_bytes(instance->result_buffer);
|
||||
if(bytes_received != bytes_to_read) {
|
||||
FURI_LOG_W(TAG, "Read %zu out of %zu bytes", bytes_received, bytes_to_read);
|
||||
error = MfDesfireErrorProtocol;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
uint8_t* file_data = simple_array_get_data(data->data);
|
||||
bit_buffer_write_bytes(instance->result_buffer, &file_data[current_offset], bytes_to_read);
|
||||
bytes_read += bytes_to_read;
|
||||
current_offset += bytes_to_read;
|
||||
}
|
||||
|
||||
if(error != MfDesfireErrorNone) {
|
||||
simple_array_reset(data->data);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_read_file_data(
|
||||
MfDesfirePoller* instance,
|
||||
MfDesfireFileId id,
|
||||
uint32_t offset,
|
||||
size_t size,
|
||||
MfDesfireFileData* data) {
|
||||
return mf_desfire_poller_read_file(instance, id, MF_DESFIRE_CMD_READ_DATA, offset, size, data);
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_read_file_value(
|
||||
MfDesfirePoller* instance,
|
||||
MfDesfireFileId id,
|
||||
@ -389,28 +416,8 @@ MfDesfireError mf_desfire_poller_read_file_records(
|
||||
uint32_t offset,
|
||||
size_t size,
|
||||
MfDesfireFileData* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_RECORDS);
|
||||
bit_buffer_append_byte(instance->input_buffer, id);
|
||||
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3);
|
||||
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3);
|
||||
|
||||
MfDesfireError error;
|
||||
|
||||
do {
|
||||
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
|
||||
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
|
||||
error = MfDesfireErrorProtocol;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
return mf_desfire_poller_read_file(
|
||||
instance, id, MF_DESFIRE_CMD_READ_RECORDS, offset, size, data);
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_read_file_data_multi(
|
||||
|
@ -28,8 +28,7 @@ void pretty_format_bytes_hex_canonical(
|
||||
const size_t line_length = (line_prefix ? strlen(line_prefix) : 0) + 4 * num_places + 2;
|
||||
|
||||
/* Reserve memory in adance in order to avoid unnecessary reallocs */
|
||||
furi_string_reset(result);
|
||||
furi_string_reserve(result, line_count * line_length);
|
||||
furi_string_reserve(result, furi_string_size(result) + line_count * line_length);
|
||||
|
||||
for(size_t i = 0; i < data_size; i += num_places) {
|
||||
if(line_prefix) {
|
||||
|
@ -198,12 +198,21 @@ LevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_in
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_dict_render_uid(ProtocolDict* dict, FuriString* result, size_t protocol_index) {
|
||||
furi_check(protocol_index < dict->count);
|
||||
ProtocolRenderData fn = dict->base[protocol_index]->render_uid;
|
||||
|
||||
if(fn) {
|
||||
fn(dict->data[protocol_index], result);
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_dict_render_data(ProtocolDict* dict, FuriString* result, size_t protocol_index) {
|
||||
furi_check(protocol_index < dict->count);
|
||||
ProtocolRenderData fn = dict->base[protocol_index]->render_data;
|
||||
|
||||
if(fn) {
|
||||
return fn(dict->data[protocol_index], result);
|
||||
fn(dict->data[protocol_index], result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,7 +221,7 @@ void protocol_dict_render_brief_data(ProtocolDict* dict, FuriString* result, siz
|
||||
ProtocolRenderData fn = dict->base[protocol_index]->render_brief_data;
|
||||
|
||||
if(fn) {
|
||||
return fn(dict->data[protocol_index], result);
|
||||
fn(dict->data[protocol_index], result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,8 @@ bool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index);
|
||||
|
||||
LevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index);
|
||||
|
||||
void protocol_dict_render_uid(ProtocolDict* dict, FuriString* result, size_t protocol_index);
|
||||
|
||||
void protocol_dict_render_data(ProtocolDict* dict, FuriString* result, size_t protocol_index);
|
||||
|
||||
void protocol_dict_render_brief_data(ProtocolDict* dict, FuriString* result, size_t protocol_index);
|
||||
|
@ -2251,6 +2251,7 @@ Function,+,protocol_dict_get_validate_count,uint32_t,"ProtocolDict*, size_t"
|
||||
Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*"
|
||||
Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t"
|
||||
Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t"
|
||||
Function,+,protocol_dict_render_uid,void,"ProtocolDict*, FuriString*, size_t"
|
||||
Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t"
|
||||
Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t"
|
||||
Function,-,pulse_reader_free,void,PulseReader*
|
||||
|
|
@ -17,6 +17,7 @@ void furi_hal_init_early(void) {
|
||||
furi_hal_i2c_init_early();
|
||||
furi_hal_light_init();
|
||||
furi_hal_rtc_init_early();
|
||||
furi_hal_version_init();
|
||||
}
|
||||
|
||||
void furi_hal_deinit_early(void) {
|
||||
@ -39,7 +40,6 @@ void furi_hal_init(void) {
|
||||
furi_hal_interrupt_init();
|
||||
furi_hal_flash_init();
|
||||
furi_hal_resources_init();
|
||||
furi_hal_version_init();
|
||||
furi_hal_spi_config_init();
|
||||
furi_hal_spi_dma_init();
|
||||
furi_hal_speaker_init();
|
||||
|
@ -2864,6 +2864,7 @@ Function,+,protocol_dict_get_validate_count,uint32_t,"ProtocolDict*, size_t"
|
||||
Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*"
|
||||
Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t"
|
||||
Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t"
|
||||
Function,+,protocol_dict_render_uid,void,"ProtocolDict*, FuriString*, size_t"
|
||||
Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t"
|
||||
Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t"
|
||||
Function,-,pulse_reader_free,void,PulseReader*
|
||||
|
|
@ -38,6 +38,8 @@ typedef struct {
|
||||
FuriThread* thread;
|
||||
FuriMessageQueue* command_queue;
|
||||
bool enable_adv;
|
||||
bool is_secure;
|
||||
uint8_t negotiation_round;
|
||||
} Gap;
|
||||
|
||||
typedef enum {
|
||||
@ -72,17 +74,46 @@ static void gap_verify_connection_parameters(Gap* gap) {
|
||||
|
||||
// Send connection parameters request update if necessary
|
||||
GapConnectionParamsRequest* params = &gap->config->conn_param;
|
||||
if(params->conn_int_min > gap->connection_params.conn_interval ||
|
||||
params->conn_int_max < gap->connection_params.conn_interval) {
|
||||
FURI_LOG_W(TAG, "Unsupported connection interval. Request connection parameters update");
|
||||
|
||||
// Desired max connection interval depends on how many negotiation rounds we had in the past
|
||||
// In the first negotiation round we want connection interval to be minimum
|
||||
// If platform disagree then we request wider range
|
||||
uint16_t connection_interval_max = gap->negotiation_round ? params->conn_int_max :
|
||||
params->conn_int_min;
|
||||
|
||||
// We do care about lower connection interval bound a lot: if it's lower than 30ms 2nd core will not allow us to use flash controller
|
||||
bool negotiation_failed = params->conn_int_min > gap->connection_params.conn_interval;
|
||||
|
||||
// We don't care about upper bound till connection become secure
|
||||
if(gap->is_secure) {
|
||||
negotiation_failed |= connection_interval_max < gap->connection_params.conn_interval;
|
||||
}
|
||||
|
||||
if(negotiation_failed) {
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"Connection interval doesn't suite us. Trying to negotiate, round %u",
|
||||
gap->negotiation_round + 1);
|
||||
if(aci_l2cap_connection_parameter_update_req(
|
||||
gap->service.connection_handle,
|
||||
params->conn_int_min,
|
||||
params->conn_int_max,
|
||||
connection_interval_max,
|
||||
gap->connection_params.slave_latency,
|
||||
gap->connection_params.supervisor_timeout)) {
|
||||
FURI_LOG_E(TAG, "Failed to request connection parameters update");
|
||||
// The other side is not in the mood
|
||||
// But we are open to try it again
|
||||
gap->negotiation_round = 0;
|
||||
} else {
|
||||
gap->negotiation_round++;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Connection interval suits us. Spent %u rounds to negotiate",
|
||||
gap->negotiation_round);
|
||||
// Looks like the other side is open to negotiation
|
||||
gap->negotiation_round = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,9 +128,9 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) {
|
||||
|
||||
event_pckt = (hci_event_pckt*)((hci_uart_pckt*)pckt)->data;
|
||||
|
||||
if(gap) {
|
||||
furi_mutex_acquire(gap->state_mutex, FuriWaitForever);
|
||||
}
|
||||
furi_check(gap);
|
||||
furi_mutex_acquire(gap->state_mutex, FuriWaitForever);
|
||||
|
||||
switch(event_pckt->evt) {
|
||||
case HCI_DISCONNECTION_COMPLETE_EVT_CODE: {
|
||||
hci_disconnection_complete_event_rp0* disconnection_complete_event =
|
||||
@ -110,6 +141,8 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) {
|
||||
FURI_LOG_I(
|
||||
TAG, "Disconnect from client. Reason: %02X", disconnection_complete_event->Reason);
|
||||
}
|
||||
gap->is_secure = false;
|
||||
gap->negotiation_round = 0;
|
||||
// Enterprise sleep
|
||||
furi_delay_us(666 + 666);
|
||||
if(gap->enable_adv) {
|
||||
@ -211,6 +244,7 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) {
|
||||
|
||||
case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE:
|
||||
FURI_LOG_D(TAG, "Slave security initiated");
|
||||
gap->is_secure = true;
|
||||
break;
|
||||
|
||||
case ACI_GAP_BOND_LOST_VSEVT_CODE:
|
||||
@ -269,9 +303,9 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(gap) {
|
||||
furi_mutex_release(gap->state_mutex);
|
||||
}
|
||||
|
||||
furi_mutex_release(gap->state_mutex);
|
||||
|
||||
return BleEventFlowEnable;
|
||||
}
|
||||
|
||||
@ -512,6 +546,10 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
|
||||
gap->thread = furi_thread_alloc_ex("BleGapDriver", 1024, gap_app, gap);
|
||||
furi_thread_start(gap->thread);
|
||||
|
||||
// Set initial state
|
||||
gap->is_secure = false;
|
||||
gap->negotiation_round = 0;
|
||||
|
||||
uint8_t adv_service_uid[2];
|
||||
gap->service.adv_svc_uuid_len = 1;
|
||||
adv_service_uid[0] = gap->config->adv_service_uuid & 0xff;
|
||||
|
@ -47,7 +47,7 @@ static GapConfig serial_template_config = {
|
||||
.pairing_method = GapPairingPinCodeShow,
|
||||
.conn_param = {
|
||||
.conn_int_min = 0x18, // AN5289: 4.7, we need at least 25ms + advertisement, which is 30 ms
|
||||
.conn_int_max = 0x18, // 30 ms
|
||||
.conn_int_max = 0x24, // 45 ms
|
||||
.slave_latency = 0,
|
||||
.supervisor_timeout = 0,
|
||||
}};
|
||||
|
@ -17,6 +17,7 @@ void furi_hal_init_early(void) {
|
||||
furi_hal_i2c_init_early();
|
||||
furi_hal_light_init();
|
||||
furi_hal_rtc_init_early();
|
||||
furi_hal_version_init();
|
||||
}
|
||||
|
||||
void furi_hal_deinit_early(void) {
|
||||
@ -39,7 +40,6 @@ void furi_hal_init(void) {
|
||||
furi_hal_interrupt_init();
|
||||
furi_hal_flash_init();
|
||||
furi_hal_resources_init();
|
||||
furi_hal_version_init();
|
||||
furi_hal_region_init();
|
||||
furi_hal_spi_config_init();
|
||||
furi_hal_spi_dma_init();
|
||||
|