diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 7e625229..527e9a71 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -10,7 +10,7 @@ env: jobs: run_units_on_bench: - runs-on: [self-hosted, FlipperZeroTest] + runs-on: [self-hosted, FlipperZeroUnitTest] steps: - name: 'Decontaminate previous build leftovers' run: | diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index 0b02920f..300440aa 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -10,7 +10,7 @@ env: jobs: test_updater_on_bench: - runs-on: [self-hosted, FlipperZeroTestMac1] + runs-on: [self-hosted, FlipperZeroUpdaterTest] steps: - name: 'Decontaminate previous build leftovers' run: | @@ -27,7 +27,7 @@ jobs: - name: 'Get flipper from device manager (mock)' id: device run: | - echo "flipper=/dev/tty.usbmodemflip_Rekigyn1" >> $GITHUB_OUTPUT + echo "flipper=Rekigyn" >> $GITHUB_OUTPUT echo "stlink=0F020D026415303030303032" >> $GITHUB_OUTPUT - name: 'Flashing target firmware' diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index 5c46d397..f7a9f826 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -11,9 +11,10 @@ "args": { "useSingleResult": true, "env": { - "PATH": "${workspaceFolder};${env:PATH}" + "PATH": "${workspaceFolder};${env:PATH}", + "FBT_QUIET": 1 }, - "command": "./fbt get_blackmagic", + "command": "fbt get_blackmagic", "description": "Get Blackmagic device", } } diff --git a/applications/debug/unit_tests/power/power_test.c b/applications/debug/unit_tests/power/power_test.c index ce2c7aad..a9b66b22 100644 --- a/applications/debug/unit_tests/power/power_test.c +++ b/applications/debug/unit_tests/power/power_test.c @@ -3,56 +3,63 @@ #include "../minunit.h" static void power_test_deinit(void) { - // Try to reset to default charging voltage - furi_hal_power_set_battery_charging_voltage(4.208f); + // Try to reset to default charge voltage limit + furi_hal_power_set_battery_charge_voltage_limit(4.208f); } -MU_TEST(test_power_charge_voltage_exact) { - // Power of 16mV charge voltages get applied exactly +MU_TEST(test_power_charge_voltage_limit_exact) { + // Power of 16mV charge voltage limits get applied exactly // (bq25896 charge controller works in 16mV increments) // // This test may need adapted if other charge controllers are used in the future. for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) { float charge_volt = (float)charge_mv / 1000.0f; - furi_hal_power_set_battery_charging_voltage(charge_volt); - mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(charge_volt); + mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charge_voltage_limit()); } } -MU_TEST(test_power_charge_voltage_floating_imprecision) { +MU_TEST(test_power_charge_voltage_limit_floating_imprecision) { // 4.016f should act as 4.016 V, even with floating point imprecision - furi_hal_power_set_battery_charging_voltage(4.016f); - mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.016f); + mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charge_voltage_limit()); } -MU_TEST(test_power_charge_voltage_inexact) { - // Charge voltages that are not power of 16mV get truncated down - furi_hal_power_set_battery_charging_voltage(3.841f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); +MU_TEST(test_power_charge_voltage_limit_inexact) { + // Charge voltage limits that are not power of 16mV get truncated down + furi_hal_power_set_battery_charge_voltage_limit(3.841f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); - furi_hal_power_set_battery_charging_voltage(3.900f); - mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(3.900f); + mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit()); - furi_hal_power_set_battery_charging_voltage(4.200f); - mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.200f); + mu_assert_double_eq(4.192, furi_hal_power_get_battery_charge_voltage_limit()); } -MU_TEST(test_power_charge_voltage_invalid_clamped) { - // Out-of-range charge voltages get clamped to 3.840 V and 4.208 V - furi_hal_power_set_battery_charging_voltage(3.808f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); +MU_TEST(test_power_charge_voltage_limit_invalid_clamped) { + // Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V + furi_hal_power_set_battery_charge_voltage_limit(3.808f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); + furi_hal_power_set_battery_charge_voltage_limit(1.0f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); // NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an // unhappy battery if this fails. - furi_hal_power_set_battery_charging_voltage(4.240f); - mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.240f); + mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit()); + // Likewise, picking a number that the uint8_t wraparound in the driver would result in a + // VREG value under 23 if this test fails. + // E.g. (uint8_t)((8105-3840)/16) -> 10 + furi_hal_power_set_battery_charge_voltage_limit(8.105f); + mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit()); } MU_TEST_SUITE(test_power_suite) { - MU_RUN_TEST(test_power_charge_voltage_exact); - MU_RUN_TEST(test_power_charge_voltage_floating_imprecision); - MU_RUN_TEST(test_power_charge_voltage_inexact); - MU_RUN_TEST(test_power_charge_voltage_invalid_clamped); + MU_RUN_TEST(test_power_charge_voltage_limit_exact); + MU_RUN_TEST(test_power_charge_voltage_limit_floating_imprecision); + MU_RUN_TEST(test_power_charge_voltage_limit_inexact); + MU_RUN_TEST(test_power_charge_voltage_limit_invalid_clamped); power_test_deinit(); } diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index 1b249579..ea97c448 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -142,10 +142,6 @@ void bad_usb_app_free(BadUsbApp* app) { app->bad_usb_script = NULL; } - if(app->usb_if_prev) { - furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL)); - } - // Views view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); bad_usb_free(app->bad_usb_view); @@ -172,6 +168,10 @@ void bad_usb_app_free(BadUsbApp* app) { furi_string_free(app->file_path); furi_string_free(app->keyboard_layout); + if(app->usb_if_prev) { + furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL)); + } + free(app); } diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 34dfec2c..beb35b89 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -32,6 +32,7 @@ struct BadUsbScript { FuriString* file_path; uint32_t defdelay; uint16_t layout[128]; + uint32_t stringdelay; FuriThread* thread; uint8_t file_buf[FILE_BUFFER_LEN + 1]; uint8_t buf_start; @@ -113,6 +114,8 @@ static const char ducky_cmd_delay[] = {"DELAY "}; static const char ducky_cmd_string[] = {"STRING "}; static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "}; +static const char ducky_cmd_stringdelay_1[] = {"STRINGDELAY "}; +static const char ducky_cmd_stringdelay_2[] = {"STRING_DELAY "}; static const char ducky_cmd_repeat[] = {"REPEAT "}; static const char ducky_cmd_sysrq[] = {"SYSRQ "}; @@ -211,14 +214,19 @@ static bool ducky_altstring(const char* param) { static bool ducky_string(BadUsbScript* bad_usb, const char* param) { uint32_t i = 0; + while(param[i] != '\0') { uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); if(keycode != HID_KEYBOARD_NONE) { furi_hal_hid_kb_press(keycode); furi_hal_hid_kb_release(keycode); + if(bad_usb->stringdelay > 0) { + furi_delay_ms(bad_usb->stringdelay); + } } i++; } + bad_usb->stringdelay = 0; return true; } @@ -277,6 +285,20 @@ static int32_t snprintf(error, error_len, "Invalid number %s", line_tmp); } return (state) ? (0) : SCRIPT_STATE_ERROR; + } else if( + (strncmp(line_tmp, ducky_cmd_stringdelay_1, strlen(ducky_cmd_stringdelay_1)) == 0) || + (strncmp(line_tmp, ducky_cmd_stringdelay_2, strlen(ducky_cmd_stringdelay_2)) == 0)) { + //STRINGDELAY, finally it's here + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + state = ducky_get_number(line_tmp, &bad_usb->stringdelay); + if((state) && (bad_usb->stringdelay > 0)) { + return state; + } + if(error != NULL) { + snprintf(error, error_len, "Invalid number %s", line_tmp); + } + return SCRIPT_STATE_ERROR; + } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { // STRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; @@ -484,6 +506,19 @@ static void bad_usb_hid_state_callback(bool state, void* context) { furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); } +static uint32_t bad_usb_flags_get(uint32_t flags_mask, uint32_t timeout) { + uint32_t flags = furi_thread_flags_get(); + furi_check((flags & FuriFlagError) == 0); + if(flags == 0) { + flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); + furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); + } else { + uint32_t state = furi_thread_flags_clear(flags); + furi_check((state & FuriFlagError) == 0); + } + return flags; +} + static int32_t bad_usb_worker(void* context) { BadUsbScript* bad_usb = context; @@ -520,11 +555,9 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { @@ -535,11 +568,9 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateIdle) { // State: ready to start - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever); + if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtToggle) { // Start executing script @@ -548,6 +579,7 @@ static int32_t bad_usb_worker(void* context) { bad_usb->buf_len = 0; bad_usb->st.line_cur = 0; bad_usb->defdelay = 0; + bad_usb->stringdelay = 0; bad_usb->repeat_cnt = 0; bad_usb->file_end = false; storage_file_seek(script_file, 0, true); @@ -558,11 +590,9 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateWillRun) { // State: start on connection - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { // Start executing script @@ -571,12 +601,22 @@ static int32_t bad_usb_worker(void* context) { bad_usb->buf_len = 0; bad_usb->st.line_cur = 0; bad_usb->defdelay = 0; + bad_usb->stringdelay = 0; bad_usb->repeat_cnt = 0; bad_usb->file_end = false; storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard - furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); - worker_state = BadUsbStateRunning; + flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle, + FuriFlagWaitAny | FuriFlagNoClear, + 1500); + if(flags == (unsigned)FuriFlagErrorTimeout) { + // If nothing happened - start script execution + worker_state = BadUsbStateRunning; + } else if(flags & WorkerEvtToggle) { + worker_state = BadUsbStateIdle; + furi_thread_flags_clear(WorkerEvtToggle); + } } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution worker_state = BadUsbStateNotConnected; } @@ -586,6 +626,7 @@ static int32_t bad_usb_worker(void* context) { uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + delay_val -= delay_cur; if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { @@ -629,9 +670,9 @@ static int32_t bad_usb_worker(void* context) { } else if( (worker_state == BadUsbStateFileError) || (worker_state == BadUsbStateScriptError)) { // State: error - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = + bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command + if(flags & WorkerEvtEnd) { break; } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index b0466925..d6f05a1e 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -22,7 +22,6 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) { void bad_usb_scene_file_select_on_enter(void* context) { BadUsbApp* bad_usb = context; - furi_hal_usb_disable(); if(bad_usb->bad_usb_script) { bad_usb_script_close(bad_usb->bad_usb_script); bad_usb->bad_usb_script = NULL; @@ -34,7 +33,6 @@ void bad_usb_scene_file_select_on_enter(void* context) { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); } else { - furi_hal_usb_enable(); view_dispatcher_stop(bad_usb->view_dispatcher); } } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 187b83bd..6f2b8269 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -16,7 +16,9 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == InputKeyLeft) { - scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); + if(bad_usb_is_idle_state(app->bad_usb_view)) { + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); + } consumed = true; } else if(event.event == InputKeyOk) { bad_usb_script_toggle(app->bad_usb_script); diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index bb9dc3b7..9ee9dc34 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -48,17 +48,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { elements_button_center(canvas, "Run"); + elements_button_left(canvas, "Config"); } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { elements_button_center(canvas, "Stop"); } else if(model->state.state == BadUsbStateWillRun) { elements_button_center(canvas, "Cancel"); } - if((model->state.state == BadUsbStateNotConnected) || - (model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { - elements_button_left(canvas, "Config"); - } - if(model->state.state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); @@ -203,6 +199,7 @@ void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) { { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true); } + void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { furi_assert(st); with_view_model( @@ -214,3 +211,19 @@ void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { }, true); } + +bool bad_usb_is_idle_state(BadUsb* bad_usb) { + bool is_idle = false; + with_view_model( + bad_usb->view, + BadUsbModel * model, + { + if((model->state.state == BadUsbStateIdle) || + (model->state.state == BadUsbStateDone) || + (model->state.state == BadUsbStateNotConnected)) { + is_idle = true; + } + }, + false); + return is_idle; +} diff --git a/applications/main/bad_usb/views/bad_usb_view.h b/applications/main/bad_usb/views/bad_usb_view.h index 8447fb05..2fc01688 100644 --- a/applications/main/bad_usb/views/bad_usb_view.h +++ b/applications/main/bad_usb/views/bad_usb_view.h @@ -19,3 +19,5 @@ void bad_usb_set_file_name(BadUsb* bad_usb, const char* name); void bad_usb_set_layout(BadUsb* bad_usb, const char* layout); void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st); + +bool bad_usb_is_idle_state(BadUsb* bad_usb); diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index a6475ca6..23335e29 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -32,7 +32,7 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) { while(!cmd_exit) { cmd_exit |= cli_cmd_interrupt_received(cli); if(furi_hal_nfc_detect(&dev_data, 400)) { - printf("found: %s ", nfc_get_dev_type(dev_data.type)); + printf("Found: %s ", nfc_get_dev_type(dev_data.type)); printf("UID length: %d, UID:", dev_data.uid_len); for(size_t i = 0; i < dev_data.uid_len; i++) { printf("%02X", dev_data.uid[i]); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index f81fe178..a9da07df 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -29,6 +29,7 @@ ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess) +ADD_SCENE(nfc, mf_classic_data, MfClassicData) ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c new file mode 100644 index 00000000..dcb02d36 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c @@ -0,0 +1,106 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_data_on_enter(void* context) { + Nfc* nfc = context; + MfClassicType type = nfc->dev->dev_data.mf_classic_data.type; + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + TextBox* text_box = nfc->text_box; + + text_box_set_font(text_box, TextBoxFontHex); + + int card_blocks = 0; + if(type == MfClassicType1k) { + card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; + } else if(type == MfClassicType4k) { + // 16 sectors of 4 blocks each plus 8 sectors of 16 blocks each + card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4 + 8 * 16; + } else if(type == MfClassicTypeMini) { + card_blocks = MF_MINI_TOTAL_SECTORS_NUM * 4; + } + + int bytes_written = 0; + for(int block_num = 0; block_num < card_blocks; block_num++) { + bool is_sec_trailer = mf_classic_is_sector_trailer(block_num); + if(is_sec_trailer) { + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, sector_num); + // Key A + for(size_t i = 0; i < sizeof(sec_tr->key_a); i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) { + furi_string_cat_printf( + nfc->text_box_store, "%02X%02X ", sec_tr->key_a[i], sec_tr->key_a[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + // Access bytes + for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf( + nfc->text_box_store, + "%02X%02X ", + sec_tr->access_bits[i], + sec_tr->access_bits[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + // Key B + for(size_t i = 0; i < sizeof(sec_tr->key_b); i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) { + furi_string_cat_printf( + nfc->text_box_store, "%02X%02X ", sec_tr->key_b[i], sec_tr->key_b[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + } else { + // Write data block + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf( + nfc->text_box_store, + "%02X%02X ", + data->block[block_num].value[i], + data->block[block_num].value[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + } + } + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); +} + +bool nfc_scene_mf_classic_data_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_scene_mf_classic_data_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index b44ab782..92ad7b56 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -14,7 +14,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; - if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)) { + if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) || + (protocol == NfcDeviceProtocolMifareClassic)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -136,6 +137,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { } else if(protocol == NfcDeviceProtocolMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); consumed = true; + } else if(protocol == NfcDeviceProtocolMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); + consumed = true; } } } diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 04c686fb..f42e0fe9 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -150,6 +150,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { application_info_present = nfc_supported_card_verify_and_parse(dev_data); } + FURI_LOG_I("nfc", "application_info_present: %d", application_info_present); + if(application_info_present) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { diff --git a/applications/plugins/dap_link/application.fam b/applications/plugins/dap_link/application.fam index 3b99d5ef..711e4833 100644 --- a/applications/plugins/dap_link/application.fam +++ b/applications/plugins/dap_link/application.fam @@ -10,7 +10,7 @@ App( stack_size=4 * 1024, order=20, fap_icon="dap_link.png", - fap_category="Tools", + fap_category="GPIO", fap_private_libs=[ Lib( name="free-dap", diff --git a/applications/plugins/hid_app/application.fam b/applications/plugins/hid_app/application.fam index b8c13e35..b6e4e3bf 100644 --- a/applications/plugins/hid_app/application.fam +++ b/applications/plugins/hid_app/application.fam @@ -1,10 +1,10 @@ App( appid="hid_usb", - name="USB Remote", + name="Remote", apptype=FlipperAppType.PLUGIN, entry_point="hid_usb_app", stack_size=1 * 1024, - fap_category="Tools", + fap_category="USB", fap_icon="hid_usb_10px.png", fap_icon_assets="assets", fap_icon_assets_symbol="hid", @@ -13,11 +13,11 @@ App( App( appid="hid_ble", - name="Bluetooth Remote", + name="Remote", apptype=FlipperAppType.PLUGIN, entry_point="hid_ble_app", stack_size=1 * 1024, - fap_category="Tools", + fap_category="Bluetooth", fap_icon="hid_ble_10px.png", fap_icon_assets="assets", fap_icon_assets_symbol="hid", diff --git a/applications/plugins/music_player/application.fam b/applications/plugins/music_player/application.fam index a3698898..c51abf19 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/plugins/music_player/application.fam @@ -12,7 +12,7 @@ App( stack_size=2 * 1024, order=20, fap_icon="icons/music_10px.png", - fap_category="Misc", + fap_category="Media", fap_icon_assets="icons", ) diff --git a/applications/plugins/nfc_magic/application.fam b/applications/plugins/nfc_magic/application.fam index bf42681c..a89b45d0 100644 --- a/applications/plugins/nfc_magic/application.fam +++ b/applications/plugins/nfc_magic/application.fam @@ -11,7 +11,7 @@ App( stack_size=4 * 1024, order=30, fap_icon="../../../assets/icons/Archive/125_10px.png", - fap_category="Tools", + fap_category="NFC", fap_private_libs=[ Lib( name="magic", diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam index f2da6a9f..c5087b80 100644 --- a/applications/plugins/picopass/application.fam +++ b/applications/plugins/picopass/application.fam @@ -1,6 +1,6 @@ App( appid="picopass", - name="PicoPass Reader", + name="PicoPass", apptype=FlipperAppType.EXTERNAL, targets=["f7"], entry_point="picopass_app", @@ -11,7 +11,7 @@ App( stack_size=4 * 1024, order=30, fap_icon="125_10px.png", - fap_category="Tools", + fap_category="NFC", fap_libs=["mbedtls"], fap_private_libs=[ Lib( diff --git a/applications/plugins/picopass/picopass.c b/applications/plugins/picopass/picopass.c index 217f963d..96ea82c3 100644 --- a/applications/plugins/picopass/picopass.c +++ b/applications/plugins/picopass/picopass.c @@ -171,6 +171,16 @@ void picopass_show_loading_popup(void* context, bool show) { } } +bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size) { + bool result = size > 0; + while(size > 0) { + result &= (*data == pattern); + data++; + size--; + } + return result; +} + int32_t picopass_app(void* p) { UNUSED(p); Picopass* picopass = picopass_alloc(); diff --git a/applications/plugins/picopass/picopass_device.c b/applications/plugins/picopass/picopass_device.c index fd8ddbfb..e3940698 100644 --- a/applications/plugins/picopass/picopass_device.c +++ b/applications/plugins/picopass/picopass_device.c @@ -368,7 +368,7 @@ ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* r record->CardNumber = (bot >> 1) & 0xFFFF; record->FacilityCode = (bot >> 17) & 0xFF; - FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); + FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber); record->valid = true; } else { record->CardNumber = 0; diff --git a/applications/plugins/picopass/picopass_device.h b/applications/plugins/picopass/picopass_device.h index 150b095a..99f1ceea 100644 --- a/applications/plugins/picopass/picopass_device.h +++ b/applications/plugins/picopass/picopass_device.h @@ -22,6 +22,7 @@ #define PICOPASS_KD_BLOCK_INDEX 3 #define PICOPASS_KC_BLOCK_INDEX 4 #define PICOPASS_AIA_BLOCK_INDEX 5 +#define PICOPASS_PACS_CFG_BLOCK_INDEX 6 #define PICOPASS_APP_FOLDER ANY_PATH("picopass") #define PICOPASS_APP_EXTENSION ".picopass" diff --git a/applications/plugins/picopass/picopass_i.h b/applications/plugins/picopass/picopass_i.h index 469a672b..54533e82 100644 --- a/applications/plugins/picopass/picopass_i.h +++ b/applications/plugins/picopass/picopass_i.h @@ -81,3 +81,15 @@ void picopass_blink_start(Picopass* picopass); void picopass_blink_stop(Picopass* picopass); void picopass_show_loading_popup(void* context, bool show); + +/** Check if memory is set to pattern + * + * @warning zero size will return false + * + * @param[in] data Pointer to the byte array + * @param[in] pattern The pattern + * @param[in] size The byte array size + * + * @return True if memory is set to pattern, false otherwise + */ +bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size); diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index 1ee814aa..6d904478 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/applications/plugins/picopass/picopass_worker.c @@ -5,7 +5,8 @@ #define TAG "PicopassWorker" const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; -const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; +const uint8_t picopass_factory_credit_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; +const uint8_t picopass_factory_debit_key[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87}; static void picopass_worker_enable_field() { furi_hal_nfc_ll_txrx_on(); @@ -197,6 +198,28 @@ static ReturnCode picopass_auth_standard(uint8_t* csn, uint8_t* div_key) { return rfalPicoPassPollerCheck(mac, &chkRes); } +static ReturnCode picopass_auth_factory(uint8_t* csn, uint8_t* div_key) { + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err; + + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + loclass_diversifyKey(csn, picopass_factory_debit_key, div_key); + loclass_opt_doReaderMAC(ccnr, div_key, mac); + + return rfalPicoPassPollerCheck(mac, &chkRes); +} + static ReturnCode picopass_auth_dict( uint8_t* csn, PicopassPacs* pacs, @@ -264,14 +287,23 @@ static ReturnCode picopass_auth_dict( ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { ReturnCode err; - FURI_LOG_E(TAG, "Trying standard legacy key"); + FURI_LOG_I(TAG, "Trying standard legacy key"); err = picopass_auth_standard( AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data); if(err == ERR_NONE) { + memcpy(pacs->key, picopass_iclass_key, PICOPASS_BLOCK_LEN); return ERR_NONE; } - FURI_LOG_E(TAG, "Starting user dictionary attack"); + FURI_LOG_I(TAG, "Trying factory default key"); + err = picopass_auth_factory( + AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data); + if(err == ERR_NONE) { + memcpy(pacs->key, picopass_factory_debit_key, PICOPASS_BLOCK_LEN); + return ERR_NONE; + } + + FURI_LOG_I(TAG, "Starting user dictionary attack"); err = picopass_auth_dict( AA1[PICOPASS_CSN_BLOCK_INDEX].data, pacs, @@ -281,7 +313,7 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { return ERR_NONE; } - FURI_LOG_E(TAG, "Starting in-built dictionary attack"); + FURI_LOG_I(TAG, "Starting system dictionary attack"); err = picopass_auth_dict( AA1[PICOPASS_CSN_BLOCK_INDEX].data, pacs, @@ -406,6 +438,84 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) { return ERR_NONE; } +ReturnCode picopass_write_block(PicopassPacs* pacs, uint8_t blockNo, uint8_t* newBlock) { + rfalPicoPassIdentifyRes idRes; + rfalPicoPassSelectRes selRes; + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err; + + uint8_t div_key[8] = {0}; + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; + + err = rfalPicoPassPollerIdentify(&idRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); + return err; + } + + err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err); + return err; + } + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + loclass_diversifyKey(selRes.CSN, pacs->key, div_key); + loclass_opt_doReaderMAC(ccnr, div_key, mac); + + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + return err; + } + + FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", blockNo); + uint8_t data[9] = { + blockNo, + newBlock[0], + newBlock[1], + newBlock[2], + newBlock[3], + newBlock[4], + newBlock[5], + newBlock[6], + newBlock[7]}; + loclass_doMAC_N(data, sizeof(data), div_key, mac); + FURI_LOG_D( + TAG, + "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x", + blockNo, + data[1], + data[2], + data[3], + data[4], + data[5], + data[6], + data[7], + data[8], + mac[0], + mac[1], + mac[2], + mac[3]); + + err = rfalPicoPassPollerWriteBlock(data[0], data + 1, mac); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err); + return err; + } + + return ERR_NONE; +} + int32_t picopass_worker_task(void* context) { PicopassWorker* picopass_worker = context; @@ -414,6 +524,8 @@ int32_t picopass_worker_task(void* context) { picopass_worker_detect(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateWrite) { picopass_worker_write(picopass_worker); + } else if(picopass_worker->state == PicopassWorkerStateWriteStandardKey) { + picopass_worker_write_standard_key(picopass_worker); } picopass_worker_disable_field(ERR_NONE); @@ -448,7 +560,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { } // Thank you proxmark! - pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8); pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); if(pacs->se_enabled) { FURI_LOG_D(TAG, "SE enabled"); @@ -520,3 +632,46 @@ void picopass_worker_write(PicopassWorker* picopass_worker) { furi_delay_ms(100); } } + +void picopass_worker_write_standard_key(PicopassWorker* picopass_worker) { + PicopassDeviceData* dev_data = picopass_worker->dev_data; + PicopassBlock* AA1 = dev_data->AA1; + PicopassPacs* pacs = &dev_data->pacs; + ReturnCode err; + PicopassWorkerEvent nextState = PicopassWorkerEventSuccess; + + uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; + uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; + uint8_t fuses = configBlock[7]; + uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data; + + uint8_t newKey[PICOPASS_BLOCK_LEN] = {0}; + loclass_diversifyKey(csn, picopass_iclass_key, newKey); + + if((fuses & 0x80) == 0x80) { + FURI_LOG_D(TAG, "Plain write for personalized mode key change"); + } else { + FURI_LOG_D(TAG, "XOR write for application mode key change"); + // XOR when in application mode + for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + newKey[i] ^= oldKey[i]; + } + } + + while(picopass_worker->state == PicopassWorkerStateWriteStandardKey) { + if(picopass_detect_card(1000) == ERR_NONE) { + err = picopass_write_block(pacs, PICOPASS_KD_BLOCK_INDEX, newKey); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_write_block error %d", err); + nextState = PicopassWorkerEventFail; + } + + // Notify caller and exit + if(picopass_worker->callback) { + picopass_worker->callback(nextState, picopass_worker->context); + } + break; + } + furi_delay_ms(100); + } +} diff --git a/applications/plugins/picopass/picopass_worker.h b/applications/plugins/picopass/picopass_worker.h index 29a890a1..775212c6 100644 --- a/applications/plugins/picopass/picopass_worker.h +++ b/applications/plugins/picopass/picopass_worker.h @@ -12,6 +12,7 @@ typedef enum { // Main worker states PicopassWorkerStateDetect, PicopassWorkerStateWrite, + PicopassWorkerStateWriteStandardKey, // Transition PicopassWorkerStateStop, } PicopassWorkerState; diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/plugins/picopass/picopass_worker_i.h index ded40e6c..cf55fbdf 100644 --- a/applications/plugins/picopass/picopass_worker_i.h +++ b/applications/plugins/picopass/picopass_worker_i.h @@ -31,3 +31,4 @@ int32_t picopass_worker_task(void* context); void picopass_worker_detect(PicopassWorker* picopass_worker); void picopass_worker_write(PicopassWorker* picopass_worker); +void picopass_worker_write_standard_key(PicopassWorker* picopass_worker); diff --git a/applications/plugins/picopass/scenes/picopass_scene_config.h b/applications/plugins/picopass/scenes/picopass_scene_config.h index 27d6bbcd..95700787 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_config.h +++ b/applications/plugins/picopass/scenes/picopass_scene_config.h @@ -11,3 +11,5 @@ ADD_SCENE(picopass, delete, Delete) ADD_SCENE(picopass, delete_success, DeleteSuccess) ADD_SCENE(picopass, write_card, WriteCard) ADD_SCENE(picopass, write_card_success, WriteCardSuccess) +ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) +ADD_SCENE(picopass, write_key, WriteKey) diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card.c b/applications/plugins/picopass/scenes/picopass_scene_read_card.c index 8188207a..90422a2e 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card.c @@ -1,6 +1,8 @@ #include "../picopass_i.h" #include +const uint8_t picopass_factory_key_check[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87}; + void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) { UNUSED(event); Picopass* picopass = context; @@ -34,7 +36,14 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == PicopassCustomEventWorkerExit) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + if(memcmp( + picopass->dev->dev_data.pacs.key, + picopass_factory_key_check, + PICOPASS_BLOCK_LEN) == 0) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess); + } else { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + } consumed = true; } } diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index d89a5d89..f078d460 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -15,6 +15,7 @@ void picopass_scene_read_card_success_widget_callback( void picopass_scene_read_card_success_on_enter(void* context) { Picopass* picopass = context; + FuriString* csn_str = furi_string_alloc_set("CSN:"); FuriString* credential_str = furi_string_alloc(); FuriString* wiegand_str = furi_string_alloc(); @@ -30,27 +31,31 @@ void picopass_scene_read_card_success_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - uint8_t csn[PICOPASS_BLOCK_LEN]; - memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); + uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(csn_str, "%02X ", csn[i]); } - // Neither of these are valid. Indicates the block was all 0x00 or all 0xff - if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); + bool empty = + picopass_is_memset(AA1[PICOPASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN); + + if(no_key) { furi_string_cat_printf(wiegand_str, "Read Failed"); if(pacs->se_enabled) { furi_string_cat_printf(credential_str, "SE enabled"); } + } else if(empty) { + furi_string_cat_printf(wiegand_str, "Empty"); + } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + // Neither of these are valid. Indicates the block was all 0x00 or all 0xff + furi_string_cat_printf(wiegand_str, "Invalid PACS"); - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_read_card_success_widget_callback, - picopass); - + if(pacs->se_enabled) { + furi_string_cat_printf(credential_str, "SE enabled"); + } } else { size_t bytesLength = 1 + pacs->record.bitLength / 8; furi_string_set(credential_str, ""); @@ -82,13 +87,6 @@ void picopass_scene_read_card_success_on_enter(void* context) { } } - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_read_card_success_widget_callback, - picopass); - widget_add_button_element( widget, GuiButtonTypeRight, @@ -97,6 +95,13 @@ void picopass_scene_read_card_success_on_enter(void* context) { picopass); } + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + picopass_scene_read_card_success_widget_callback, + picopass); + widget_add_string_element( widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); widget_add_string_element( diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c new file mode 100644 index 00000000..8e32d21f --- /dev/null +++ b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c @@ -0,0 +1,78 @@ +#include "../picopass_i.h" +#include + +void picopass_scene_read_factory_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Picopass* picopass = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(picopass->view_dispatcher, result); + } +} + +void picopass_scene_read_factory_success_on_enter(void* context) { + Picopass* picopass = context; + FuriString* title = furi_string_alloc_set("Factory Default"); + FuriString* subtitle = furi_string_alloc_set(""); + + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Send notification + notification_message(picopass->notifications, &sequence_success); + + // Setup view + Widget* widget = picopass->widget; + //PicopassPacs* pacs = &picopass->dev->dev_data.pacs; + PicopassBlock* AA1 = picopass->dev->dev_data.AA1; + + uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; + uint8_t fuses = configBlock[7]; + + if((fuses & 0x80) == 0x80) { + furi_string_cat_printf(subtitle, "Personalization mode"); + } else { + furi_string_cat_printf(subtitle, "Application mode"); + } + + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Write Standard iClass Key", + picopass_scene_read_factory_success_widget_callback, + picopass); + + widget_add_string_element( + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(title)); + widget_add_string_element( + widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(subtitle)); + + furi_string_free(title); + furi_string_free(subtitle); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); +} + +bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } else if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); + consumed = true; + } + } + return consumed; +} + +void picopass_scene_read_factory_success_on_exit(void* context) { + Picopass* picopass = context; + + // Clear view + widget_reset(picopass->widget); +} diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c index 108e7d1c..4bbca816 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c @@ -16,6 +16,7 @@ void picopass_scene_write_card_success_widget_callback( void picopass_scene_write_card_success_on_enter(void* context) { Picopass* picopass = context; Widget* widget = picopass->widget; + FuriString* str = furi_string_alloc_set("Write Success!"); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); @@ -29,6 +30,18 @@ void picopass_scene_write_card_success_on_enter(void* context) { picopass_scene_write_card_success_widget_callback, picopass); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Menu", + picopass_scene_write_card_success_widget_callback, + picopass); + + widget_add_string_element( + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(str)); + + furi_string_free(str); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); } diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_key.c b/applications/plugins/picopass/scenes/picopass_scene_write_key.c new file mode 100644 index 00000000..83d594ca --- /dev/null +++ b/applications/plugins/picopass/scenes/picopass_scene_write_key.c @@ -0,0 +1,53 @@ +#include "../picopass_i.h" +#include + +void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context) { + UNUSED(event); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); +} + +void picopass_scene_write_key_on_enter(void* context) { + Picopass* picopass = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + // Setup view + Popup* popup = picopass->popup; + popup_set_header(popup, "Writing\niClass\nkey", 68, 30, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + // Start worker + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); + picopass_worker_start( + picopass->worker, + PicopassWorkerStateWriteStandardKey, + &picopass->dev->dev_data, + picopass_write_key_worker_callback, + picopass); + + picopass_blink_start(picopass); +} + +bool picopass_scene_write_key_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassCustomEventWorkerExit) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess); + consumed = true; + } + } + return consumed; +} + +void picopass_scene_write_key_on_exit(void* context) { + Picopass* picopass = context; + + // Stop worker + picopass_worker_stop(picopass->worker); + // Clear view + popup_reset(picopass->popup); + + picopass_blink_stop(picopass); +} diff --git a/applications/plugins/signal_generator/application.fam b/applications/plugins/signal_generator/application.fam index de915733..60f8deff 100644 --- a/applications/plugins/signal_generator/application.fam +++ b/applications/plugins/signal_generator/application.fam @@ -8,6 +8,6 @@ App( stack_size=1 * 1024, order=50, fap_icon="signal_gen_10px.png", - fap_category="Tools", + fap_category="GPIO", fap_icon_assets="icons", ) diff --git a/applications/plugins/spi_mem_manager/application.fam b/applications/plugins/spi_mem_manager/application.fam index 09d80187..c1b10bfe 100644 --- a/applications/plugins/spi_mem_manager/application.fam +++ b/applications/plugins/spi_mem_manager/application.fam @@ -7,7 +7,7 @@ App( stack_size=1 * 2048, order=30, fap_icon="images/Dip8_10px.png", - fap_category="Tools", + fap_category="GPIO", fap_icon_assets="images", fap_private_libs=[ Lib( diff --git a/applications/plugins/weather_station/application.fam b/applications/plugins/weather_station/application.fam index 769b6dd2..935f9257 100644 --- a/applications/plugins/weather_station/application.fam +++ b/applications/plugins/weather_station/application.fam @@ -9,6 +9,6 @@ App( stack_size=4 * 1024, order=50, fap_icon="weather_station_10px.png", - fap_category="Tools", + fap_category="Sub-GHz", fap_icon_assets="images", ) diff --git a/applications/plugins/weather_station/scenes/weather_station_receiver.c b/applications/plugins/weather_station/scenes/weather_station_receiver.c index 670c8c38..e7681043 100644 --- a/applications/plugins/weather_station/scenes/weather_station_receiver.c +++ b/applications/plugins/weather_station/scenes/weather_station_receiver.c @@ -195,6 +195,10 @@ bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent ev ws_hopper_update(app); weather_station_scene_receiver_update_statusbar(app); } + // Get current RSSI + float rssi = furi_hal_subghz_get_rssi(); + ws_view_receiver_set_rssi(app->ws_receiver, rssi); + if(app->txrx->txrx_state == WSTxRxStateRx) { notification_message(app->notifications, &sequence_blink_cyan_10); } diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c index de5d7b1a..f8e2e328 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.c +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -12,6 +12,7 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f typedef struct { FuriString* item_str; uint8_t type; @@ -59,8 +60,24 @@ typedef struct { uint16_t list_offset; uint16_t history_item; WSReceiverBarShow bar_show; + uint8_t u_rssi; } WSReceiverModel; +void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + WSReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + } + }, + true); +} + void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock lock) { furi_assert(ws_receiver); ws_receiver->lock_count = 0; @@ -164,13 +181,22 @@ static void ws_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrol canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); } +static void ws_view_rssi_draw(Canvas* canvas, WSReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 50); + canvas_draw_dot(canvas, 47 + i, 51); + canvas_draw_dot(canvas, 46 + i, 52); + } + } +} + void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_left(canvas, "Config"); - canvas_draw_line(canvas, 46, 51, 125, 51); bool scrollbar = model->history_item > 4; FuriString* str_buff; @@ -203,10 +229,12 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); } + // Draw RSSI + ws_view_rssi_draw(canvas, model); + switch(model->bar_show) { case WSReceiverBarShowLock: canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); diff --git a/applications/plugins/weather_station/views/weather_station_receiver.h b/applications/plugins/weather_station/views/weather_station_receiver.h index 30c6516d..f81aa1f5 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.h +++ b/applications/plugins/weather_station/views/weather_station_receiver.h @@ -8,6 +8,8 @@ typedef struct WSReceiver WSReceiver; typedef void (*WSReceiverCallback)(WSCustomEvent event, void* context); +void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi); + void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock keyboard); void ws_view_receiver_set_callback( diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 9e578269..16b60231 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -373,7 +373,7 @@ int32_t bt_srv(void* p) { Bt* bt = bt_alloc(); if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - FURI_LOG_W(TAG, "Skipped BT init: device in special startup mode"); + FURI_LOG_W(TAG, "Skipping start in special boot mode"); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); furi_record_create(RECORD_BT, bt); return 0; diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index 384d1780..b68505c5 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -461,7 +461,7 @@ int32_t cli_srv(void* p) { if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { cli_session_open(cli, &cli_vcp); } else { - FURI_LOG_W(TAG, "Skipped CLI session open: device in special startup mode"); + FURI_LOG_W(TAG, "Skipping start in special boot mode"); } while(1) { diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 4414d365..ca9d8b98 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -12,26 +12,48 @@ // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" -void cli_command_device_info_callback(const char* key, const char* value, bool last, void* context) { - UNUSED(context); +void cli_command_info_callback(const char* key, const char* value, bool last, void* context) { UNUSED(last); + UNUSED(context); printf("%-30s: %s\r\n", key, value); } -/* - * Device Info Command +/** Info Command + * * This command is intended to be used by humans + * + * Arguments: + * - device - print device info + * - power - print power info + * - power_debug - print power debug info + * + * @param cli The cli instance + * @param args The arguments + * @param context The context */ -void cli_command_device_info(Cli* cli, FuriString* args, void* context) { +void cli_command_info(Cli* cli, FuriString* args, void* context) { UNUSED(cli); - UNUSED(args); - furi_hal_info_get(cli_command_device_info_callback, '_', context); + + if(context) { + furi_hal_info_get(cli_command_info_callback, '_', NULL); + return; + } + + if(!furi_string_cmp(args, "device")) { + furi_hal_info_get(cli_command_info_callback, '.', NULL); + } else if(!furi_string_cmp(args, "power")) { + furi_hal_power_info_get(cli_command_info_callback, '.', NULL); + } else if(!furi_string_cmp(args, "power_debug")) { + furi_hal_power_debug_get(cli_command_info_callback, NULL); + } else { + cli_print_usage("info", "", furi_string_get_cstr(args)); + } } void cli_command_help(Cli* cli, FuriString* args, void* context) { UNUSED(args); UNUSED(context); - printf("Commands we have:"); + printf("Commands available:"); // Command count const size_t commands_count = CliCommandTree_size(cli->commands); @@ -61,9 +83,9 @@ void cli_command_help(Cli* cli, FuriString* args, void* context) { if(furi_string_size(args) > 0) { cli_nl(); - printf("Also I have no clue what '"); + printf("`"); printf("%s", furi_string_get_cstr(args)); - printf("' is."); + printf("` command not found"); } } @@ -410,8 +432,9 @@ void cli_command_i2c(Cli* cli, FuriString* args, void* context) { } void cli_commands_init(Cli* cli) { - cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_device_info, NULL); - cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_device_info, NULL); + cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true); + cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL); + cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true); cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL); cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL); diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 848f5cb6..f8716e6c 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,6 +17,8 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" +#define TAG "Desktop" + static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -321,6 +323,12 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; + } + Desktop* desktop = desktop_alloc(); bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index 41eeef3b..dd8b7105 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -154,6 +154,12 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) { int32_t dolphin_srv(void* p) { UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; + } + Dolphin* dolphin = dolphin_alloc(); furi_record_create(RECORD_DOLPHIN, dolphin); diff --git a/applications/services/power/power_cli.c b/applications/services/power/power_cli.c index f4a10f0a..021ce355 100644 --- a/applications/services/power/power_cli.c +++ b/applications/services/power/power_cli.c @@ -26,24 +26,6 @@ void power_cli_reboot2dfu(Cli* cli, FuriString* args) { power_reboot(PowerBootModeDfu); } -static void power_cli_callback(const char* key, const char* value, bool last, void* context) { - UNUSED(last); - UNUSED(context); - printf("%-24s: %s\r\n", key, value); -} - -void power_cli_info(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - furi_hal_power_info_get(power_cli_callback, '_', NULL); -} - -void power_cli_debug(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - furi_hal_power_debug_get(power_cli_callback, NULL); -} - void power_cli_5v(Cli* cli, FuriString* args) { UNUSED(cli); if(!furi_string_cmp(args, "0")) { @@ -74,8 +56,6 @@ static void power_cli_command_print_usage() { printf("\toff\t - shutdown power\r\n"); printf("\treboot\t - reboot\r\n"); printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); - printf("\tinfo\t - show power info\r\n"); - printf("\tdebug\t - show debug information\r\n"); printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\t3v3 <0 or 1>\t - enable or disable 3v3 ext\r\n"); @@ -108,16 +88,6 @@ void power_cli(Cli* cli, FuriString* args, void* context) { break; } - if(furi_string_cmp_str(cmd, "info") == 0) { - power_cli_info(cli, args); - break; - } - - if(furi_string_cmp_str(cmd, "debug") == 0) { - power_cli_debug(cli, args); - break; - } - if(furi_string_cmp_str(cmd, "5v") == 0) { power_cli_5v(cli, args); break; diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 5df611a7..56dbd0f8 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -4,6 +4,7 @@ #include #define POWER_OFF_TIMEOUT 90 +#define TAG "Power" void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); @@ -12,8 +13,8 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->info.gauge_is_ok) { canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4); - if(power->info.voltage_battery_charging < 4.2) { - // Battery charging voltage is modified, indicate with cross pattern + if(power->info.voltage_battery_charge_limit < 4.2) { + // Battery charge voltage limit is modified, indicate with cross pattern canvas_invert_color(canvas); uint8_t battery_bar_width = (power->info.charge + 4) / 5; bool cross_odd = false; @@ -146,7 +147,7 @@ static bool power_update_info(Power* power) { info.capacity_full = furi_hal_power_get_battery_full_capacity(); info.current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger); info.current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); - info.voltage_battery_charging = furi_hal_power_get_battery_charging_voltage(); + info.voltage_battery_charge_limit = furi_hal_power_get_battery_charge_voltage_limit(); info.voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger); info.voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge); info.voltage_vbus = furi_hal_power_get_usb_voltage(); @@ -217,6 +218,12 @@ static void power_check_battery_level_change(Power* power) { int32_t power_srv(void* p) { UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; + } + Power* power = power_alloc(); power_update_info(power); furi_record_create(RECORD_POWER, power); diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 8b9019c4..c7f5d7e3 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -41,7 +41,7 @@ typedef struct { float current_charger; float current_gauge; - float voltage_battery_charging; + float voltage_battery_charge_limit; float voltage_charger; float voltage_gauge; float voltage_vbus; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c index 5fa38df7..5181c93f 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c @@ -7,7 +7,7 @@ static void power_settings_scene_battery_info_update_model(PowerSettingsApp* app .gauge_voltage = app->info.voltage_gauge, .gauge_current = app->info.current_gauge, .gauge_temperature = app->info.temperature_gauge, - .charging_voltage = app->info.voltage_battery_charging, + .charge_voltage_limit = app->info.voltage_battery_charge_limit, .charge = app->info.charge, .health = app->info.health, }; diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index d29769d2..7394fd3c 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -69,7 +69,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); } else if(drain_current != 0) { snprintf(header, 20, "..."); - } else if(data->charging_voltage < 4.2) { + } else if(data->charge_voltage_limit < 4.2) { // Non-default battery charging limit, mention it snprintf(emote, sizeof(emote), "Charged!"); snprintf(header, sizeof(header), "Limited to"); @@ -77,8 +77,8 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { value, sizeof(value), "%lu.%luV", - (uint32_t)(data->charging_voltage), - (uint32_t)(data->charging_voltage * 10) % 10); + (uint32_t)(data->charge_voltage_limit), + (uint32_t)(data->charge_voltage_limit * 10) % 10); } else { snprintf(header, sizeof(header), "Charged!"); } diff --git a/applications/settings/power_settings_app/views/battery_info.h b/applications/settings/power_settings_app/views/battery_info.h index 7bfacf69..e52d1844 100644 --- a/applications/settings/power_settings_app/views/battery_info.h +++ b/applications/settings/power_settings_app/views/battery_info.h @@ -9,7 +9,7 @@ typedef struct { float gauge_voltage; float gauge_current; float gauge_temperature; - float charging_voltage; + float charge_voltage_limit; uint8_t charge; uint8_t health; } BatteryInfoModel; diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c index 71a3df78..8359c00b 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -103,6 +103,9 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { break; furi_string_cat_printf(app->text_string, "R %luK", bench_r_speed[i]); + + storage_common_remove(app->fs_api, BENCH_FILE); + dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); } diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index ed53c353..f2c33c2e 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -97,7 +97,16 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ path_concat( STORAGE_EXT_PATH_PREFIX, furi_string_get_cstr(entry_ptr->name), file_path); FURI_LOG_D(TAG, "Removing %s", furi_string_get_cstr(file_path)); - storage_simply_remove(update_task->storage, furi_string_get_cstr(file_path)); + + FS_Error result = + storage_common_remove(update_task->storage, furi_string_get_cstr(file_path)); + if(result != FSE_OK && result != FSE_EXIST) { + FURI_LOG_E( + TAG, + "%s remove failed, cause %s", + furi_string_get_cstr(file_path), + storage_error_get_desc(result)); + } furi_string_free(file_path); } else if(entry_ptr->type == ResourceManifestEntryTypeDirectory) { n_dir_entries++; @@ -116,7 +125,6 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ n_dir_entries); FuriString* folder_path = furi_string_alloc(); - File* folder_file = storage_file_alloc(update_task->storage); do { path_concat( @@ -125,24 +133,17 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ folder_path); FURI_LOG_D(TAG, "Removing folder %s", furi_string_get_cstr(folder_path)); - if(!storage_dir_open(folder_file, furi_string_get_cstr(folder_path))) { - FURI_LOG_W( + FS_Error result = storage_common_remove( + update_task->storage, furi_string_get_cstr(folder_path)); + if(result != FSE_OK && result != FSE_EXIST) { + FURI_LOG_E( TAG, - "%s can't be opened, skipping", - furi_string_get_cstr(folder_path)); - break; + "%s remove failed, cause %s", + furi_string_get_cstr(folder_path), + storage_error_get_desc(result)); } - - if(storage_dir_read(folder_file, NULL, NULL, 0)) { - FURI_LOG_I( - TAG, "%s is not empty, skipping", furi_string_get_cstr(folder_path)); - break; - } - - storage_simply_remove(update_task->storage, furi_string_get_cstr(folder_path)); } while(false); - storage_file_free(folder_file); furi_string_free(folder_path); } } diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png deleted file mode 100644 index 909cc330..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png deleted file mode 100644 index fdc1805d..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png deleted file mode 100644 index 3a43c6b8..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png deleted file mode 100644 index 20ea12eb..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png deleted file mode 100644 index a05abd80..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png deleted file mode 100644 index fc7e0364..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png deleted file mode 100644 index 06f08ae6..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png deleted file mode 100644 index bcc7f28a..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png deleted file mode 100644 index 7d945dd0..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png deleted file mode 100644 index c0b1a4fe..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png deleted file mode 100644 index 19c8181d..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png deleted file mode 100644 index c0b31695..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png deleted file mode 100644 index 929a207e..00000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt deleted file mode 100644 index a2c73339..00000000 --- a/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 10 -Active frames: 18 -Frames order: 0 1 2 1 0 1 2 1 0 1 2 3 4 5 6 5 4 7 2 8 9 10 11 10 9 10 11 12 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 11 -Y: 19 -Text: HAPPY\nHOLIDAYS! -AlignH: Right -AlignV: Center -StartFrame: 22 -EndFrame: 27 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index df84ed7b..46abcd96 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -36,13 +36,6 @@ Min level: 1 Max level: 1 Weight: 3 -Name: L1_Happy_holidays_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 3 -Weight: 4 - Name: L1_Read_books_128x64 Min butthurt: 0 Max butthurt: 8 diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index bcf035df..825d1bc3 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -285,3 +285,46 @@ type: parsed protocol: NECext address: 10 E7 00 00 command: 41 BE 00 00 +# +# Model: Grundig CMS 5000 +name: Power +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 10 EF 00 00 +# Also Pause +name: Play +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 02 FD 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 0D F2 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 17 E8 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 13 EC 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 11 EE 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 0C F3 00 00 diff --git a/assets/slideshow/first_start/frame_02.png b/assets/slideshow/first_start/frame_02.png index dc1080ab..adff6af6 100644 Binary files a/assets/slideshow/first_start/frame_02.png and b/assets/slideshow/first_start/frame_02.png differ diff --git a/documentation/fbt.md b/documentation/fbt.md index 5166d0ab..65c3ee68 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -3,17 +3,27 @@ FBT is the entry point for firmware-related commands and utilities. It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system. -## Requirements +## Environment -Install Python packages required by assets build scripts: `pip3 install -r scripts/requirements.txt` +To use `fbt`, you only need `git` installed in your system. -## NB +`fbt` by default downloads and unpacks a pre-built toolchain, and then modifies environment variables for itself to use it. It does not contaminate your global system's path with the toolchain. + > However, if you wish to use tools supplied with the toolchain outside `fbt`, you can open an *fbt shell*, with properly configured environment. + > - On Windows, simply run `scripts/toochain/fbtenv.cmd`. + > - On Linux & MacOS, run `source scripts/toochain/fbtenv.sh` in a new shell. + + If your system is not supported by pre-built toolchain variants or you want to use custom versions of dependencies, you can `set FBT_NOENV=1`. `fbt` will skip toolchain & environment configuration and will expect all tools to be available on your system's `PATH`. *(this option is not available on Windows)* + + If `FBT_TOOLCHAIN_PATH` variable is set, `fbt` will use that directory to unpack toolchain into. By default, it downloads toolchain into `toolchain` subdirectory repo's root. -- `fbt` constructs all referenced environments and their targets' dependency trees on startup. So, to keep startup time as low as possible, we're hiding the construction of certain targets behind command-line options. -- `fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in the environment: +If you want to enable extra debug output for `fbt` and toolchain management scripts, you can `set FBT_VERBOSE=1`. + +`fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in the environment: - On Windows, it's `set "FBT_NO_SYNC=1"` in the shell you're running `fbt` from - On \*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...` -- `fbt` builds updater & firmware in separate subdirectories in `build`, and their names depend on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, the latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder (used for code completion support in IDE). + + > There are more variables controlling basic `fbt` behavior. See `fbt` & `fbtenv` scripts' sources for details. + ## Invoking FBT @@ -23,6 +33,12 @@ To build with FBT, call it and specify configuration options & targets to build. To run cleanup (think of `make clean`) for specified targets, add the `-c` option. +## Build directories + +`fbt` builds updater & firmware in separate subdirectories in `build`, and their names depend on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, the latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder (it is used for code completion support in IDEs). + +`build/latest` symlink & compilation database are only updated upon *firmware build targets* - that is, when you're re-building the firmware itself. Running other tasks, like firmware flashing or building update bundles *for a different debug/release configuration or hardware target*, does not update `built/latest` dir to point to that configuration. + ## VSCode integration `fbt` includes basic development environment configuration for VS Code. Run `./fbt vscode_dist` to deploy it. That will copy the initial environment configuration to the `.vscode` folder. After that, you can use that configuration by starting VS Code and choosing the firmware root folder in the "File > Open Folder" menu. diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index 2ef1d313..94dee594 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -75,6 +75,14 @@ Can be combined with a special key command or a single character. | ------- | ----------- | ----------------- | | STRING | Text string | Print text string | +## String delay + +Delay between keypresses. +|Command|Parameters|Notes| +|-|-|-| +|STRING_DELAY|Delay value in ms|Applied once to next appearing string| +|STRINGDELAY|Delay value in ms|Same as STRING_DELAY| + ## Repeat | Command | Parameters | Notes | diff --git a/fbt b/fbt index e576f37a..f80e802b 100755 --- a/fbt +++ b/fbt @@ -6,16 +6,21 @@ set -eu; # private variables SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"; -SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; -SCONS_EP="python3 -m SCons" +SCONS_DEFAULT_FLAGS="--warn=target-not-built"; +SCONS_EP="python3 -m SCons"; # public variables FBT_NOENV="${FBT_NOENV:-""}"; FBT_NO_SYNC="${FBT_NO_SYNC:-""}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; +FBT_VERBOSE="${FBT_VERBOSE:-""}"; if [ -z "$FBT_NOENV" ]; then - . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; + FBT_VERBOSE="$FBT_VERBOSE" . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; +fi + +if [ -z "$FBT_VERBOSE" ]; then + SCONS_DEFAULT_FLAGS="$SCONS_DEFAULT_FLAGS -Q"; fi if [ -z "$FBT_NO_SYNC" ]; then diff --git a/fbt.cmd b/fbt.cmd index 197f2359..92c73486 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -12,5 +12,10 @@ if [%FBT_NO_SYNC%] == [] ( ) ) -set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" +set "SCONS_DEFAULT_FLAGS=--warn=target-not-built" + +if not defined FBT_VERBOSE ( + set "SCONS_DEFAULT_FLAGS=%SCONS_DEFAULT_FLAGS% -Q" +) + %SCONS_EP% %SCONS_DEFAULT_FLAGS% %* diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 462fbf73..549d3812 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,14.0,, +Version,+,15.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -979,7 +979,7 @@ Function,+,furi_hal_power_enable_external_3_3v,void, Function,+,furi_hal_power_enable_otg,void, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, -Function,+,furi_hal_power_get_battery_charging_voltage,float, +Function,+,furi_hal_power_get_battery_charge_voltage_limit,float, Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, @@ -998,7 +998,7 @@ Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, -Function,+,furi_hal_power_set_battery_charging_voltage,void,float +Function,+,furi_hal_power_set_battery_charge_voltage_limit,void,float Function,+,furi_hal_power_shutdown,void, Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 33c443ae..8152095d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,14.0,, +Version,+,15.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1171,6 +1171,7 @@ Function,+,furi_hal_mpu_protect_disable,void,FuriHalMpuRegion Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_nfc_activate_nfca,_Bool,"uint32_t, uint32_t*" +Function,-,furi_hal_nfc_deinit,void, Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t" Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, FuriHalNfcEmulateCallback, void*, uint32_t" Function,+,furi_hal_nfc_exit_sleep,void, @@ -1210,7 +1211,7 @@ Function,+,furi_hal_power_enable_external_3_3v,void, Function,+,furi_hal_power_enable_otg,void, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, -Function,+,furi_hal_power_get_battery_charging_voltage,float, +Function,+,furi_hal_power_get_battery_charge_voltage_limit,float, Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, @@ -1229,7 +1230,7 @@ Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, -Function,+,furi_hal_power_set_battery_charging_voltage,void,float +Function,+,furi_hal_power_set_battery_charge_voltage_limit,void,float Function,+,furi_hal_power_shutdown,void, Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c index 93b837e8..68903acf 100644 --- a/firmware/targets/f7/fatfs/sd_spi_io.c +++ b/firmware/targets/f7/fatfs/sd_spi_io.c @@ -17,7 +17,6 @@ #define SD_DUMMY_BYTE 0xFF #define SD_ANSWER_RETRY_COUNT 8 #define SD_IDLE_RETRY_COUNT 100 -#define SD_BLOCK_SIZE 512 #define FLAG_SET(x, y) (((x) & (y)) == (y)) @@ -598,23 +597,6 @@ static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { return ret; } -static inline bool sd_cache_get(uint32_t address, uint32_t* data) { - uint8_t* cached_data = sector_cache_get(address); - if(cached_data) { - memcpy(data, cached_data, SD_BLOCK_SIZE); - return true; - } - return false; -} - -static inline void sd_cache_put(uint32_t address, uint32_t* data) { - sector_cache_put(address, (uint8_t*)data); -} - -static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { - sector_cache_invalidate_range(start_sector, end_sector); -} - static SdSpiStatus sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { uint32_t block_address = address; @@ -833,30 +815,12 @@ SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) { SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { - SdSpiStatus status = SdSpiStatusError; - - bool single_sector_read = (blocks == 1); - - if(single_sector_read) { - if(sd_cache_get(address, data)) { - return SdSpiStatusOK; - } - - status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); - - if(status == SdSpiStatusOK) { - sd_cache_put(address, data); - } - } else { - status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); - } - + SdSpiStatus status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); return status; } SdSpiStatus sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { - sd_cache_invalidate_range(address, address + blocks); SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms); return status; } diff --git a/firmware/targets/f7/fatfs/sd_spi_io.h b/firmware/targets/f7/fatfs/sd_spi_io.h index 8850eceb..954c78c4 100644 --- a/firmware/targets/f7/fatfs/sd_spi_io.h +++ b/firmware/targets/f7/fatfs/sd_spi_io.h @@ -5,6 +5,7 @@ #define __IO volatile #define SD_TIMEOUT_MS (1000) +#define SD_BLOCK_SIZE 512 typedef enum { SdSpiStatusOK, diff --git a/firmware/targets/f7/fatfs/user_diskio.c b/firmware/targets/f7/fatfs/user_diskio.c index 16ac78e4..d7be09c5 100644 --- a/firmware/targets/f7/fatfs/user_diskio.c +++ b/firmware/targets/f7/fatfs/user_diskio.c @@ -36,6 +36,7 @@ /* Includes ------------------------------------------------------------------*/ #include "user_diskio.h" #include +#include "sector_cache.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ @@ -79,6 +80,26 @@ Diskio_drvTypeDef USER_Driver = { }; /* Private functions ---------------------------------------------------------*/ +static inline bool sd_cache_get(uint32_t address, uint32_t* data) { + uint8_t* cached_data = sector_cache_get(address); + if(cached_data) { + memcpy(data, cached_data, SD_BLOCK_SIZE); + return true; + } + return false; +} + +static inline void sd_cache_put(uint32_t address, uint32_t* data) { + sector_cache_put(address, (uint8_t*)data); +} + +static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { + sector_cache_invalidate_range(start_sector, end_sector); +} + +static inline void sd_cache_invalidate_all() { + sector_cache_init(); +} /** * @brief Initializes a Drive @@ -125,6 +146,14 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { UNUSED(pdrv); DRESULT res = RES_ERROR; + bool single_sector = count == 1; + + if(single_sector) { + if(sd_cache_get(sector, (uint32_t*)buff)) { + return RES_OK; + } + } + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; @@ -145,6 +174,10 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { furi_hal_sd_spi_handle = NULL; furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + if(single_sector && res == RES_OK) { + sd_cache_put(sector, (uint32_t*)buff); + } + return res; /* USER CODE END READ */ } @@ -164,6 +197,8 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { UNUSED(pdrv); DRESULT res = RES_ERROR; + sd_cache_invalidate_range(sector, sector + count); + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; @@ -175,6 +210,8 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { res = RES_OK; while(sd_get_card_state() != SdSpiStatusOK) { if(furi_hal_cortex_timer_is_expired(timer)) { + sd_cache_invalidate_all(); + res = RES_ERROR; break; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index ce81fd05..8910d887 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -24,13 +24,29 @@ FuriEventFlag* event = NULL; #define FURI_HAL_NFC_UID_INCOMPLETE (0x04) void furi_hal_nfc_init() { + furi_assert(!event); + event = furi_event_flag_alloc(); + ReturnCode ret = rfalNfcInitialize(); if(ret == ERR_NONE) { furi_hal_nfc_start_sleep(); - event = furi_event_flag_alloc(); FURI_LOG_I(TAG, "Init OK"); } else { - FURI_LOG_W(TAG, "Initialization failed, RFAL returned: %d", ret); + FURI_LOG_W(TAG, "Init Failed, RFAL returned: %d", ret); + } +} + +void furi_hal_nfc_deinit() { + ReturnCode ret = rfalDeinitialize(); + if(ret == ERR_NONE) { + FURI_LOG_I(TAG, "Deinit OK"); + } else { + FURI_LOG_W(TAG, "Deinit Failed, RFAL returned: %d", ret); + } + + if(event) { + furi_event_flag_free(event); + event = NULL; } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h index d3f6de60..dc3f873f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.h @@ -101,6 +101,10 @@ typedef struct { */ void furi_hal_nfc_init(); +/** Deinit nfc + */ +void furi_hal_nfc_deinit(); + /** Check if nfc worker is busy * * @return true if busy diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 2d709620..dd7c34ae 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -341,14 +341,14 @@ bool furi_hal_power_is_otg_enabled() { return ret; } -float furi_hal_power_get_battery_charging_voltage() { +float furi_hal_power_get_battery_charge_voltage_limit() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); float ret = (float)bq25896_get_vreg_voltage(&furi_hal_i2c_handle_power) / 1000.0f; furi_hal_i2c_release(&furi_hal_i2c_handle_power); return ret; } -void furi_hal_power_set_battery_charging_voltage(float voltage) { +void furi_hal_power_set_battery_charge_voltage_limit(float voltage) { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); // Adding 0.0005 is necessary because 4.016f is 4.015999794000, which gets truncated bq25896_set_vreg_voltage(&furi_hal_i2c_handle_power, (uint16_t)(voltage * 1000.0f + 0.0005f)); @@ -486,7 +486,7 @@ void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) property_value_out(&property_context, NULL, 2, "format", "major", "2"); property_value_out(&property_context, NULL, 2, "format", "minor", "1"); } else { - property_value_out(&property_context, NULL, 3, "power", "info", "major", "1"); + property_value_out(&property_context, NULL, 3, "power", "info", "major", "2"); property_value_out(&property_context, NULL, 3, "power", "info", "minor", "1"); } @@ -505,8 +505,10 @@ void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) } property_value_out(&property_context, NULL, 2, "charge", "state", charge_state); - uint16_t charge_voltage = (uint16_t)(furi_hal_power_get_battery_charging_voltage() * 1000.f); - property_value_out(&property_context, "%u", 2, "charge", "voltage", charge_voltage); + uint16_t charge_voltage_limit = + (uint16_t)(furi_hal_power_get_battery_charge_voltage_limit() * 1000.f); + property_value_out( + &property_context, "%u", 3, "charge", "voltage", "limit", charge_voltage_limit); uint16_t voltage = (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f); property_value_out(&property_context, "%u", 2, "battery", "voltage", voltage); diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 39a11e99..462e20e4 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -121,21 +121,21 @@ void furi_hal_power_check_otg_status(); */ bool furi_hal_power_is_otg_enabled(); -/** Get battery charging voltage in V +/** Get battery charge voltage limit in V * * @return voltage in V */ -float furi_hal_power_get_battery_charging_voltage(); +float furi_hal_power_get_battery_charge_voltage_limit(); -/** Set battery charging voltage in V +/** Set battery charge voltage limit in V * - * Invalid values will be clamped to the nearest valid value. + * Invalid values will be clamped downward to the nearest valid value. * * @param voltage[in] voltage in V * * @return voltage in V */ -void furi_hal_power_set_battery_charging_voltage(float voltage); +void furi_hal_power_set_battery_charge_voltage_limit(float voltage); /** Get remaining battery battery capacity in mAh * diff --git a/furi/core/thread.c b/furi/core/thread.c index 9a112d9a..ea9f45e8 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -96,9 +96,9 @@ static void furi_thread_body(void* context) { furi_assert(thread->state == FuriThreadStateRunning); if(thread->is_service) { - FURI_LOG_E( + FURI_LOG_W( TAG, - "%s service thread exited. Thread memory cannot be reclaimed.", + "%s service thread TCB memory will not be reclaimed", thread->name ? thread->name : ""); } diff --git a/furi/flipper.c b/furi/flipper.c index 73899e58..d16a84a1 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "Flipper" @@ -29,10 +30,10 @@ static void flipper_print_version(const char* target, const Version* version) { void flipper_init() { flipper_print_version("Firmware", furi_hal_version_get_firmware_version()); - FURI_LOG_I(TAG, "starting services"); + FURI_LOG_I(TAG, "Boot mode %d, starting services", furi_hal_rtc_get_boot_mode()); for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { - FURI_LOG_I(TAG, "starting service %s", FLIPPER_SERVICES[i].name); + FURI_LOG_I(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); FuriThread* thread = furi_thread_alloc_ex( FLIPPER_SERVICES[i].name, @@ -44,7 +45,7 @@ void flipper_init() { furi_thread_start(thread); } - FURI_LOG_I(TAG, "services startup complete"); + FURI_LOG_I(TAG, "Startup complete"); } void vApplicationGetIdleTaskMemory( diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 7e3008d6..4c1d687c 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -140,19 +140,16 @@ uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) { void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) { if(vreg_voltage < 3840) { - // Minimum value is 3840 mV - bq25896_regs.r06.VREG = 0; - } else { - // Find the nearest voltage value (subtract offset, divide into sections) - // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV) - bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16); + // Minimum valid value is 3840 mV + vreg_voltage = 3840; + } else if(vreg_voltage > 4208) { + // Maximum safe value is 4208 mV + vreg_voltage = 4208; } - // Do not allow values above 23 (0x17, 4208mV) - // Exceeding 4.2v will overcharge the battery! - if(bq25896_regs.r06.VREG > 23) { - bq25896_regs.r06.VREG = 23; - } + // Find the nearest voltage value (subtract offset, divide into sections) + // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV) + bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16); // Apply changes furi_hal_i2c_write_reg_8( diff --git a/lib/drivers/bq25896.h b/lib/drivers/bq25896.h index c8a8526a..f3d1d0e0 100644 --- a/lib/drivers/bq25896.h +++ b/lib/drivers/bq25896.h @@ -36,10 +36,10 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle); /** Is otg enabled */ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle); -/** Get VREG (charging) voltage in mV */ +/** Get VREG (charging limit) voltage in mV */ uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle); -/** Set VREG (charging) voltage in mV +/** Set VREG (charging limit) voltage in mV * * Valid range: 3840mV - 4208mV, in steps of 16mV */ diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index a652e088..54bdbb24 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -56,6 +56,8 @@ void nfc_worker_start( while(furi_hal_nfc_is_busy()) { furi_delay_ms(10); } + furi_hal_nfc_deinit(); + furi_hal_nfc_init(); nfc_worker->callback = callback; nfc_worker->context = context; diff --git a/scripts/flipper/assets/dolphin.py b/scripts/flipper/assets/dolphin.py index 096d3162..cbd1320b 100644 --- a/scripts/flipper/assets/dolphin.py +++ b/scripts/flipper/assets/dolphin.py @@ -22,7 +22,6 @@ def _convert_image(source_filename: str): class DolphinBubbleAnimation: - FILE_TYPE = "Flipper Animation" FILE_VERSION = 1 @@ -243,7 +242,6 @@ class DolphinBubbleAnimation: class DolphinManifest: - FILE_TYPE = "Flipper Animation Manifest" FILE_VERSION = 1 diff --git a/scripts/flipper/assets/icon.py b/scripts/flipper/assets/icon.py index ed85b024..f0dae25b 100644 --- a/scripts/flipper/assets/icon.py +++ b/scripts/flipper/assets/icon.py @@ -105,7 +105,7 @@ def file2image(file): data_enc = bytearray([len(data_enc) & 0xFF, len(data_enc) >> 8]) + data_enc # Use encoded data only if its length less than original, including header - if len(data_enc) < len(data_bin) + 1: + if len(data_enc) + 2 < len(data_bin) + 1: data = b"\x01\x00" + data_enc else: data = b"\x00" + data_bin diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index 7047db2a..7c735167 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -1,5 +1,6 @@ import serial.tools.list_ports as list_ports + # Returns a valid port or None, if it cannot be found def resolve_port(logger, portname: str = "auto"): if portname != "auto": diff --git a/scripts/flipper/utils/templite.py b/scripts/flipper/utils/templite.py index 8af57d7f..2d958bd7 100644 --- a/scripts/flipper/utils/templite.py +++ b/scripts/flipper/utils/templite.py @@ -173,12 +173,14 @@ class Templite: """Renders the template according to the given namespace.""" stack = [] namespace["__file__"] = self.file + # add write method def write(*args): for value in args: stack.append(str(value)) namespace["write"] = write + # add include method def include(file): if not os.path.isabs(file): diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 9fbd8fd9..8587f6d0 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=19" +set "FLIPPER_TOOLCHAIN_VERSION=21" if ["%FBT_TOOLCHAIN_ROOT%"] == [""] ( set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" @@ -31,7 +31,13 @@ if not exist "%FBT_TOOLCHAIN_VERSION_FILE%" ( set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( + echo FBT: starting toolchain upgrade process.. powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" %flipper_toolchain_version% "%FBT_TOOLCHAIN_ROOT%" + set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" +) + +if defined FBT_VERBOSE ( + echo FBT: using toolchain version %REAL_TOOLCHAIN_VERSION% ) set "HOME=%USERPROFILE%" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index dd5484aa..8f05c23c 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,15 +5,16 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"19"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; +FBT_VERBOSE="${FBT_VERBOSE:-""}"; fbtenv_show_usage() { echo "Running this script manually is wrong, please source it"; echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh\n"; - echo "To restore your environment source fbtenv.sh with '--restore'." + echo "To restore your environment, source fbtenv.sh with '--restore'." echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh --restore\n"; } @@ -35,16 +36,26 @@ fbtenv_restore_env() PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/bin://g")"; PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/protobuf\/bin://g")"; PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openocd\/bin://g")"; + PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openssl\/bin://g")"; if [ -n "${PS1:-""}" ]; then PS1="$(echo "$PS1" | sed 's/\[fbt\]//g')"; elif [ -n "${PROMPT:-""}" ]; then PROMPT="$(echo "$PROMPT" | sed 's/\[fbt\]//g')"; fi - PYTHONNOUSERSITE="$SAVED_PYTHONNOUSERSITE"; - PYTHONPATH="$SAVED_PYTHONPATH"; - PYTHONHOME="$SAVED_PYTHONHOME"; + if [ -n "$SAVED_SSL_CERT_FILE" ]; then + export SSL_CERT_FILE="$SAVED_SSL_CERT_FILE"; + export REQUESTS_CA_BUNDLE="$SAVED_REQUESTS_CA_BUNDLE"; + else + unset SSL_CERT_FILE; + unset REQUESTS_CA_BUNDLE; + fi + export PYTHONNOUSERSITE="$SAVED_PYTHONNOUSERSITE"; + export PYTHONPATH="$SAVED_PYTHONPATH"; + export PYTHONHOME="$SAVED_PYTHONHOME"; + unset SAVED_SSL_CERT_FILE; + unset SAVED_REQUESTS_CA_BUNDLE; unset SAVED_PYTHONNOUSERSITE; unset SAVED_PYTHONPATH; unset SAVED_PYTHONHOME; @@ -121,7 +132,7 @@ fbtenv_get_kernel_type() TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-linux"; TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz"; elif echo "$SYS_TYPE" | grep -q "MINGW"; then - echo "In MinGW shell use \"[u]fbt.cmd\" instead of \"[u]fbt\""; + echo "In MinGW shell, use \"[u]fbt.cmd\" instead of \"[u]fbt\""; return 1; else echo "Your system configuration is not supported. Sorry.. Please report us your configuration."; @@ -248,6 +259,7 @@ fbtenv_check_download_toolchain() elif [ ! -f "$TOOLCHAIN_ARCH_DIR/VERSION" ]; then fbtenv_download_toolchain || return 1; elif [ "$(cat "$TOOLCHAIN_ARCH_DIR/VERSION")" -ne "$FBT_TOOLCHAIN_VERSION" ]; then + echo "FBT: starting toolchain upgrade process.." fbtenv_download_toolchain || return 1; fi return 0; @@ -269,6 +281,13 @@ fbtenv_download_toolchain() return 0; } +fbtenv_print_version() +{ + if [ -n "$FBT_VERBOSE" ]; then + echo "FBT: using toolchain version $(cat "$TOOLCHAIN_ARCH_DIR/VERSION")"; + fi +} + fbtenv_main() { fbtenv_check_sourced || return 1; @@ -281,18 +300,25 @@ fbtenv_main() fbtenv_check_script_path || return 1; fbtenv_check_download_toolchain || return 1; fbtenv_set_shell_prompt; + fbtenv_print_version; PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/openocd/bin:$PATH"; + PATH="$TOOLCHAIN_ARCH_DIR/openssl/bin:$PATH"; + export PATH; - SAVED_PYTHONNOUSERSITE="${PYTHONNOUSERSITE:-""}"; - SAVED_PYTHONPATH="${PYTHONPATH:-""}"; - SAVED_PYTHONHOME="${PYTHONHOME:-""}"; + export SAVED_SSL_CERT_FILE="${SSL_CERT_FILE:-""}"; + export SAVED_REQUESTS_CA_BUNDLE="${REQUESTS_CA_BUNDLE:-""}"; + export SAVED_PYTHONNOUSERSITE="${PYTHONNOUSERSITE:-""}"; + export SAVED_PYTHONPATH="${PYTHONPATH:-""}"; + export SAVED_PYTHONHOME="${PYTHONHOME:-""}"; - PYTHONNOUSERSITE=1; - PYTHONPATH=; - PYTHONHOME=; + export SSL_CERT_FILE="$TOOLCHAIN_ARCH_DIR/python/lib/python3.11/site-packages/certifi/cacert.pem"; + export REQUESTS_CA_BUNDLE="$SSL_CERT_FILE"; + export PYTHONNOUSERSITE=1; + export PYTHONPATH=; + export PYTHONHOME=; } fbtenv_main "${1:-""}"; diff --git a/scripts/update.py b/scripts/update.py index 3259c5b0..2b015726 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -199,7 +199,7 @@ class Main(App): def disclaimer(self): self.logger.error( - "You might brick you device into a state in which you'd need an SWD programmer to fix it." + "You might brick your device into a state in which you'd need an SWD programmer to fix it." ) self.logger.error( "Please confirm that you REALLY want to do that with --I-understand-what-I-am-doing=yes"