From a7ab4b9c326e76d5060aa528364f2fce6455a741 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Sun, 14 Jan 2024 08:47:38 +0300 Subject: [PATCH 01/15] [FL-3678] NFC UI refactor (#3361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added new image DolphinSaved_113x58.png for all "saved" pages * New image DolphinDone_80x58.png added * Replaced dolphins on all scenes accroding to new UI specs * New success dolphin image added * Success scene image replaced * Changed image and text for update initial scene * Image and text adjusted for "Original restored" scene * Removed old DolphinNice_96x59.png image * New image for LFRFID scene * Removed unused image * New UI image added to assets * Replaced warning dolphin on mf_classic write initial fail scene * Removed old image * Changed image on scenes to a new one * New dolphin mafia image * Replaced dolphin mafia image to a new one * Removed DolphinMafia_115x62.png * New check symbol on completed state for detect_reader * Adjusted layout elements position * Removed second switching to popup view in order to achieve control in support callbacks In general now we show generic scene and after that in on_enter callback we can redefine it for particular protocol * CardDetected event now also triggers on_event callback * Now on AuthRequest we throw CardDetected custom event * Added callback for read_on_event * Now we show different screen while reading and unlocking * Fixed missing asstes for some scenes * Update DolphinMafia_119x62.png * Adjusted all the scenes with DolphinMafia image * Scenes with save image adjusted * Removed unnecessary assets DolphinMafia_119x62.png and DolphinSaved_113x58.png * All common dolphins moved to Dolphin folder * Moved DolphinReadingSuccess_59x63.png to Dolphin folder * Set proper led color for detect and read scenes * Added new notification sequence for semi_success results * Use new sequence for semi_success nfc reads * Different events are now throwed depending on read result * Added handling of incomplete event for ultralight cards * Replaced image for iButton scene * Updated API for f18 * Fixed issue with unlock retry sequence * Fix after review * Success notification replaced to semi success in case of incomplete mf classic reading * New text for read scene * New read result sound notification logic for mf classic cards * Change MIFARE name accroding to new requirements * New QR code image for MFKey app * Update nfc_scene_mf_classic_mfkey_complete.c scene according to new UI requirements * Update detect_reader.c and check_big_20x17.png * New nfc save confirm scene added * Implemented new flow for 'Detect Reader button' after partial mf classic read according to new UI * UID for 15693 tags now shown on the new line * Fix nfc unit tests * Revert "Fix nfc unit tests" This reverts commit 685ed6bfad1980e42098a8bbe366de5b8b4cfd09. * Rolled back all Mifare renamings in library files * Revert "Change MIFARE name accroding to new requirements" This reverts commit cfb974dc1f5bff1d46a0483741b2b8f4726cdda3. * Now Mifare word is changed only on the app level without changes to lib level Co-authored-by: あく Co-authored-by: gornekich --- .../iso15693_3/iso15693_3_render.c | 6 +-- .../protocol_support/mf_classic/mf_classic.c | 6 ++- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 +- .../nfc_scene_mf_classic_detect_reader.c | 7 +++ .../nfc_scene_mf_classic_mfkey_complete.c | 13 +++--- .../main/nfc/scenes/nfc_scene_save_confirm.c | 44 ++++++++++++++++++ .../main/nfc/scenes/nfc_scene_save_success.c | 3 ++ .../main/nfc/scenes/nfc_scene_set_type.c | 14 +++++- applications/main/nfc/views/detect_reader.c | 2 +- assets/icons/NFC/MFKey_qr_25x25.png | Bin 0 -> 218 bytes assets/icons/NFC/check_big_20x17.png | Bin 199 -> 994 bytes 12 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_save_confirm.c create mode 100644 assets/icons/NFC/MFKey_qr_25x25.png diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c index 92bdb22dc..bb2ab92d3 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -18,20 +18,20 @@ void nfc_render_iso15693_3_info( } void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) { - furi_string_cat_printf(str, "UID:"); + furi_string_cat_printf(str, "UID:\n"); size_t uid_len; const uint8_t* uid = iso15693_3_get_uid(data, &uid_len); for(size_t i = 0; i < uid_len; i++) { - furi_string_cat_printf(str, " %02X", uid[i]); + furi_string_cat_printf(str, "%02X ", uid[i]); } if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { const uint16_t block_count = iso15693_3_get_block_count(data); const uint8_t block_size = iso15693_3_get_block_size(data); - furi_string_cat_printf(str, "Memory: %u bytes\n", block_count * block_size); + furi_string_cat_printf(str, "\nMemory: %u bytes\n", block_count * block_size); furi_string_cat_printf(str, "(%u blocks x %u bytes)", block_count, block_size); } } diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 3e0468cd9..4f4668ea7 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -23,6 +23,8 @@ static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( @@ -126,6 +128,8 @@ static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeShort, temp_str); widget_add_text_scroll_element( @@ -168,7 +172,7 @@ static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { if(event == SubmenuIndexDetectReader) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); dolphin_deed(DolphinDeedNfcDetectReader); return true; } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9887996d..70e7c3d46 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -23,6 +23,7 @@ ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) +ADD_SCENE(nfc, save_confirm, SaveConfirm) ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite) ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 721919d2b..d14f80b62 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -24,7 +24,7 @@ void nfc_scene_extra_actions_on_enter(void* context) { instance); submenu_add_item( submenu, - "Mifare Classic Keys", + "MIFARE Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, instance); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c index 987f81837..e2d3e6d72 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c @@ -134,6 +134,13 @@ bool nfc_scene_mf_classic_detect_reader_on_event(void* context, SceneManagerEven instance->listener = NULL; } mfkey32_logger_free(instance->mfkey32_logger); + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSaveSuccess)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } else if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneReadSuccess)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneReadSuccess); + } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c index 8e07043e2..eb0aa7c3a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c @@ -18,15 +18,16 @@ void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) { widget_add_string_multiline_element( instance->widget, 64, - 32, - AlignCenter, + 13, AlignCenter, + AlignTop, FontSecondary, - "Now use Mfkey32\nto extract keys"); + "Now use Mfkey32 to extract \nkeys: lab.flipper.net/nfc-tools"); + widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25); widget_add_button_element( instance->widget, - GuiButtonTypeCenter, - "OK", + GuiButtonTypeRight, + "Finish", nfc_scene_mf_classic_mfkey_complete_callback, instance); @@ -38,7 +39,7 @@ bool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEve bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeCenter) { + if(event.event == GuiButtonTypeRight) { consumed = scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneStart); } diff --git a/applications/main/nfc/scenes/nfc_scene_save_confirm.c b/applications/main/nfc/scenes/nfc_scene_save_confirm.c new file mode 100644 index 000000000..9d0a206d3 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_save_confirm.c @@ -0,0 +1,44 @@ +#include "../nfc_app_i.h" + +void nfc_scene_save_confirm_dialog_callback(DialogExResult result, void* context) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_save_confirm_on_enter(void* context) { + NfcApp* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Skip"); + dialog_ex_set_right_button_text(dialog_ex, "Save"); + dialog_ex_set_header(dialog_ex, "Save the Key?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_save_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_save_confirm_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == DialogExResultLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_save_confirm_on_exit(void* context) { + NfcApp* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 9d2a38013..ef7863c13 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -28,6 +28,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index ff82587df..21c0d91db 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -32,10 +32,20 @@ void nfc_scene_set_type_on_enter(void* context) { nfc_protocol_support_common_submenu_callback, instance); + FuriString* str = furi_string_alloc(); for(size_t i = 0; i < NfcDataGeneratorTypeNum; i++) { - const char* name = nfc_data_generator_get_name(i); - submenu_add_item(submenu, name, i, nfc_protocol_support_common_submenu_callback, instance); + furi_string_cat_str(str, nfc_data_generator_get_name(i)); + furi_string_replace_str(str, "Mifare", "MIFARE"); + + submenu_add_item( + submenu, + furi_string_get_cstr(str), + i, + nfc_protocol_support_common_submenu_callback, + instance); + furi_string_reset(str); } + furi_string_free(str); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index d832d27d6..4d7b324e0 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -50,7 +50,7 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateDone) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!"); - canvas_draw_icon(canvas, 20, 23, &I_check_big_20x17); + canvas_draw_icon(canvas, 24, 23, &I_check_big_20x17); } else { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting..."); diff --git a/assets/icons/NFC/MFKey_qr_25x25.png b/assets/icons/NFC/MFKey_qr_25x25.png new file mode 100644 index 0000000000000000000000000000000000000000..feb07e2807e7e116bcbd76b48e5555a4c48dc7a1 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(hUwg8_HS0MfW|No^o=iddg`aNA7 zLn>~i1TqR8P~d3#Utcx5>%rMDZBr+fTM?gSRZ{+}sksq*TzU3X_G(3uq)mQDZhSMV z@_o$KFX5THNn&|Yaa_i)=;WZqcju(8c+~R3zcok>&>S`_TF9C gbmIB1#PS~u`_;J>%r~*R04-whboFyt=akR{02j7K1^@s6 From 1fd4839bb6a5346666bffa04310081005a20aaa8 Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Sun, 14 Jan 2024 17:07:42 +0900 Subject: [PATCH 02/15] Furi_hal_rtc: new function (#3294) * furi_hal_rtc_timestamp_to_datetime added * hw targets api version sync * hw targets api version sync, bump * FuriHal: update rtc docs * unit tests added Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- .../unit_tests/furi_hal/furi_hal_tests.c | 41 +++++++++++++++++++ targets/f18/api_symbols.csv | 1 + targets/f7/api_symbols.csv | 1 + targets/f7/furi_hal/furi_hal_rtc.c | 26 ++++++++++++ targets/furi_hal_include/furi_hal_rtc.h | 13 +++++- 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c index 2dbaa4d86..e3e44291f 100644 --- a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c +++ b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c @@ -1,8 +1,11 @@ +#include "furi_hal_rtc.h" +#include #include #include #include #include #include "../minunit.h" +#include #define DATA_SIZE 4 #define EEPROM_ADDRESS 0b10101000 @@ -211,6 +214,37 @@ MU_TEST(furi_hal_i2c_ext_eeprom) { } } +MU_TEST(furi_hal_rtc_timestamp2datetime_min) { + uint32_t test_value = 0; + FuriHalRtcDateTime min_datetime_expected = {0, 0, 0, 1, 1, 1970, 0}; + + FuriHalRtcDateTime result = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &result); + + mu_assert_mem_eq(&min_datetime_expected, &result, sizeof(result)); +} + +MU_TEST(furi_hal_rtc_timestamp2datetime_max) { + uint32_t test_value = UINT32_MAX; + FuriHalRtcDateTime max_datetime_expected = {6, 28, 15, 7, 2, 2106, 0}; + + FuriHalRtcDateTime result = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &result); + + mu_assert_mem_eq(&max_datetime_expected, &result, sizeof(result)); +} + +MU_TEST(furi_hal_rtc_timestamp2datetime2timestamp) { + uint32_t test_value = random(); + + FuriHalRtcDateTime datetime = {0}; + furi_hal_rtc_timestamp_to_datetime(test_value, &datetime); + + uint32_t result = furi_hal_rtc_datetime_to_timestamp(&datetime); + + mu_assert_int_eq(test_value, result); +} + MU_TEST_SUITE(furi_hal_i2c_int_suite) { MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown); MU_RUN_TEST(furi_hal_i2c_int_1b); @@ -224,8 +258,15 @@ MU_TEST_SUITE(furi_hal_i2c_ext_suite) { MU_RUN_TEST(furi_hal_i2c_ext_eeprom); } +MU_TEST_SUITE(furi_hal_rtc_datetime_suite) { + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime_min); + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime_max); + MU_RUN_TEST(furi_hal_rtc_timestamp2datetime2timestamp); +} + int run_minunit_test_furi_hal() { MU_RUN_SUITE(furi_hal_i2c_int_suite); MU_RUN_SUITE(furi_hal_i2c_ext_suite); + MU_RUN_SUITE(furi_hal_rtc_datetime_suite); return MU_EXIT_CODE; } diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 56e47318e..1a09a4768 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1261,6 +1261,7 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_sync_shadow,void, +Function,+,furi_hal_rtc_timestamp_to_datetime,void,"uint32_t, FuriHalRtcDateTime*" Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_sd_get_card_state,FuriStatus, Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo* diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 712733d1a..3f3915594 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1456,6 +1456,7 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_sync_shadow,void, +Function,+,furi_hal_rtc_timestamp_to_datetime,void,"uint32_t, FuriHalRtcDateTime*" Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_sd_get_card_state,FuriStatus, Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo* diff --git a/targets/f7/furi_hal/furi_hal_rtc.c b/targets/f7/furi_hal/furi_hal_rtc.c index cb8065bed..6c1c34a9b 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.c +++ b/targets/f7/furi_hal/furi_hal_rtc.c @@ -424,6 +424,32 @@ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { return timestamp; } +void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) { + uint32_t days = timestamp / FURI_HAL_RTC_SECONDS_PER_DAY; + uint32_t seconds_in_day = timestamp % FURI_HAL_RTC_SECONDS_PER_DAY; + + datetime->year = FURI_HAL_RTC_EPOCH_START_YEAR; + + while(days >= furi_hal_rtc_get_days_per_year(datetime->year)) { + days -= furi_hal_rtc_get_days_per_year(datetime->year); + (datetime->year)++; + } + + datetime->month = 1; + while(days >= furi_hal_rtc_get_days_per_month( + furi_hal_rtc_is_leap_year(datetime->year), datetime->month)) { + days -= furi_hal_rtc_get_days_per_month( + furi_hal_rtc_is_leap_year(datetime->year), datetime->month); + (datetime->month)++; + } + + datetime->day = days + 1; + datetime->hour = seconds_in_day / FURI_HAL_RTC_SECONDS_PER_HOUR; + datetime->minute = + (seconds_in_day % FURI_HAL_RTC_SECONDS_PER_HOUR) / FURI_HAL_RTC_SECONDS_PER_MINUTE; + datetime->second = seconds_in_day % FURI_HAL_RTC_SECONDS_PER_MINUTE; +} + uint16_t furi_hal_rtc_get_days_per_year(uint16_t year) { return furi_hal_rtc_days_per_year[furi_hal_rtc_is_leap_year(year) ? 1 : 0]; } diff --git a/targets/furi_hal_include/furi_hal_rtc.h b/targets/furi_hal_include/furi_hal_rtc.h index 98b23466c..fb9d39b3c 100644 --- a/targets/furi_hal_include/furi_hal_rtc.h +++ b/targets/furi_hal_include/furi_hal_rtc.h @@ -252,13 +252,24 @@ uint32_t furi_hal_rtc_get_pin_fails(); uint32_t furi_hal_rtc_get_timestamp(); /** Convert DateTime to UNIX timestamp + * + * @warning Mind timezone when perform conversion * - * @param datetime The datetime + * @param datetime The datetime (UTC) * * @return UNIX Timestamp in seconds from UNIX epoch start */ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); +/** Convert UNIX timestamp to DateTime + * + * @warning Mind timezone when perform conversion + * + * @param[in] timestamp UNIX Timestamp in seconds from UNIX epoch start + * @param[out] datetime The datetime (UTC) + */ +void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime); + /** Gets the number of days in the year according to the Gregorian calendar. * * @param year Input year. From 36114de5f72021bc46a8ebfcb6e72985228306bb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:39:53 +0300 Subject: [PATCH 03/15] SubGhz: add `subghz tx_from_file` CLI cmd, major TX flow refactoring, various improvements and bug fixes (#3302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By Skorpionm * SubGhz: add cmd CLI "subghz tx_from_file" * SubGhz: add sending raw.sub files * SubGhz: add load custom preset * SubGhz: remove unnecessary files * SubGhz: change message * SubGhz: fix printf formatting * SubGhz: Cli refactoring code * FuriHal: add furi_hal_subghz Tx Rx IDLE state switching test * SubGhz: remove debug code, fix ext driver compilation * SubGhz: cleanup code, move wait status routine to cc1101 driver * SubGhz: proper pin mode transition in tx stop isr routine, proper DMA and ISR priorities, fix issue with async tx stuck * SubGhz: simplify async tx stop flow, fix ISR ARR check condition race * SubGhz: check ARR only when we transmitting * SubGhz: check ARR only when we transmitting for ext cc1101 * SubGhz: lower ISR priorities to safe level * SubGhz: proper gpio config, comments update Co-authored-by: あく --- .../drivers/subghz/cc1101_ext/cc1101_ext.c | 49 +-- .../protocol_support/mf_classic/mf_classic.c | 2 +- .../subghz_frequency_analyzer_worker.c | 11 +- applications/main/subghz/subghz_cli.c | 314 ++++++++++++++++-- lib/drivers/cc1101.c | 14 + lib/drivers/cc1101.h | 10 + targets/f7/furi_hal/furi_hal_interrupt.c | 3 +- targets/f7/furi_hal/furi_hal_subghz.c | 68 ++-- 8 files changed, 362 insertions(+), 109 deletions(-) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index 5c79f19f4..5bbfc9f95 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -48,7 +48,6 @@ typedef enum { SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */ SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */ SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzDeviceCC1101ExtStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzDeviceCC1101ExtState; /** SubGhz regulation, receive transmission on the current frequency for the @@ -417,6 +416,9 @@ void subghz_device_cc1101_ext_reset() { void subghz_device_cc1101_ext_idle() { furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to IDLE mode + furi_check(cc1101_wait_status_state( + subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); if(subghz_device_cc1101_ext->power_amp) { furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 0); @@ -426,6 +428,9 @@ void subghz_device_cc1101_ext_idle() { void subghz_device_cc1101_ext_rx() { furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to Rx mode + furi_check( + cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); if(subghz_device_cc1101_ext->power_amp) { furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 0); @@ -436,6 +441,9 @@ bool subghz_device_cc1101_ext_tx() { if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle); + //waiting for the chip to switch to Tx mode + furi_check( + cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000)); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); if(subghz_device_cc1101_ext->power_amp) { furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO, 1); @@ -706,7 +714,6 @@ static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t sa if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); } - LL_TIM_EnableIT_UPDATE(TIM17); break; } else { // Lowest possible value is 4us @@ -742,22 +749,6 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() { #endif } -static void subghz_device_cc1101_ext_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { - if(LL_TIM_GetAutoReload(TIM17) == 0) { - if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) { - LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); - subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - if(subghz_device_cc1101_ext->async_mirror_pin != NULL) - furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); - LL_TIM_DisableCounter(TIM17); - } - } - LL_TIM_ClearFlag_UPDATE(TIM17); - } -} - bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) { furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); furi_assert(callback); @@ -786,7 +777,7 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | - LL_DMA_MODE_NORMAL); + LL_DMA_PRIORITY_VERYHIGH); LL_DMA_SetDataLength( SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP); @@ -809,9 +800,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM17); - furi_hal_interrupt_set_isr( - FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); - subghz_device_cc1101_ext_async_tx_middleware_idle( &subghz_device_cc1101_ext->async_tx.middleware); subghz_device_cc1101_ext_async_tx_refill( @@ -869,22 +857,21 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb } bool subghz_device_cc1101_ext_is_async_tx_complete() { - return subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd; + return ( + (subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) && + (LL_TIM_GetAutoReload(TIM17) == 0)); } void subghz_device_cc1101_ext_stop_async_tx() { - furi_assert( - subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || - subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); - - // Deinitialize GPIO - furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); - furi_hal_gpio_init( - subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); // Shutdown radio subghz_device_cc1101_ext_idle(); + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + // Deinitialize Timer furi_hal_bus_disable(FuriHalBusTIM17); furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 4f4668ea7..7feeccf22 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -121,7 +121,7 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { } } -static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { +static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { //-V524 const NfcDevice* device = instance->nfc_device; const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 6551e0425..c198f2fe2 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -81,7 +81,6 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { uint32_t frequency = 0; float rssi_temp = 0; uint32_t frequency_temp = 0; - CC1101Status status; FuriHalSpiBusHandle* spi_bus = instance->spi_bus; const SubGhzDevice* radio_device = instance->radio_device; @@ -143,9 +142,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { frequency = cc1101_set_frequency(spi_bus, current_frequency); cc1101_calibrate(spi_bus); - do { - status = cc1101_get_status(spi_bus); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(spi_bus); furi_hal_spi_release(spi_bus); @@ -191,9 +189,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { frequency = cc1101_set_frequency(spi_bus, i); cc1101_calibrate(spi_bus); - do { - status = cc1101_get_status(spi_bus); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(spi_bus); furi_hal_spi_release(spi_bus); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index dc379296a..679a1c5e7 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -49,6 +49,28 @@ static void subghz_cli_radio_device_power_off() { if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); } +static SubGhzEnvironment* subghz_cli_environment_init(void) { + SubGhzEnvironment* environment = subghz_environment_alloc(); + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { + printf("Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); + } else { + printf("Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); + } + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { + printf("Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); + } else { + printf("Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n"); + } + subghz_environment_set_came_atomo_rainbow_table_file_name( + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + return environment; +} + void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -323,14 +345,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); furi_check(instance->stream); - SubGhzEnvironment* environment = subghz_environment_alloc(); - subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME); - subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); - subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + SubGhzEnvironment* environment = subghz_cli_environment_init(); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -512,23 +527,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { // Allocate context SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); - SubGhzEnvironment* environment = subghz_environment_alloc(); - if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); - } else { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); - } - if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); - } else { - printf( - "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); - } - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); - subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + SubGhzEnvironment* environment = subghz_cli_environment_init(); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -573,6 +572,262 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { furi_string_free(file_name); } +static FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) { + FuriHalSubGhzPreset preset = FuriHalSubGhzPresetIDLE; + if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) { + preset = FuriHalSubGhzPresetOok270Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) { + preset = FuriHalSubGhzPresetOok650Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) { + preset = FuriHalSubGhzPreset2FSKDev238Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) { + preset = FuriHalSubGhzPreset2FSKDev476Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) { + preset = FuriHalSubGhzPresetCustom; + } else { + printf("subghz tx_from_file: unknown preset"); + } + return preset; +} + +void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) { // -V524 + UNUSED(context); + FuriString* file_name; + file_name = furi_string_alloc(); + furi_string_set(file_name, ANY_PATH("subghz/test.sub")); + uint32_t repeat = 10; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + FlipperFormat* fff_data_raw = flipper_format_string_alloc(); + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data32; + bool check_file = false; + const SubGhzDevice* device = NULL; + + uint32_t frequency = 0; + SubGhzTransmitter* transmitter = NULL; + + subghz_devices_init(); + + SubGhzEnvironment* environment = subghz_cli_environment_init(); + + do { + if(furi_string_size(args)) { + if(!args_read_string_and_trim(args, file_name)) { + cli_print_usage( + "subghz tx_from_file: ", + " ", + furi_string_get_cstr(args)); + break; + } + } + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &repeat, &device_ind); + if(ret != 2) { + printf("sscanf returned %d, repeat: %lu device: %lu\r\n", ret, repeat, device_ind); + cli_print_usage( + "subghz tx_from_file:", + " ", + furi_string_get_cstr(args)); + break; + } + } + + device = subghz_cli_command_get_device(&device_ind); + if(device == NULL) { + printf("subghz tx_from_file: \033[0;31mError device not found\033[0m\r\n"); + break; + } + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + printf( + "subghz tx_from_file: \033[0;31mError open file\033[0m %s\r\n", + furi_string_get_cstr(file_name)); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + printf("subghz tx_from_file: \033[0;31mMissing or incorrect header\033[0m\r\n"); + break; + } + + if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || + (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && + temp_data32 == SUBGHZ_KEY_FILE_VERSION) { + } else { + printf("subghz tx_from_file: \033[0;31mType or version mismatch\033[0m\r\n"); + break; + } + + //Load frequency + if(!flipper_format_read_uint32(fff_data_file, "Frequency", &frequency, 1)) { + printf("subghz tx_from_file: \033[0;31mMissing Frequency\033[0m\r\n"); + break; + } + + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf("subghz tx_from_file: \033[0;31mFrequency not supported\033[0m\r\n"); + break; + } + + //Load preset + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + printf("subghz tx_from_file: \033[0;31mMissing Preset\033[0m\r\n"); + break; + } + + subghz_devices_begin(device); + subghz_devices_reset(device); + + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + uint8_t* custom_preset_data; + uint32_t custom_preset_data_size; + if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) + break; + if(!temp_data32 || (temp_data32 % 2)) { + printf("subghz tx_from_file: \033[0;31mCustom_preset_data size error\033[0m\r\n"); + break; + } + custom_preset_data_size = sizeof(uint8_t) * temp_data32; + custom_preset_data = malloc(custom_preset_data_size); + if(!flipper_format_read_hex( + fff_data_file, + "Custom_preset_data", + custom_preset_data, + custom_preset_data_size)) { + printf("subghz tx_from_file: \033[0;31mCustom_preset_data read error\033[0m\r\n"); + break; + } + subghz_devices_load_preset( + device, + subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), + custom_preset_data); + free(custom_preset_data); + } else { + subghz_devices_load_preset( + device, subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), NULL); + } + + subghz_devices_set_frequency(device, frequency); + + //Load protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + printf("subghz tx_from_file: \033[0;31mMissing protocol\033[0m\r\n"); + break; + } + + SubGhzProtocolStatus status; + bool is_init_protocol = true; + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { // if RAW protocol + subghz_protocol_raw_gen_fff_data( + fff_data_raw, furi_string_get_cstr(file_name), subghz_devices_get_name(device)); + + transmitter = + subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n"); + is_init_protocol = false; + } + + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, fff_data_raw); + if(status != SubGhzProtocolStatusOk) { + printf( + "subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n", + status); + is_init_protocol = false; + } + } + + } else { //if not RAW protocol + flipper_format_insert_or_update_uint32(fff_data_file, "Repeat", &repeat, 1); + + transmitter = + subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n"); + is_init_protocol = false; + } + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, fff_data_file); + if(status != SubGhzProtocolStatusOk) { + printf( + "subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n", + status); + is_init_protocol = false; + } + } + + flipper_format_delete_key(fff_data_file, "Repeat"); + } + + if(is_init_protocol) { + check_file = true; + } else { + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_transmitter_free(transmitter); + } + + } while(false); + + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + if(check_file) { + furi_hal_power_suppress_charge_enter(); + + printf( + "Listening at \033[0;33m%s\033[0m. Frequency=%lu, Protocol=%s\r\n\r\nPress CTRL+C to stop\r\n\r\n", + furi_string_get_cstr(file_name), + frequency, + furi_string_get_cstr(temp_str)); + do { + //delay in downloading files and other preparatory processes + furi_delay_ms(200); + if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { + while( + !(subghz_devices_is_async_complete_tx(device) || + cli_cmd_interrupt_received(cli))) { + printf("."); + fflush(stdout); + furi_delay_ms(333); + } + subghz_devices_stop_async_tx(device); + + } else { + printf("Transmission on this frequency is restricted in your region\r\n"); + } + + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { + subghz_transmitter_stop(transmitter); + repeat--; + if(!cli_cmd_interrupt_received(cli) && repeat) + subghz_transmitter_deserialize(transmitter, fff_data_raw); + } + + } while(!cli_cmd_interrupt_received(cli) && + (repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW"))); + + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_cli_radio_device_power_off(); + + furi_hal_power_suppress_charge_exit(); + + subghz_transmitter_free(transmitter); + } + flipper_format_free(fff_data_raw); + furi_string_free(file_name); + furi_string_free(temp_str); + subghz_devices_deinit(); + subghz_environment_free(environment); +} + static void subghz_cli_command_print_usage() { printf("Usage:\r\n"); printf("subghz \r\n"); @@ -585,11 +840,13 @@ static void subghz_cli_command_print_usage() { printf("\trx \t - Receive\r\n"); printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); + printf( + "\ttx_from_file \t - Transmitting from file\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\r\n"); printf(" debug cmd:\r\n"); - printf("\ttx_carrier \t - Transmit carrier\r\n"); + printf("\ttx_carrier \t - Transmitting carrier\r\n"); printf("\trx_carrier \t - Receive carrier\r\n"); printf( "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); @@ -901,6 +1158,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "tx_from_file") == 0) { + subghz_cli_command_tx_from_file(cli, args, context); + break; + } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) { subghz_cli_command_encrypt_keeloq(cli, args); diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index 85d915acd..b71d78ff0 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -78,6 +78,20 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SNOP); } +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us) { + bool result = false; + CC1101Status status = {0}; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us); + while(!furi_hal_cortex_timer_is_expired(timer)) { + status = cc1101_strobe(handle, CC1101_STROBE_SNOP); + if(status.STATE == state) { + result = true; + break; + } + } + return result; +} + CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SPWD); } diff --git a/lib/drivers/cc1101.h b/lib/drivers/cc1101.h index d8ee05d52..c8c552bec 100644 --- a/lib/drivers/cc1101.h +++ b/lib/drivers/cc1101.h @@ -59,6 +59,16 @@ CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); */ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); +/** Wait specific chip state + * + * @param handle The SPI bus handle + * @param[in] state The state to wait + * @param[in] timeout_us The timeout in microseconds + * + * @return true on success, false otherwise + */ +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us); + /** Enable shutdown mode * * @param handle - pointer to FuriHalSpiHandle diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index c508dac72..889ddc56c 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,7 +11,7 @@ #define TAG "FuriHalInterrupt" -#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY 5 +#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) typedef struct { FuriHalInterruptISR isr; diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c index 51c65f8ac..7c4af4411 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.c +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -36,7 +36,6 @@ typedef enum { SubGhzStateAsyncRx, /**< Async RX started */ SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ } SubGhzState; @@ -305,12 +304,16 @@ void furi_hal_subghz_reset() { void furi_hal_subghz_idle() { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to IDLE mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } void furi_hal_subghz_rx() { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to Rx mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateRX, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } @@ -318,6 +321,8 @@ bool furi_hal_subghz_tx() { if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz); + //waiting for the chip to switch to Tx mode + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateTX, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); return true; } @@ -405,10 +410,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) { uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value); cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - while(true) { - CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - if(status.STATE == CC1101StateIDLE) break; - } + furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000)); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); return real_frequency; @@ -678,7 +680,6 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); } - LL_TIM_EnableIT_UPDATE(TIM2); break; } else { // Lowest possible value is 2us @@ -720,21 +721,6 @@ static void furi_hal_subghz_async_tx_dma_isr() { #endif } -static void furi_hal_subghz_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) { - LL_TIM_ClearFlag_UPDATE(TIM2); - if(LL_TIM_GetAutoReload(TIM2) == 0) { - if(furi_hal_subghz.state == SubGhzStateAsyncTx) { - furi_hal_subghz.state = SubGhzStateAsyncTxEnd; - LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); - //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - LL_TIM_DisableCounter(TIM2); - } - } - } -} - bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { furi_assert(furi_hal_subghz.state == SubGhzStateIdle); furi_assert(callback); @@ -755,7 +741,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* // Connect CC1101_GD0 to TIM2 as output furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); + &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); // Configure DMA LL_DMA_InitTypeDef dma_config = {0}; @@ -769,7 +755,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; dma_config.NbData = FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; + dma_config.Priority = + LL_DMA_PRIORITY_VERYHIGH; // Ensure that ARR is updated before anyone else try to check it LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); @@ -797,8 +784,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); LL_TIM_DisableMasterSlaveMode(TIM2); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); - furi_hal_subghz_async_tx_middleware_idle(&furi_hal_subghz_async_tx.middleware); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); @@ -806,15 +791,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_EnableDMAReq_UPDATE(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); - // Start counter -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); -#endif - furi_hal_subghz_tx(); - - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); - // Start debug if(furi_hal_subghz_start_debug()) { const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; @@ -836,30 +812,36 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; dma_config.NbData = 2; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + dma_config.Priority = LL_DMA_PRIORITY_HIGH; // Ensure that it's updated after ARR LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); } + // Start counter +#ifdef FURI_HAL_SUBGHZ_TX_GPIO + furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); +#endif + furi_hal_subghz_tx(); + + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + return true; } bool furi_hal_subghz_is_async_tx_complete() { - return furi_hal_subghz.state == SubGhzStateAsyncTxEnd; + return (furi_hal_subghz.state == SubGhzStateAsyncTx) && (LL_TIM_GetAutoReload(TIM2) == 0); } void furi_hal_subghz_stop_async_tx() { - furi_assert( - furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxEnd); - - // Deinitialize GPIO - // Keep in mind that cc1101 will try to pull it up in idle. - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); // Shutdown radio furi_hal_subghz_idle(); + + // Deinitialize GPIO + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); #ifdef FURI_HAL_SUBGHZ_TX_GPIO furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false); #endif From eef4574a634e82d1805334076569f2cea63d7dc3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:17:20 +0300 Subject: [PATCH 04/15] small fixes for subghz cli --- applications/main/subghz/subghz_cli.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 679a1c5e7..77554becf 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -61,8 +61,6 @@ static SubGhzEnvironment* subghz_cli_environment_init(void) { } else { printf("Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n"); } - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, SUBGHZ_CAME_ATOMO_DIR_NAME); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( @@ -800,7 +798,7 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) subghz_devices_stop_async_tx(device); } else { - printf("Transmission on this frequency is restricted in your region\r\n"); + printf("Transmission on this frequency is restricted in your settings\r\n"); } if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { @@ -825,6 +823,11 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) furi_string_free(file_name); furi_string_free(temp_str); subghz_devices_deinit(); + // Reset custom settings + subghz_environment_reset_keeloq(environment); + faac_slh_reset_prog_mode(); + subghz_custom_btns_reset(); + // Free environment subghz_environment_free(environment); } From 16a3f4c06ad3fcda67d331fc757d810215101a33 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:19:07 +0300 Subject: [PATCH 05/15] add missing include --- applications/main/subghz/subghz_cli.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 77554becf..23b9c7d4c 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -20,6 +20,8 @@ #include #include +#include + #define SUBGHZ_FREQUENCY_RANGE_STR \ "299999755...348000000 or 386999938...464000000 or 778999847...928000000" From a1e62c3e7687ebabb92a41aaccf5436d1864cf6b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:20:37 +0300 Subject: [PATCH 06/15] FuriHal: UART refactoring (#3211) * FuriHal: UART refactoring * ApiSymbols: add furi_record_destroy * FuriHal: cleanup serial API, add logging configuration in RTC * FuriHal: hide private part in _i header. Toolbox: cleanup value index. SystemSettings: logging device and baudrate. * FuriHal: RTC logging method documentation * Synchronize API Symbols * Furi: mark HEAP_PRINT_DEBUG as broken * FuriHal: furi_hal_serial, add custom IRQ func * Fix PR review issues * FuriHal: UART add reception DMA (#3220) * FuriHal: add DMA serial rx mode * usb_uart_bridge: switch to working with DMA * Sync api symbol versions * FuriHal: update serial docs and api * FuriHal: Selial added similar API for simple reception mode as with DMA * FuriHal: Update API target H18 * API: ver API H7 * FuriHal: Serial error processing * FuriHal: fix furi_hal_serial set baudrate * Sync api symbols * FuriHal: cleanup serial isr and various flag handling procedures * FuriHal: cleanup and simplify serial API * Debug: update UART Echo serial related flags * FuriHal: update serial API symbols naming * FuriHalSerial: various improvements and PR review fixes * FuriHal: proper ISR function signatures --------- Co-authored-by: Aleksandr Kutuzov Co-authored-by: hedger Co-authored-by: SkorP Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com> --- applications/debug/uart_echo/uart_echo.c | 85 +- applications/main/gpio/application.fam | 2 +- .../gpio/scenes/gpio_scene_usb_uart_config.c | 6 +- applications/main/gpio/usb_uart_bridge.c | 78 +- applications/main/u2f/u2f_hid.c | 2 - applications/services/cli/cli_commands.c | 9 +- .../settings/system/system_settings.c | 70 +- documentation/FuriHalBus.md | 6 +- furi/core/check.c | 59 +- furi/core/log.c | 107 ++- furi/core/log.h | 53 +- furi/core/memmgr_heap.c | 40 +- furi/core/mutex.c | 4 +- furi/core/thread.c | 3 +- lib/toolbox/value_index.c | 87 +- lib/toolbox/value_index.h | 9 +- targets/f18/api_symbols.csv | 62 +- targets/f18/furi_hal/furi_hal.c | 2 +- targets/f7/api_symbols.csv | 62 +- targets/f7/furi_hal/furi_hal.c | 2 +- targets/f7/furi_hal/furi_hal_console.c | 99 --- targets/f7/furi_hal/furi_hal_console.h | 37 - targets/f7/furi_hal/furi_hal_interrupt.c | 14 + targets/f7/furi_hal/furi_hal_interrupt.h | 6 + targets/f7/furi_hal/furi_hal_os.c | 7 +- targets/f7/furi_hal/furi_hal_power.c | 8 +- targets/f7/furi_hal/furi_hal_rtc.c | 60 +- .../furi_hal}/furi_hal_rtc.h | 82 +- targets/f7/furi_hal/furi_hal_serial.c | 838 ++++++++++++++++++ targets/f7/furi_hal/furi_hal_serial.h | 189 ++++ targets/f7/furi_hal/furi_hal_serial_control.c | 233 +++++ targets/f7/furi_hal/furi_hal_serial_control.h | 46 + targets/f7/furi_hal/furi_hal_serial_types.h | 15 + targets/f7/furi_hal/furi_hal_serial_types_i.h | 8 + targets/f7/furi_hal/furi_hal_uart.c | 244 ----- targets/f7/furi_hal/furi_hal_uart.h | 89 -- targets/furi_hal_include/furi_hal.h | 4 +- 37 files changed, 1967 insertions(+), 760 deletions(-) delete mode 100644 targets/f7/furi_hal/furi_hal_console.c delete mode 100644 targets/f7/furi_hal/furi_hal_console.h rename targets/{furi_hal_include => f7/furi_hal}/furi_hal_rtc.h (73%) create mode 100644 targets/f7/furi_hal/furi_hal_serial.c create mode 100644 targets/f7/furi_hal/furi_hal_serial.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_control.c create mode 100644 targets/f7/furi_hal/furi_hal_serial_control.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_types.h create mode 100644 targets/f7/furi_hal/furi_hal_serial_types_i.h delete mode 100644 targets/f7/furi_hal/furi_hal_uart.c delete mode 100644 targets/f7/furi_hal/furi_hal_uart.h diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index 4bede9ab4..0291c9e79 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -1,13 +1,14 @@ #include +#include + #include -#include -#include #include -#include -#include #include #include +#include +#include + #define LINES_ON_SCREEN 6 #define COLUMNS_ON_SCREEN 21 #define TAG "UartEcho" @@ -22,6 +23,7 @@ typedef struct { View* view; FuriThread* worker_thread; FuriStreamBuffer* rx_stream; + FuriHalSerialHandle* serial_handle; } UartEchoApp; typedef struct { @@ -39,10 +41,16 @@ struct UartDumpModel { typedef enum { WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event WorkerEventStop = (1 << 1), - WorkerEventRx = (1 << 2), + WorkerEventRxData = (1 << 2), + WorkerEventRxIdle = (1 << 3), + WorkerEventRxOverrunError = (1 << 4), + WorkerEventRxFramingError = (1 << 5), + WorkerEventRxNoiseError = (1 << 6), } WorkerEventFlags; -#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) +#define WORKER_EVENTS_MASK \ + (WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \ + WorkerEventRxFramingError | WorkerEventRxNoiseError) const NotificationSequence sequence_notification = { &message_display_backlight_on, @@ -91,14 +99,39 @@ static uint32_t uart_echo_exit(void* context) { return VIEW_NONE; } -static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { +static void + uart_echo_on_irq_cb(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) { furi_assert(context); + UNUSED(handle); UartEchoApp* app = context; + volatile FuriHalSerialRxEvent event_copy = event; + UNUSED(event_copy); - if(ev == UartIrqEventRXNE) { + WorkerEventFlags flag = 0; + + if(event & FuriHalSerialRxEventData) { + uint8_t data = furi_hal_serial_async_rx(handle); furi_stream_buffer_send(app->rx_stream, &data, 1, 0); - furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx); + flag |= WorkerEventRxData; } + + if(event & FuriHalSerialRxEventIdle) { + //idle line detected, packet transmission may have ended + flag |= WorkerEventRxIdle; + } + + //error detected + if(event & FuriHalSerialRxEventFrameError) { + flag |= WorkerEventRxFramingError; + } + if(event & FuriHalSerialRxEventNoiseError) { + flag |= WorkerEventRxNoiseError; + } + if(event & FuriHalSerialRxEventOverrunError) { + flag |= WorkerEventRxOverrunError; + } + + furi_thread_flags_set(furi_thread_get_id(app->worker_thread), flag); } static void uart_echo_push_to_list(UartDumpModel* model, const char data) { @@ -153,13 +186,13 @@ static int32_t uart_echo_worker(void* context) { furi_check((events & FuriFlagError) == 0); if(events & WorkerEventStop) break; - if(events & WorkerEventRx) { + if(events & WorkerEventRxData) { size_t length = 0; do { uint8_t data[64]; length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0); if(length > 0) { - furi_hal_uart_tx(FuriHalUartIdUSART1, data, length); + furi_hal_serial_tx(app->serial_handle, data, length); with_view_model( app->view, UartDumpModel * model, @@ -176,6 +209,23 @@ static int32_t uart_echo_worker(void* context) { with_view_model( app->view, UartDumpModel * model, { UNUSED(model); }, true); } + + if(events & WorkerEventRxIdle) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect IDLE\r\n", 15); + } + + if(events & + (WorkerEventRxOverrunError | WorkerEventRxFramingError | WorkerEventRxNoiseError)) { + if(events & WorkerEventRxOverrunError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect ORE\r\n", 14); + } + if(events & WorkerEventRxFramingError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect FE\r\n", 13); + } + if(events & WorkerEventRxNoiseError) { + furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect NE\r\n", 13); + } + } } return 0; @@ -221,9 +271,11 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { furi_thread_start(app->worker_thread); // Enable uart listener - furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); + app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart); + furi_check(app->serial_handle); + furi_hal_serial_init(app->serial_handle, baudrate); + + furi_hal_serial_async_rx_start(app->serial_handle, uart_echo_on_irq_cb, app, true); return app; } @@ -231,12 +283,13 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { static void uart_echo_app_free(UartEchoApp* app) { furi_assert(app); - furi_hal_console_enable(); // this will also clear IRQ callback so thread is no longer referenced - furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop); furi_thread_join(app->worker_thread); furi_thread_free(app->worker_thread); + furi_hal_serial_deinit(app->serial_handle); + furi_hal_serial_control_release(app->serial_handle); + // Free views view_dispatcher_remove_view(app->view_dispatcher, 0); diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index 763919921..607d97a27 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -3,7 +3,7 @@ App( name="GPIO", apptype=FlipperAppType.MENUEXTERNAL, entry_point="gpio_app", - stack_size=1 * 1024, + stack_size=2 * 1024, icon="A_GPIO_14", order=50, fap_libs=["assets"], diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index 8fcacd403..f8b142c63 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -46,7 +46,7 @@ void line_ensure_flow_invariant(GpioApp* app) { // selected. This function enforces that invariant by resetting flow_pins // to None if it is configured to 16,15 when LPUART is selected. - uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; + uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalSerialIdLpuart ? 3 : 4; VariableItem* item = app->var_item_flow; variable_item_set_values_count(item, available_flow_pins); @@ -77,9 +77,9 @@ static void line_port_cb(VariableItem* item) { variable_item_set_current_value_text(item, uart_ch[index]); if(index == 0) - app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1; + app->usb_uart_cfg->uart_ch = FuriHalSerialIdUsart; else if(index == 1) - app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1; + app->usb_uart_cfg->uart_ch = FuriHalSerialIdLpuart; line_ensure_flow_invariant(app); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 366c5cdc4..8dff09cb8 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -29,17 +29,18 @@ typedef enum { WorkerEvtTxStop = (1 << 2), WorkerEvtCdcRx = (1 << 3), + WorkerEvtCdcTxComplete = (1 << 4), - WorkerEvtCfgChange = (1 << 4), + WorkerEvtCfgChange = (1 << 5), - WorkerEvtLineCfgSet = (1 << 5), - WorkerEvtCtrlLineSet = (1 << 6), + WorkerEvtLineCfgSet = (1 << 6), + WorkerEvtCtrlLineSet = (1 << 7), } WorkerEvtFlags; #define WORKER_ALL_RX_EVENTS \ (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \ - WorkerEvtCtrlLineSet) + WorkerEvtCtrlLineSet | WorkerEvtCdcTxComplete) #define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx) struct UsbUartBridge { @@ -50,6 +51,7 @@ struct UsbUartBridge { FuriThread* tx_thread; FuriStreamBuffer* rx_stream; + FuriHalSerialHandle* serial_handle; FuriMutex* usb_mutex; @@ -80,11 +82,23 @@ static const CdcCallbacks cdc_cb = { static int32_t usb_uart_tx_thread(void* context); -static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { +static void usb_uart_on_irq_rx_dma_cb( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent ev, + size_t size, + void* context) { UsbUartBridge* usb_uart = (UsbUartBridge*)context; - if(ev == UartIrqEventRXNE) { - furi_stream_buffer_send(usb_uart->rx_stream, &data, 1, 0); + if(ev & (FuriHalSerialRxEventData | FuriHalSerialRxEventIdle)) { + uint8_t data[FURI_HAL_SERIAL_DMA_BUFFER_SIZE] = {0}; + while(size) { + size_t ret = furi_hal_serial_dma_rx( + handle, + data, + (size > FURI_HAL_SERIAL_DMA_BUFFER_SIZE) ? FURI_HAL_SERIAL_DMA_BUFFER_SIZE : size); + furi_stream_buffer_send(usb_uart->rx_stream, data, ret, 0); + size -= ret; + }; furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone); } } @@ -116,32 +130,33 @@ static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { } static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) { - if(uart_ch == FuriHalUartIdUSART1) { - furi_hal_console_disable(); - } else if(uart_ch == FuriHalUartIdLPUART1) { - furi_hal_uart_init(uart_ch, 115200); - } - furi_hal_uart_set_irq_cb(uart_ch, usb_uart_on_irq_cb, usb_uart); + furi_assert(!usb_uart->serial_handle); + + usb_uart->serial_handle = furi_hal_serial_control_acquire(uart_ch); + furi_assert(usb_uart->serial_handle); + + furi_hal_serial_init(usb_uart->serial_handle, 115200); + furi_hal_serial_dma_rx_start( + usb_uart->serial_handle, usb_uart_on_irq_rx_dma_cb, usb_uart, false); } -static void usb_uart_serial_deinit(UsbUartBridge* usb_uart, uint8_t uart_ch) { - UNUSED(usb_uart); - furi_hal_uart_set_irq_cb(uart_ch, NULL, NULL); - if(uart_ch == FuriHalUartIdUSART1) - furi_hal_console_enable(); - else if(uart_ch == FuriHalUartIdLPUART1) - furi_hal_uart_deinit(uart_ch); +static void usb_uart_serial_deinit(UsbUartBridge* usb_uart) { + furi_assert(usb_uart->serial_handle); + + furi_hal_serial_deinit(usb_uart->serial_handle); + furi_hal_serial_control_release(usb_uart->serial_handle); + usb_uart->serial_handle = NULL; } static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) { if(baudrate != 0) { - furi_hal_uart_set_br(usb_uart->cfg.uart_ch, baudrate); + furi_hal_serial_set_br(usb_uart->serial_handle, baudrate); usb_uart->st.baudrate_cur = baudrate; } else { struct usb_cdc_line_coding* line_cfg = furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch); if(line_cfg->dwDTERate > 0) { - furi_hal_uart_set_br(usb_uart->cfg.uart_ch, line_cfg->dwDTERate); + furi_hal_serial_set_br(usb_uart->serial_handle, line_cfg->dwDTERate); usb_uart->st.baudrate_cur = line_cfg->dwDTERate; } } @@ -191,7 +206,7 @@ static int32_t usb_uart_worker(void* context) { furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); furi_check(!(events & FuriFlagError)); if(events & WorkerEvtStop) break; - if(events & WorkerEvtRxDone) { + if(events & (WorkerEvtRxDone | WorkerEvtCdcTxComplete)) { size_t len = furi_stream_buffer_receive( usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0); if(len > 0) { @@ -223,7 +238,7 @@ static int32_t usb_uart_worker(void* context) { furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); furi_thread_join(usb_uart->tx_thread); - usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + usb_uart_serial_deinit(usb_uart); usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch); usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch; @@ -274,7 +289,7 @@ static int32_t usb_uart_worker(void* context) { } } usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); - usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + usb_uart_serial_deinit(usb_uart); furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); @@ -320,18 +335,10 @@ static int32_t usb_uart_tx_thread(void* context) { if(usb_uart->cfg.software_de_re != 0) furi_hal_gpio_write(USB_USART_DE_RE_PIN, false); - furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); + furi_hal_serial_tx(usb_uart->serial_handle, data, len); if(usb_uart->cfg.software_de_re != 0) { - //TODO: FL-3276 port to new USART API - if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) { - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - } else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) { - while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) - ; - } - + furi_hal_serial_tx_wait_complete(usb_uart->serial_handle); furi_hal_gpio_write(USB_USART_DE_RE_PIN, true); } } @@ -345,6 +352,7 @@ static int32_t usb_uart_tx_thread(void* context) { static void vcp_on_cdc_tx_complete(void* context) { UsbUartBridge* usb_uart = (UsbUartBridge*)context; furi_semaphore_release(usb_uart->tx_sem); + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCdcTxComplete); } static void vcp_on_cdc_rx(void* context) { diff --git a/applications/main/u2f/u2f_hid.c b/applications/main/u2f/u2f_hid.c index d7d7e6cf4..83c8a575f 100644 --- a/applications/main/u2f/u2f_hid.c +++ b/applications/main/u2f/u2f_hid.c @@ -8,8 +8,6 @@ #include #include -#include - #define TAG "U2fHid" #define WORKER_TAG TAG "Worker" diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 2c40fdc16..4f93e08e6 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -221,7 +221,12 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { furi_log_level_to_string(furi_log_get_level(), ¤t_level); printf("Current log level: %s\r\n", current_level); - furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring); + FuriLogHandler log_handler = { + .callback = cli_command_log_tx_callback, + .context = ring, + }; + + furi_log_add_handler(log_handler); printf("Use to list available log levels\r\n"); printf("Press CTRL+C to stop...\r\n"); @@ -230,7 +235,7 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { cli_write(cli, buffer, ret); } - furi_hal_console_set_tx_callback(NULL, NULL); + furi_log_remove_handler(log_handler); if(restore_log_level) { // There will be strange behaviour if log level is set from settings while log command is running diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index e6118344b..72036a647 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -24,12 +24,56 @@ const uint32_t log_level_value[] = { }; static void log_level_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, log_level_text[index]); furi_hal_rtc_set_log_level(log_level_value[index]); } +const char* const log_device_text[] = { + "USART", + "LPUART", + "None", +}; + +const uint32_t log_device_value[] = { + FuriHalRtcLogDeviceUsart, + FuriHalRtcLogDeviceLpuart, + FuriHalRtcLogDeviceNone}; + +static void log_device_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, log_device_text[index]); + furi_hal_rtc_set_log_device(log_device_value[index]); +} + +const char* const log_baud_rate_text[] = { + "9600", + "38400", + "57600", + "115200", + "230400", + "460800", + "921600", + "1843200", +}; + +const uint32_t log_baud_rate_value[] = { + FuriHalRtcLogBaudRate9600, + FuriHalRtcLogBaudRate38400, + FuriHalRtcLogBaudRate57600, + FuriHalRtcLogBaudRate115200, + FuriHalRtcLogBaudRate230400, + FuriHalRtcLogBaudRate460800, + FuriHalRtcLogBaudRate921600, + FuriHalRtcLogBaudRate1843200, +}; + +static void log_baud_rate_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, log_baud_rate_text[index]); + furi_hal_rtc_set_log_baud_rate(log_baud_rate_value[index]); +} + const char* const debug_text[] = { "OFF", "ON", @@ -64,7 +108,6 @@ const uint32_t heap_trace_mode_value[] = { }; static void heap_trace_mode_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, heap_trace_mode_text[index]); furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); @@ -80,8 +123,7 @@ const uint32_t measurement_units_value[] = { LocaleMeasurementUnitsImperial, }; -static void measurement_units_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); +static void mesurement_units_changed(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, measurement_units_text[index]); locale_set_measurement_unit(measurement_units_value[index]); @@ -98,7 +140,6 @@ const uint32_t time_format_value[] = { }; static void time_format_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, time_format_text[index]); locale_set_time_format(time_format_value[index]); @@ -117,7 +158,6 @@ const uint32_t date_format_value[] = { }; static void date_format_changed(VariableItem* item) { - // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, date_format_text[index]); locale_set_date_format(date_format_value[index]); @@ -227,6 +267,24 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, log_level_text[value_index]); + item = variable_item_list_add( + app->var_item_list, "Log Device", COUNT_OF(log_device_text), log_device_changed, app); + value_index = value_index_uint32( + furi_hal_rtc_get_log_device(), log_device_value, COUNT_OF(log_device_text)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, log_device_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, + "Log Baud Rate", + COUNT_OF(log_baud_rate_text), + log_baud_rate_changed, + app); + value_index = value_index_uint32( + furi_hal_rtc_get_log_baud_rate(), log_baud_rate_value, COUNT_OF(log_baud_rate_text)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, log_baud_rate_text[value_index]); + item = variable_item_list_add( app->var_item_list, "Debug", COUNT_OF(debug_text), debug_changed, app); value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0; diff --git a/documentation/FuriHalBus.md b/documentation/FuriHalBus.md index 230a98050..7880c041f 100644 --- a/documentation/FuriHalBus.md +++ b/documentation/FuriHalBus.md @@ -58,7 +58,7 @@ When not using the API, these peripherals MUST be enabled by the user code and t | SPI2 | -- | | I2C1 | `furi_hal_i2c.h` | | I2C3 | -- | -| USART1 | `furi_hal_uart.h` | +| USART1 | `furi_hal_serial.h` | | LPUART1 | -- | | USB | `furi_hal_usb.h` | @@ -102,8 +102,8 @@ Below is the list of DMA channels and their usage by the system. | -- | 3 | | | | -- | 4 | yes | pulse reader | | -- | 5 | | | -| -- | 6 | | | -| -- | 7 | | | +| -- | 6 | yes | USART_Rx | +| -- | 7 | yes | LPUART_Rx | | DMA2 | 1 | yes | infrared, lfrfid, subghz, | | -- | 2 | yes | -- | | -- | 3 | yes | cc1101_ext | diff --git a/furi/core/check.c b/furi/core/check.c index b56db6563..233b574b0 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -2,7 +2,6 @@ #include "common_defines.h" #include -#include #include #include #include @@ -59,69 +58,69 @@ extern size_t xPortGetTotalHeapSize(void); static void __furi_put_uint32_as_text(uint32_t data) { char tmp_str[] = "-2147483648"; itoa(data, tmp_str, 10); - furi_hal_console_puts(tmp_str); + furi_log_puts(tmp_str); } static void __furi_put_uint32_as_hex(uint32_t data) { char tmp_str[] = "0xFFFFFFFF"; itoa(data, tmp_str, 16); - furi_hal_console_puts(tmp_str); + furi_log_puts(tmp_str); } static void __furi_print_register_info() { // Print registers for(uint8_t i = 0; i < 12; i++) { - furi_hal_console_puts("\r\n\tr"); + furi_log_puts("\r\n\tr"); __furi_put_uint32_as_text(i); - furi_hal_console_puts(" : "); + furi_log_puts(" : "); __furi_put_uint32_as_hex(__furi_check_registers[i]); } - furi_hal_console_puts("\r\n\tlr : "); + furi_log_puts("\r\n\tlr : "); __furi_put_uint32_as_hex(__furi_check_registers[12]); } static void __furi_print_stack_info() { - furi_hal_console_puts("\r\n\tstack watermark: "); + furi_log_puts("\r\n\tstack watermark: "); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); } static void __furi_print_bt_stack_info() { const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info(); if(fault_info == NULL) { - furi_hal_console_puts("\r\n\tcore2: not faulted"); + furi_log_puts("\r\n\tcore2: not faulted"); } else { - furi_hal_console_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); + furi_log_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); __furi_put_uint32_as_hex(fault_info->source_pc); - furi_hal_console_puts("\r\n\tLR: "); + furi_log_puts("\r\n\tLR: "); __furi_put_uint32_as_hex(fault_info->source_lr); - furi_hal_console_puts("\r\n\tSP: "); + furi_log_puts("\r\n\tSP: "); __furi_put_uint32_as_hex(fault_info->source_sp); } } static void __furi_print_heap_info() { - furi_hal_console_puts("\r\n\t heap total: "); + furi_log_puts("\r\n\t heap total: "); __furi_put_uint32_as_text(xPortGetTotalHeapSize()); - furi_hal_console_puts("\r\n\t heap free: "); + furi_log_puts("\r\n\t heap free: "); __furi_put_uint32_as_text(xPortGetFreeHeapSize()); - furi_hal_console_puts("\r\n\t heap watermark: "); + furi_log_puts("\r\n\t heap watermark: "); __furi_put_uint32_as_text(xPortGetMinimumEverFreeHeapSize()); } static void __furi_print_name(bool isr) { if(isr) { - furi_hal_console_puts("[ISR "); + furi_log_puts("[ISR "); __furi_put_uint32_as_text(__get_IPSR()); - furi_hal_console_puts("] "); + furi_log_puts("] "); } else { const char* name = pcTaskGetName(NULL); if(name == NULL) { - furi_hal_console_puts("[main] "); + furi_log_puts("[main] "); } else { - furi_hal_console_puts("["); - furi_hal_console_puts(name); - furi_hal_console_puts("] "); + furi_log_puts("["); + furi_log_puts(name); + furi_log_puts("] "); } } } @@ -140,9 +139,9 @@ FURI_NORETURN void __furi_crash_implementation() { __furi_check_message = "furi_check failed"; } - furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); + furi_log_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); - furi_hal_console_puts(__furi_check_message); + furi_log_puts(__furi_check_message); __furi_print_register_info(); if(!isr) { @@ -157,8 +156,8 @@ FURI_NORETURN void __furi_crash_implementation() { #ifdef FURI_NDEBUG if(debug) { #endif - furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts("\r\nSystem halted. Connect debugger for more info\r\n"); + furi_log_puts("\033[0m\r\n"); furi_hal_debug_enable(); RESTORE_REGISTERS_AND_HALT_MCU(debug); @@ -169,8 +168,8 @@ FURI_NORETURN void __furi_crash_implementation() { ptr = (uint32_t) "Check serial logs"; } furi_hal_rtc_set_fault_data(ptr); - furi_hal_console_puts("\r\nRebooting system.\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts("\r\nRebooting system.\r\n"); + furi_log_puts("\033[0m\r\n"); furi_hal_power_reset(); } #endif @@ -187,11 +186,11 @@ FURI_NORETURN void __furi_halt_implementation() { __furi_check_message = "System halt requested."; } - furi_hal_console_puts("\r\n\033[0;31m[HALT]"); + furi_log_puts("\r\n\033[0;31m[HALT]"); __furi_print_name(isr); - furi_hal_console_puts(__furi_check_message); - furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); - furi_hal_console_puts("\033[0m\r\n"); + furi_log_puts(__furi_check_message); + furi_log_puts("\r\nSystem halted. Bye-bye!\r\n"); + furi_log_puts("\033[0m\r\n"); // Check if debug enabled by DAP // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en diff --git a/furi/core/log.c b/furi/core/log.c index 53467ecdb..4de850d6b 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -2,17 +2,19 @@ #include "check.h" #include "mutex.h" #include +#include + +LIST_DEF(FuriLogHandlersList, FuriLogHandler, M_POD_OPLIST) #define FURI_LOG_LEVEL_DEFAULT FuriLogLevelInfo typedef struct { FuriLogLevel log_level; - FuriLogPuts puts; - FuriLogTimestamp timestamp; FuriMutex* mutex; + FuriLogHandlersList_t tx_handlers; } FuriLogParams; -static FuriLogParams furi_log; +static FuriLogParams furi_log = {0}; typedef struct { const char* str; @@ -32,9 +34,77 @@ static const FuriLogLevelDescription FURI_LOG_LEVEL_DESCRIPTIONS[] = { void furi_log_init() { // Set default logging parameters furi_log.log_level = FURI_LOG_LEVEL_DEFAULT; - furi_log.puts = furi_hal_console_puts; - furi_log.timestamp = furi_get_tick; - furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + furi_log.mutex = furi_mutex_alloc(FuriMutexTypeRecursive); + FuriLogHandlersList_init(furi_log.tx_handlers); +} + +bool furi_log_add_handler(FuriLogHandler handler) { + furi_check(handler.callback); + + bool ret = true; + + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) { + ret = false; + } else { + FuriLogHandlersList_next(it); + } + } + + if(ret) { + FuriLogHandlersList_push_back(furi_log.tx_handlers, handler); + } + + furi_mutex_release(furi_log.mutex); + + return ret; +} + +bool furi_log_remove_handler(FuriLogHandler handler) { + bool ret = false; + + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + if(memcmp(FuriLogHandlersList_ref(it), &handler, sizeof(FuriLogHandler)) == 0) { + FuriLogHandlersList_remove(furi_log.tx_handlers, it); + ret = true; + } else { + FuriLogHandlersList_next(it); + } + } + + furi_mutex_release(furi_log.mutex); + + return ret; +} + +void furi_log_tx(const uint8_t* data, size_t size) { + if(!FURI_IS_ISR()) { + furi_check(furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk); + } else { + if(furi_mutex_get_owner(furi_log.mutex)) return; + } + + FuriLogHandlersList_it_t it; + FuriLogHandlersList_it(it, furi_log.tx_handlers); + while(!FuriLogHandlersList_end_p(it)) { + FuriLogHandlersList_ref(it)->callback(data, size, FuriLogHandlersList_ref(it)->context); + FuriLogHandlersList_next(it); + } + + if(!FURI_IS_ISR()) furi_mutex_release(furi_log.mutex); +} + +void furi_log_puts(const char* data) { + furi_check(data); + furi_log_tx((const uint8_t*)data, strlen(data)); } void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) { @@ -72,13 +142,8 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form // Timestamp furi_string_printf( - string, - "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, - furi_log.timestamp(), - color, - log_letter, - tag); - furi_log.puts(furi_string_get_cstr(string)); + string, "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, furi_get_tick(), color, log_letter, tag); + furi_log_puts(furi_string_get_cstr(string)); furi_string_reset(string); va_list args; @@ -86,10 +151,10 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form furi_string_vprintf(string, format, args); va_end(args); - furi_log.puts(furi_string_get_cstr(string)); + furi_log_puts(furi_string_get_cstr(string)); furi_string_free(string); - furi_log.puts("\r\n"); + furi_log_puts("\r\n"); furi_mutex_release(furi_log.mutex); } @@ -105,7 +170,7 @@ void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) { furi_string_vprintf(string, format, args); va_end(args); - furi_log.puts(furi_string_get_cstr(string)); + furi_log_puts(furi_string_get_cstr(string)); furi_string_free(string); furi_mutex_release(furi_log.mutex); @@ -123,16 +188,6 @@ FuriLogLevel furi_log_get_level(void) { return furi_log.log_level; } -void furi_log_set_puts(FuriLogPuts puts) { - furi_assert(puts); - furi_log.puts = puts; -} - -void furi_log_set_timestamp(FuriLogTimestamp timestamp) { - furi_assert(timestamp); - furi_log.timestamp = timestamp; -} - bool furi_log_level_to_string(FuriLogLevel level, const char** str) { for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) { if(level == FURI_LOG_LEVEL_DESCRIPTIONS[i].level) { diff --git a/furi/core/log.h b/furi/core/log.h index 5d11add9b..a587d8ab2 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -39,11 +39,44 @@ typedef enum { #define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE) #define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE) -typedef void (*FuriLogPuts)(const char* data); -typedef uint32_t (*FuriLogTimestamp)(void); +typedef void (*FuriLogHandlerCallback)(const uint8_t* data, size_t size, void* context); + +typedef struct { + FuriLogHandlerCallback callback; + void* context; +} FuriLogHandler; /** Initialize logging */ -void furi_log_init(); +void furi_log_init(void); + +/** Add log TX callback + * + * @param[in] callback The callback + * + * @return true on success, false otherwise + */ +bool furi_log_add_handler(FuriLogHandler handler); + +/** Remove log TX callback + * + * @param[in] callback The callback + * + * @return true on success, false otherwise + */ +bool furi_log_remove_handler(FuriLogHandler handler); + +/** Transmit data through log IO callbacks + * + * @param[in] data The data + * @param[in] size The size + */ +void furi_log_tx(const uint8_t* data, size_t size); + +/** Transmit data through log IO callbacks + * + * @param[in] data The data, null-terminated C-string + */ +void furi_log_puts(const char* data); /** Print log record * @@ -74,19 +107,7 @@ void furi_log_set_level(FuriLogLevel level); * * @return The furi log level. */ -FuriLogLevel furi_log_get_level(); - -/** Set log output callback - * - * @param[in] puts The puts callback - */ -void furi_log_set_puts(FuriLogPuts puts); - -/** Set timestamp callback - * - * @param[in] timestamp The timestamp callback - */ -void furi_log_set_timestamp(FuriLogTimestamp timestamp); +FuriLogLevel furi_log_get_level(void); /** Log level to string * diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 826772a67..b83b4212f 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining @@ -52,6 +52,10 @@ task.h is included from an application file. */ #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#ifdef HEAP_PRINT_DEBUG +#error This feature is broken, logging transport must be replaced with RTT +#endif + #if(configSUPPORT_DYNAMIC_ALLOCATION == 0) #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 #endif @@ -286,13 +290,13 @@ static void print_heap_init() { // {PHStart|heap_start|heap_end} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{PHStart|"); + furi_log_puts("{PHStart|"); ultoa(heap_start, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("|"); + furi_log_puts(tmp_str); + furi_log_puts("|"); ultoa(heap_end, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } @@ -305,15 +309,15 @@ static void print_heap_malloc(void* ptr, size_t size) { // {thread name|m|address|size} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{"); - furi_hal_console_puts(name); - furi_hal_console_puts("|m|0x"); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|m|0x"); ultoa((unsigned long)ptr, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("|"); + furi_log_puts(tmp_str); + furi_log_puts("|"); utoa(size, tmp_str, 10); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } @@ -326,12 +330,12 @@ static void print_heap_free(void* ptr) { // {thread name|f|address} FURI_CRITICAL_ENTER(); - furi_hal_console_puts("{"); - furi_hal_console_puts(name); - furi_hal_console_puts("|f|0x"); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|f|0x"); ultoa((unsigned long)ptr, tmp_str, 16); - furi_hal_console_puts(tmp_str); - furi_hal_console_puts("}\r\n"); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); FURI_CRITICAL_EXIT(); } #endif diff --git a/furi/core/mutex.c b/furi/core/mutex.c index 8794e10dc..f18fb1681 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -114,8 +114,10 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { + if((hMutex == NULL)) { owner = 0; + } else if(FURI_IS_IRQ_MODE()) { + owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex); } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); } diff --git a/furi/core/thread.c b/furi/core/thread.c index db4feeb4e..abc85bb90 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -9,7 +9,6 @@ #include "log.h" #include -#include #include #include @@ -570,7 +569,7 @@ static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, s if(thread->output.write_callback != NULL) { thread->output.write_callback(data, size); } else { - furi_hal_console_tx((const uint8_t*)data, size); + furi_log_tx((const uint8_t*)data, size); } return size; } diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c index 5ec0fb962..c17b0ae79 100644 --- a/lib/toolbox/value_index.c +++ b/lib/toolbox/value_index.c @@ -1,52 +1,55 @@ #include "value_index.h" +#include -uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) { - int64_t last_value = INT64_MIN; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value) && (value <= values[i])) { - index = i; - break; - } - last_value = values[i]; - } - return index; -} +size_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count) { + size_t index = 0; -uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) { - int64_t last_value = INT64_MIN; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value) && (value <= values[i])) { - index = i; - break; - } - last_value = values[i]; - } - return index; -} - -uint8_t value_index_float(const float value, const float values[], uint8_t values_count) { - const float epsilon = 0.01f; - float last_value = values[0]; - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) { - index = i; - break; - } - last_value = values[i]; - } - return index; -} - -uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count) { - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { + for(size_t i = 0; i < values_count; i++) { if(value == values[i]) { index = i; break; } } + + return index; +} + +size_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + if(value == values[i]) { + index = i; + break; + } + } + + return index; +} + +size_t value_index_float(const float value, const float values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + const float epsilon = fabsf(values[i] * 0.01f); + if(fabsf(values[i] - value) <= epsilon) { + index = i; + break; + } + } + + return index; +} + +size_t value_index_bool(const bool value, const bool values[], size_t values_count) { + size_t index = 0; + + for(size_t i = 0; i < values_count; i++) { + if(value == values[i]) { + index = i; + break; + } + } + return index; } diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h index 5aa768e3d..bcd3024ac 100644 --- a/lib/toolbox/value_index.h +++ b/lib/toolbox/value_index.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -18,7 +19,7 @@ extern "C" { * * @return value's index. */ -uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count); +size_t value_index_int32(const int32_t value, const int32_t values[], size_t values_count); /** Get the index of a uint32_t array element which is closest to the given value. * @@ -31,7 +32,7 @@ uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t v * * @return value's index. */ -uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count); +size_t value_index_uint32(const uint32_t value, const uint32_t values[], size_t values_count); /** Get the index of a float array element which is closest to the given value. * @@ -44,7 +45,7 @@ uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_ * * @return value's index. */ -uint8_t value_index_float(const float value, const float values[], uint8_t values_count); +size_t value_index_float(const float value, const float values[], size_t values_count); /** Get the index of a bool array element which is equal to the given value. * @@ -57,7 +58,7 @@ uint8_t value_index_float(const float value, const float values[], uint8_t value * * @return value's index. */ -uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count); +size_t value_index_bool(const bool value, const bool values[], size_t values_count); #ifdef __cplusplus } diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 1a09a4768..960cee658 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.2,, +Version,+,51.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -160,7 +160,6 @@ Header,+,targets/f18/furi_hal/furi_hal_spi_config.h,, Header,+,targets/f18/furi_hal/furi_hal_target_hw.h,, Header,+,targets/f7/furi_hal/furi_hal_bus.h,, Header,+,targets/f7/furi_hal/furi_hal_clock.h,, -Header,+,targets/f7/furi_hal/furi_hal_console.h,, Header,+,targets/f7/furi_hal/furi_hal_dma.h,, Header,+,targets/f7/furi_hal/furi_hal_flash.h,, Header,+,targets/f7/furi_hal/furi_hal_gpio.h,, @@ -170,8 +169,11 @@ Header,+,targets/f7/furi_hal/furi_hal_idle_timer.h,, Header,+,targets/f7/furi_hal/furi_hal_interrupt.h,, Header,+,targets/f7/furi_hal/furi_hal_os.h,, Header,+,targets/f7/furi_hal/furi_hal_pwm.h,, +Header,+,targets/f7/furi_hal/furi_hal_rtc.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, -Header,+,targets/f7/furi_hal/furi_hal_uart.h,, Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,targets/f7/platform_specific/intrinsic_export.h,, Header,+,targets/f7/platform_specific/math_wrapper.h,, @@ -190,7 +192,6 @@ Header,+,targets/furi_hal_include/furi_hal_mpu.h,, Header,+,targets/furi_hal_include/furi_hal_power.h,, Header,+,targets/furi_hal_include/furi_hal_random.h,, Header,+,targets/furi_hal_include/furi_hal_region.h,, -Header,+,targets/furi_hal_include/furi_hal_rtc.h,, Header,+,targets/furi_hal_include/furi_hal_sd.h,, Header,+,targets/furi_hal_include/furi_hal_speaker.h,, Header,+,targets/furi_hal_include/furi_hal_spi.h,, @@ -1057,14 +1058,6 @@ Function,-,furi_hal_clock_switch_hse2hsi,void, Function,-,furi_hal_clock_switch_hse2pll,_Bool, Function,-,furi_hal_clock_switch_hsi2hse,void, Function,-,furi_hal_clock_switch_pll2hse,_Bool, -Function,+,furi_hal_console_disable,void, -Function,+,furi_hal_console_enable,void, -Function,+,furi_hal_console_init,void, -Function,+,furi_hal_console_printf,void,"const char[], ..." -Function,+,furi_hal_console_puts,void,const char* -Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" -Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" -Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize" Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp Function,+,furi_hal_cortex_delay_us,void,uint32_t @@ -1239,6 +1232,8 @@ Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate, +Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister @@ -1257,6 +1252,8 @@ Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate +Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1271,6 +1268,26 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId +Function,+,furi_hal_serial_control_deinit,void, +Function,+,furi_hal_serial_control_init,void, +Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" +Function,+,furi_hal_serial_control_suspend,void, +Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" +Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" +Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" +Function,+,furi_hal_serial_tx_wait_complete,void,FuriHalSerialHandle* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, @@ -1294,13 +1311,6 @@ Function,-,furi_hal_spi_config_init_early,void, Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,+,furi_hal_switch,void,void* -Function,+,furi_hal_uart_deinit,void,FuriHalUartId -Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_resume,void,FuriHalUartId -Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" -Function,+,furi_hal_uart_suspend,void,FuriHalUartId -Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" Function,+,furi_hal_usb_disable,void, Function,+,furi_hal_usb_enable,void, Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, @@ -1346,15 +1356,17 @@ Function,+,furi_kernel_is_running,_Bool, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_add_handler,_Bool,FuriLogHandler Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." +Function,+,furi_log_puts,void,const char* +Function,+,furi_log_remove_handler,_Bool,FuriLogHandler Function,+,furi_log_set_level,void,FuriLogLevel -Function,-,furi_log_set_puts,void,FuriLogPuts -Function,-,furi_log_set_timestamp,void,FuriLogTimestamp +Function,+,furi_log_tx,void,"const uint8_t*, size_t" Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" Function,+,furi_message_queue_free,void,FuriMessageQueue* Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" @@ -2418,10 +2430,10 @@ Function,-,utoa,char*,"unsigned, char*, int" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* -Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" -Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" -Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" -Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,value_index_bool,size_t,"const _Bool, const _Bool[], size_t" +Function,+,value_index_float,size_t,"const float, const float[], size_t" +Function,+,value_index_int32,size_t,"const int32_t, const int32_t[], size_t" +Function,+,value_index_uint32,size_t,"const uint32_t, const uint32_t[], size_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" diff --git a/targets/f18/furi_hal/furi_hal.c b/targets/f18/furi_hal/furi_hal.c index 5f4e6165d..957d9d673 100644 --- a/targets/f18/furi_hal/furi_hal.c +++ b/targets/f18/furi_hal/furi_hal.c @@ -33,7 +33,7 @@ void furi_hal_init() { furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_random_init(); - furi_hal_console_init(); + furi_hal_serial_control_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); furi_hal_flash_init(); diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 3f3915594..ddfbf9b63 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,50.2,, +Version,+,51.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -223,7 +223,6 @@ Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Header,+,targets/f7/furi_hal/furi_hal_bus.h,, Header,+,targets/f7/furi_hal/furi_hal_clock.h,, -Header,+,targets/f7/furi_hal/furi_hal_console.h,, Header,+,targets/f7/furi_hal/furi_hal_dma.h,, Header,+,targets/f7/furi_hal/furi_hal_flash.h,, Header,+,targets/f7/furi_hal/furi_hal_gpio.h,, @@ -236,11 +235,14 @@ Header,+,targets/f7/furi_hal/furi_hal_os.h,, Header,+,targets/f7/furi_hal/furi_hal_pwm.h,, Header,+,targets/f7/furi_hal/furi_hal_resources.h,, Header,+,targets/f7/furi_hal/furi_hal_rfid.h,, +Header,+,targets/f7/furi_hal/furi_hal_rtc.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_control.h,, +Header,+,targets/f7/furi_hal/furi_hal_serial_types.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_config.h,, Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, Header,+,targets/f7/furi_hal/furi_hal_subghz.h,, Header,+,targets/f7/furi_hal/furi_hal_target_hw.h,, -Header,+,targets/f7/furi_hal/furi_hal_uart.h,, Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,targets/f7/platform_specific/intrinsic_export.h,, Header,+,targets/f7/platform_specific/math_wrapper.h,, @@ -261,7 +263,6 @@ Header,+,targets/furi_hal_include/furi_hal_nfc.h,, Header,+,targets/furi_hal_include/furi_hal_power.h,, Header,+,targets/furi_hal_include/furi_hal_random.h,, Header,+,targets/furi_hal_include/furi_hal_region.h,, -Header,+,targets/furi_hal_include/furi_hal_rtc.h,, Header,+,targets/furi_hal_include/furi_hal_sd.h,, Header,+,targets/furi_hal_include/furi_hal_speaker.h,, Header,+,targets/furi_hal_include/furi_hal_spi.h,, @@ -1174,14 +1175,6 @@ Function,-,furi_hal_clock_switch_hse2hsi,void, Function,-,furi_hal_clock_switch_hse2pll,_Bool, Function,-,furi_hal_clock_switch_hsi2hse,void, Function,-,furi_hal_clock_switch_pll2hse,_Bool, -Function,+,furi_hal_console_disable,void, -Function,+,furi_hal_console_enable,void, -Function,+,furi_hal_console_init,void, -Function,+,furi_hal_console_printf,void,"const char[], ..." -Function,+,furi_hal_console_puts,void,const char* -Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" -Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" -Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize" Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp Function,+,furi_hal_cortex_delay_us,void,uint32_t @@ -1434,6 +1427,8 @@ Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate, +Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister @@ -1452,6 +1447,8 @@ Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate +Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1466,6 +1463,26 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId +Function,+,furi_hal_serial_control_deinit,void, +Function,+,furi_hal_serial_control_init,void, +Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" +Function,+,furi_hal_serial_control_suspend,void, +Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" +Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" +Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" +Function,+,furi_hal_serial_tx_wait_complete,void,FuriHalSerialHandle* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, @@ -1524,13 +1541,6 @@ Function,+,furi_hal_subghz_stop_async_tx,void, Function,+,furi_hal_subghz_tx,_Bool, Function,+,furi_hal_subghz_write_packet,void,"const uint8_t*, uint8_t" Function,+,furi_hal_switch,void,void* -Function,+,furi_hal_uart_deinit,void,FuriHalUartId -Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_resume,void,FuriHalUartId -Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" -Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" -Function,+,furi_hal_uart_suspend,void,FuriHalUartId -Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" Function,+,furi_hal_usb_disable,void, Function,+,furi_hal_usb_enable,void, Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, @@ -1580,15 +1590,17 @@ Function,+,furi_kernel_is_running,_Bool, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_add_handler,_Bool,FuriLogHandler Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." +Function,+,furi_log_puts,void,const char* +Function,+,furi_log_remove_handler,_Bool,FuriLogHandler Function,+,furi_log_set_level,void,FuriLogLevel -Function,-,furi_log_set_puts,void,FuriLogPuts -Function,-,furi_log_set_timestamp,void,FuriLogTimestamp +Function,+,furi_log_tx,void,"const uint8_t*, size_t" Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" Function,+,furi_message_queue_free,void,FuriMessageQueue* Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" @@ -3274,10 +3286,10 @@ Function,-,utoa,char*,"unsigned, char*, int" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* -Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" -Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" -Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" -Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,value_index_bool,size_t,"const _Bool, const _Bool[], size_t" +Function,+,value_index_float,size_t,"const float, const float[], size_t" +Function,+,value_index_int32,size_t,"const int32_t, const int32_t[], size_t" +Function,+,value_index_uint32,size_t,"const uint32_t, const uint32_t[], size_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" diff --git a/targets/f7/furi_hal/furi_hal.c b/targets/f7/furi_hal/furi_hal.c index 691729ccf..9054f4df5 100644 --- a/targets/f7/furi_hal/furi_hal.c +++ b/targets/f7/furi_hal/furi_hal.c @@ -33,7 +33,7 @@ void furi_hal_init() { furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_random_init(); - furi_hal_console_init(); + furi_hal_serial_control_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); furi_hal_flash_init(); diff --git a/targets/f7/furi_hal/furi_hal_console.c b/targets/f7/furi_hal/furi_hal_console.c deleted file mode 100644 index 0b113d2da..000000000 --- a/targets/f7/furi_hal/furi_hal_console.c +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include - -#include -#include -#include - -#include - -#define TAG "FuriHalConsole" - -#ifdef HEAP_PRINT_DEBUG -#define CONSOLE_BAUDRATE 1843200 -#else -#define CONSOLE_BAUDRATE 230400 -#endif - -typedef struct { - bool alive; - FuriHalConsoleTxCallback tx_callback; - void* tx_callback_context; -} FuriHalConsole; - -FuriHalConsole furi_hal_console = { - .alive = false, - .tx_callback = NULL, - .tx_callback_context = NULL, -}; - -void furi_hal_console_init() { - furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); - furi_hal_console.alive = true; -} - -void furi_hal_console_enable() { - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); - furi_hal_console.alive = true; -} - -void furi_hal_console_disable() { - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - furi_hal_console.alive = false; -} - -void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context) { - FURI_CRITICAL_ENTER(); - furi_hal_console.tx_callback = callback; - furi_hal_console.tx_callback_context = context; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { - if(!furi_hal_console.alive) return; - - FURI_CRITICAL_ENTER(); - // Transmit data - - if(furi_hal_console.tx_callback) { - furi_hal_console.tx_callback(buffer, buffer_size, furi_hal_console.tx_callback_context); - } - - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); - // Wait for TC flag to be raised for last char - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) { - if(!furi_hal_console.alive) return; - - FURI_CRITICAL_ENTER(); - // Transmit data - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); - // Transmit new line symbols - furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2); - // Wait for TC flag to be raised for last char - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_console_printf(const char format[], ...) { - FuriString* string; - va_list args; - va_start(args, format); - string = furi_string_alloc_vprintf(format, args); - va_end(args); - furi_hal_console_tx((const uint8_t*)furi_string_get_cstr(string), furi_string_size(string)); - furi_string_free(string); -} - -void furi_hal_console_puts(const char* data) { - furi_hal_console_tx((const uint8_t*)data, strlen(data)); -} diff --git a/targets/f7/furi_hal/furi_hal_console.h b/targets/f7/furi_hal/furi_hal_console.h deleted file mode 100644 index ce31a66b3..000000000 --- a/targets/f7/furi_hal/furi_hal_console.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*FuriHalConsoleTxCallback)(const uint8_t* buffer, size_t size, void* context); - -void furi_hal_console_init(); - -void furi_hal_console_enable(); - -void furi_hal_console_disable(); - -void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context); - -void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); - -void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size); - -/** - * Printf-like plain uart interface - * @warning Will not work in ISR context - * @param format - * @param ... - */ -void furi_hal_console_printf(const char format[], ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); - -void furi_hal_console_puts(const char* data); - -#ifdef __cplusplus -} -#endif diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index 889ddc56c..6410b1090 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -59,6 +59,12 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = { // LPTIMx [FuriHalInterruptIdLpTim1] = LPTIM1_IRQn, [FuriHalInterruptIdLpTim2] = LPTIM2_IRQn, + + // UARTx + [FuriHalInterruptIdUart1] = USART1_IRQn, + + // LPUARTx + [FuriHalInterruptIdLpUart1] = LPUART1_IRQn, }; __attribute__((always_inline)) static inline void @@ -329,3 +335,11 @@ void LPTIM1_IRQHandler() { void LPTIM2_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdLpTim2); } + +void USART1_IRQHandler(void) { + furi_hal_interrupt_call(FuriHalInterruptIdUart1); +} + +void LPUART1_IRQHandler(void) { + furi_hal_interrupt_call(FuriHalInterruptIdLpUart1); +} \ No newline at end of file diff --git a/targets/f7/furi_hal/furi_hal_interrupt.h b/targets/f7/furi_hal/furi_hal_interrupt.h index 8a280ff8d..80a6323bd 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.h +++ b/targets/f7/furi_hal/furi_hal_interrupt.h @@ -49,6 +49,12 @@ typedef enum { FuriHalInterruptIdLpTim1, FuriHalInterruptIdLpTim2, + //UARTx + FuriHalInterruptIdUart1, + + //LPUARTx + FuriHalInterruptIdLpUart1, + // Service value FuriHalInterruptIdMax, } FuriHalInterruptId; diff --git a/targets/f7/furi_hal/furi_hal_os.c b/targets/f7/furi_hal/furi_hal_os.c index ea835b95f..9045295a1 100644 --- a/targets/f7/furi_hal/furi_hal_os.c +++ b/targets/f7/furi_hal/furi_hal_os.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -208,8 +207,8 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { UNUSED(xTask); - furi_hal_console_puts("\r\n\r\n stack overflow in "); - furi_hal_console_puts(pcTaskName); - furi_hal_console_puts("\r\n\r\n"); + furi_log_puts("\r\n\r\n stack overflow in "); + furi_log_puts(pcTaskName); + furi_log_puts("\r\n\r\n"); furi_crash("StackOverflow"); } diff --git a/targets/f7/furi_hal/furi_hal_power.c b/targets/f7/furi_hal/furi_hal_power.c index 9e3a70da7..483316c00 100644 --- a/targets/f7/furi_hal/furi_hal_power.c +++ b/targets/f7/furi_hal/furi_hal_power.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -178,14 +178,12 @@ static inline void furi_hal_power_light_sleep() { static inline void furi_hal_power_suspend_aux_periphs() { // Disable USART - furi_hal_uart_suspend(FuriHalUartIdUSART1); - furi_hal_uart_suspend(FuriHalUartIdLPUART1); + furi_hal_serial_control_suspend(); } static inline void furi_hal_power_resume_aux_periphs() { // Re-enable USART - furi_hal_uart_resume(FuriHalUartIdUSART1); - furi_hal_uart_resume(FuriHalUartIdLPUART1); + furi_hal_serial_control_resume(); } static inline void furi_hal_power_deep_sleep() { diff --git a/targets/f7/furi_hal/furi_hal_rtc.c b/targets/f7/furi_hal/furi_hal_rtc.c index 6c1c34a9b..88aad6858 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.c +++ b/targets/f7/furi_hal/furi_hal_rtc.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,9 @@ typedef struct { FuriHalRtcLocaleUnits locale_units : 1; FuriHalRtcLocaleTimeFormat locale_timeformat : 1; FuriHalRtcLocaleDateFormat locale_dateformat : 2; - uint8_t reserved : 6; + FuriHalRtcLogDevice log_device : 2; + FuriHalRtcLogBaudRate log_baud_rate : 3; + uint8_t reserved : 1; } SystemReg; _Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch"); @@ -51,6 +54,24 @@ static const uint8_t furi_hal_rtc_days_per_month[2][FURI_HAL_RTC_MONTHS_COUNT] = static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366}; +static const FuriHalSerialId furi_hal_rtc_log_devices[] = { + [FuriHalRtcLogDeviceUsart] = FuriHalSerialIdUsart, + [FuriHalRtcLogDeviceLpuart] = FuriHalSerialIdLpuart, + [FuriHalRtcLogDeviceReserved] = FuriHalSerialIdMax, + [FuriHalRtcLogDeviceNone] = FuriHalSerialIdMax, +}; + +static const uint32_t furi_hal_rtc_log_baud_rates[] = { + [FuriHalRtcLogBaudRate230400] = 230400, + [FuriHalRtcLogBaudRate9600] = 9600, + [FuriHalRtcLogBaudRate38400] = 38400, + [FuriHalRtcLogBaudRate57600] = 57600, + [FuriHalRtcLogBaudRate115200] = 115200, + [FuriHalRtcLogBaudRate460800] = 460800, + [FuriHalRtcLogBaudRate921600] = 921600, + [FuriHalRtcLogBaudRate1843200] = 1843200, +}; + static void furi_hal_rtc_reset() { LL_RCC_ForceBackupDomainReset(); LL_RCC_ReleaseBackupDomainReset(); @@ -153,6 +174,9 @@ void furi_hal_rtc_init() { LL_RTC_Init(RTC, &RTC_InitStruct); furi_log_set_level(furi_hal_rtc_get_log_level()); + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); FURI_LOG_I(TAG, "Init OK"); } @@ -199,6 +223,40 @@ uint8_t furi_hal_rtc_get_log_level() { return data->log_level; } +void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->log_device = device; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); +} + +FuriHalRtcLogDevice furi_hal_rtc_get_log_device() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->log_device; +} + +void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->log_baud_rate = baud_rate; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + furi_hal_serial_control_set_logging_config( + furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()], + furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]); +} + +FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->log_baud_rate; +} + void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); SystemReg* data = (SystemReg*)&data_reg; diff --git a/targets/furi_hal_include/furi_hal_rtc.h b/targets/f7/furi_hal/furi_hal_rtc.h similarity index 73% rename from targets/furi_hal_include/furi_hal_rtc.h rename to targets/f7/furi_hal/furi_hal_rtc.h index fb9d39b3c..0a5023131 100644 --- a/targets/furi_hal_include/furi_hal_rtc.h +++ b/targets/f7/furi_hal/furi_hal_rtc.h @@ -64,32 +64,50 @@ typedef enum { } FuriHalRtcRegister; typedef enum { - FuriHalRtcLocaleUnitsMetric = 0, /**< Metric measurement units */ - FuriHalRtcLocaleUnitsImperial = 1, /**< Imperial measurement units */ + FuriHalRtcLocaleUnitsMetric = 0x0, /**< Metric measurement units */ + FuriHalRtcLocaleUnitsImperial = 0x1, /**< Imperial measurement units */ } FuriHalRtcLocaleUnits; typedef enum { - FuriHalRtcLocaleTimeFormat24h = 0, /**< 24-hour format */ - FuriHalRtcLocaleTimeFormat12h = 1, /**< 12-hour format */ + FuriHalRtcLocaleTimeFormat24h = 0x0, /**< 24-hour format */ + FuriHalRtcLocaleTimeFormat12h = 0x1, /**< 12-hour format */ } FuriHalRtcLocaleTimeFormat; typedef enum { - FuriHalRtcLocaleDateFormatDMY = 0, /**< Day/Month/Year */ - FuriHalRtcLocaleDateFormatMDY = 1, /**< Month/Day/Year */ - FuriHalRtcLocaleDateFormatYMD = 2, /**< Year/Month/Day */ + FuriHalRtcLocaleDateFormatDMY = 0x0, /**< Day/Month/Year */ + FuriHalRtcLocaleDateFormatMDY = 0x1, /**< Month/Day/Year */ + FuriHalRtcLocaleDateFormatYMD = 0x2, /**< Year/Month/Day */ } FuriHalRtcLocaleDateFormat; +typedef enum { + FuriHalRtcLogDeviceUsart = 0x0, /**< Default: USART */ + FuriHalRtcLogDeviceLpuart = 0x1, /**< Default: LPUART */ + FuriHalRtcLogDeviceReserved = 0x2, /**< Reserved for future use */ + FuriHalRtcLogDeviceNone = 0x3, /**< None, disable serial logging */ +} FuriHalRtcLogDevice; + +typedef enum { + FuriHalRtcLogBaudRate230400 = 0x0, /**< 230400 baud */ + FuriHalRtcLogBaudRate9600 = 0x1, /**< 9600 baud */ + FuriHalRtcLogBaudRate38400 = 0x2, /**< 38400 baud */ + FuriHalRtcLogBaudRate57600 = 0x3, /**< 57600 baud */ + FuriHalRtcLogBaudRate115200 = 0x4, /**< 115200 baud */ + FuriHalRtcLogBaudRate460800 = 0x5, /**< 460800 baud */ + FuriHalRtcLogBaudRate921600 = 0x6, /**< 921600 baud */ + FuriHalRtcLogBaudRate1843200 = 0x7, /**< 1843200 baud */ +} FuriHalRtcLogBaudRate; + /** Early initialization */ -void furi_hal_rtc_init_early(); +void furi_hal_rtc_init_early(void); /** Early de-initialization */ -void furi_hal_rtc_deinit_early(); +void furi_hal_rtc_deinit_early(void); /** Initialize RTC subsystem */ -void furi_hal_rtc_init(); +void furi_hal_rtc_init(void); /** Force sync shadow registers */ -void furi_hal_rtc_sync_shadow(); +void furi_hal_rtc_sync_shadow(void); /** Reset ALL RTC registers content */ void furi_hal_rtc_reset_registers(); @@ -119,7 +137,31 @@ void furi_hal_rtc_set_log_level(uint8_t level); * * @return The Log Level value */ -uint8_t furi_hal_rtc_get_log_level(); +uint8_t furi_hal_rtc_get_log_level(void); + +/** Set logging device + * + * @param[in] device The device + */ +void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device); + +/** Get logging device + * + * @return The furi hal rtc log device. + */ +FuriHalRtcLogDevice furi_hal_rtc_get_log_device(void); + +/** Set logging baud rate + * + * @param[in] baud_rate The baud rate + */ +void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate); + +/** Get logging baud rate + * + * @return The furi hal rtc log baud rate. + */ +FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate(void); /** Set RTC Flag * @@ -151,7 +193,7 @@ void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode); * * @return The RTC boot mode. */ -FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(); +FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(void); /** Set Heap Track mode * @@ -163,7 +205,7 @@ void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode); * * @return The RTC heap track mode. */ -FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(); +FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(void); /** Set locale units * @@ -175,7 +217,7 @@ void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value); * * @return The RTC Locale Units. */ -FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(); +FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(void); /** Set RTC Locale Time Format * @@ -187,7 +229,7 @@ void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value); * * @return The RTC Locale Time Format. */ -FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(); +FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(void); /** Set RTC Locale Date Format * @@ -199,7 +241,7 @@ void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value); * * @return The RTC Locale Date Format */ -FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(); +FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(void); /** Set RTC Date Time * @@ -231,7 +273,7 @@ void furi_hal_rtc_set_fault_data(uint32_t value); * * @return RTC Fault Data value */ -uint32_t furi_hal_rtc_get_fault_data(); +uint32_t furi_hal_rtc_get_fault_data(void); /** Set Pin Fails count * @@ -243,13 +285,13 @@ void furi_hal_rtc_set_pin_fails(uint32_t value); * * @return Pin Fails Count */ -uint32_t furi_hal_rtc_get_pin_fails(); +uint32_t furi_hal_rtc_get_pin_fails(void); /** Get UNIX Timestamp * * @return Unix Timestamp in seconds from UNIX epoch start */ -uint32_t furi_hal_rtc_get_timestamp(); +uint32_t furi_hal_rtc_get_timestamp(void); /** Convert DateTime to UNIX timestamp * diff --git a/targets/f7/furi_hal/furi_hal_serial.c b/targets/f7/furi_hal/furi_hal_serial.c new file mode 100644 index 000000000..71dd6561e --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial.c @@ -0,0 +1,838 @@ +#include +#include "furi_hal_serial_types_i.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FURI_HAL_SERIAL_USART_OVERSAMPLING LL_USART_OVERSAMPLING_16 + +#define FURI_HAL_SERIAL_USART_DMA_INSTANCE (DMA1) +#define FURI_HAL_SERIAL_USART_DMA_CHANNEL (LL_DMA_CHANNEL_6) + +#define FURI_HAL_SERIAL_LPUART_DMA_INSTANCE (DMA1) +#define FURI_HAL_SERIAL_LPUART_DMA_CHANNEL (LL_DMA_CHANNEL_7) + +typedef struct { + uint8_t* buffer_rx_ptr; + size_t buffer_rx_index_write; + size_t buffer_rx_index_read; + bool enabled; + FuriHalSerialHandle* handle; + FuriHalSerialAsyncRxCallback rx_byte_callback; + FuriHalSerialDmaRxCallback rx_dma_callback; + void* context; +} FuriHalSerial; + +static FuriHalSerial furi_hal_serial[FuriHalSerialIdMax] = {0}; + +static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch); + +static void furi_hal_serial_async_rx_configure( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context); + +static void furi_hal_serial_usart_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialRxEvent event = 0; + // Notification flags + if(USART1->ISR & USART_ISR_RXNE_RXFNE) { + event |= FuriHalSerialRxEventData; + } + if(USART1->ISR & USART_ISR_IDLE) { + USART1->ICR = USART_ICR_IDLECF; + event |= FuriHalSerialRxEventIdle; + } + // Error flags + if(USART1->ISR & USART_ISR_ORE) { + USART1->ICR = USART_ICR_ORECF; + event |= FuriHalSerialRxEventOverrunError; + } + if(USART1->ISR & USART_ISR_NE) { + USART1->ICR = USART_ICR_NECF; + event |= FuriHalSerialRxEventNoiseError; + } + if(USART1->ISR & USART_ISR_FE) { + USART1->ICR = USART_ICR_FECF; + event |= FuriHalSerialRxEventFrameError; + } + if(USART1->ISR & USART_ISR_PE) { + USART1->ICR = USART_ICR_PECF; + event |= FuriHalSerialRxEventFrameError; + } + + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + event, + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } else { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + event, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } +} + +static void furi_hal_serial_usart_dma_rx_isr(void* context) { + UNUSED(context); +#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write = + FURI_HAL_SERIAL_DMA_BUFFER_SIZE - + LL_DMA_GetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + if((furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read > + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write) || + (furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } + + } else if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) { + if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdUsart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart), + furi_hal_serial[FuriHalSerialIdUsart].context); + } + } + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void furi_hal_serial_usart_init_dma_rx(void) { + /* USART1_RX_DMA Init */ + furi_check(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write = 0; + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read = 0; + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetMemoryAddress( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + (uint32_t)furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr); + LL_DMA_SetPeriphAddress( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + (uint32_t) & (USART1->RDR)); + + LL_DMA_ConfigTransfer( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetPeriphRequest( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, + FURI_HAL_SERIAL_USART_DMA_CHANNEL, + LL_DMAMUX_REQ_USART1_RX); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, furi_hal_serial_usart_dma_rx_isr, NULL); + +#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + + LL_DMA_EnableChannel(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_USART_EnableDMAReq_RX(USART1); + + LL_USART_EnableIT_IDLE(USART1); +} + +static void furi_hal_serial_usart_deinit_dma_rx(void) { + if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr != NULL) { + LL_DMA_DisableChannel( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_USART_DisableDMAReq_RX(USART1); + + LL_USART_DisableIT_IDLE(USART1); + LL_DMA_DisableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + LL_DMA_DisableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + + LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE); + + LL_DMA_DeInit(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, NULL, NULL); + free(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr); + furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = NULL; + } +} + +static void furi_hal_serial_usart_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_hal_bus_enable(FuriHalBusUSART1); + LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); + + furi_hal_gpio_init_ex( + &gpio_usart_tx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + furi_hal_gpio_init_ex( + &gpio_usart_rx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + + LL_USART_InitTypeDef USART_InitStruct; + USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; + USART_InitStruct.BaudRate = baud; + USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; + USART_InitStruct.StopBits = LL_USART_STOPBITS_1; + USART_InitStruct.Parity = LL_USART_PARITY_NONE; + USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; + USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; + USART_InitStruct.OverSampling = FURI_HAL_SERIAL_USART_OVERSAMPLING; + LL_USART_Init(USART1, &USART_InitStruct); + LL_USART_EnableFIFO(USART1); + LL_USART_ConfigAsyncMode(USART1); + + LL_USART_Enable(USART1); + + while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) + ; + + furi_hal_serial_set_br(handle, baud); + LL_USART_DisableIT_ERROR(USART1); + furi_hal_serial[handle->id].enabled = true; +} + +static void furi_hal_serial_lpuart_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialRxEvent event = 0; + // Notification flags + if(LPUART1->ISR & USART_ISR_RXNE_RXFNE) { + event |= FuriHalSerialRxEventData; + } + if(LPUART1->ISR & USART_ISR_IDLE) { + LPUART1->ICR = USART_ICR_IDLECF; + event |= FuriHalSerialRxEventIdle; + } + // Error flags + if(LPUART1->ISR & USART_ISR_ORE) { + LPUART1->ICR = USART_ICR_ORECF; + event |= FuriHalSerialRxEventOverrunError; + } + if(LPUART1->ISR & USART_ISR_NE) { + LPUART1->ICR = USART_ICR_NECF; + event |= FuriHalSerialRxEventNoiseError; + } + if(LPUART1->ISR & USART_ISR_FE) { + LPUART1->ICR = USART_ICR_FECF; + event |= FuriHalSerialRxEventFrameError; + } + if(LPUART1->ISR & USART_ISR_PE) { + LPUART1->ICR = USART_ICR_PECF; + event |= FuriHalSerialRxEventFrameError; + } + + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + event, + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } else { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + event, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } +} + +static void furi_hal_serial_lpuart_dma_rx_isr(void* context) { + UNUSED(context); +#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write = + FURI_HAL_SERIAL_DMA_BUFFER_SIZE - + LL_DMA_GetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + if((furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read > + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write) || + (furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } + + } else if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read < + FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) { + if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) { + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback( + furi_hal_serial[FuriHalSerialIdLpuart].handle, + FuriHalSerialRxEventData, + furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart), + furi_hal_serial[FuriHalSerialIdLpuart].context); + } + } + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void furi_hal_serial_lpuart_init_dma_rx(void) { + /* LPUART1_RX_DMA Init */ + furi_check(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write = 0; + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read = 0; + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetMemoryAddress( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + (uint32_t)furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr); + LL_DMA_SetPeriphAddress( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + (uint32_t) & (LPUART1->RDR)); + + LL_DMA_ConfigTransfer( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + FURI_HAL_SERIAL_DMA_BUFFER_SIZE); + LL_DMA_SetPeriphRequest( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, + FURI_HAL_SERIAL_LPUART_DMA_CHANNEL, + LL_DMAMUX_REQ_LPUART1_RX); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, furi_hal_serial_lpuart_dma_rx_isr, NULL); + +#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + + LL_DMA_EnableChannel(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_USART_EnableDMAReq_RX(LPUART1); + + LL_USART_EnableIT_IDLE(LPUART1); +} + +static void furi_hal_serial_lpuart_deinit_dma_rx(void) { + if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr != NULL) { + LL_DMA_DisableChannel( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_USART_DisableDMAReq_RX(LPUART1); + + LL_USART_DisableIT_IDLE(LPUART1); + LL_DMA_DisableIT_TC( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + LL_DMA_DisableIT_HT( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + + LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE); + + LL_DMA_DeInit(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, NULL, NULL); + free(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr); + furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = NULL; + } +} + +static void furi_hal_serial_lpuart_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_hal_bus_enable(FuriHalBusLPUART1); + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); + + furi_hal_gpio_init_ex( + &gpio_ext_pc0, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + furi_hal_gpio_init_ex( + &gpio_ext_pc1, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + + LL_LPUART_InitTypeDef LPUART_InitStruct; + LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; + LPUART_InitStruct.BaudRate = baud; + LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; + LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; + LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; + LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; + LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; + LL_LPUART_Init(LPUART1, &LPUART_InitStruct); + LL_LPUART_EnableFIFO(LPUART1); + + LL_LPUART_Enable(LPUART1); + + while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) + ; + + furi_hal_serial_set_br(handle, baud); + LL_LPUART_DisableIT_ERROR(LPUART1); + furi_hal_serial[handle->id].enabled = true; +} + +void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + if(handle->id == FuriHalSerialIdLpuart) { + furi_hal_serial_lpuart_init(handle, baud); + } else if(handle->id == FuriHalSerialIdUsart) { + furi_hal_serial_usart_init(handle, baud); + } +} + +static uint32_t furi_hal_serial_get_prescaler(FuriHalSerialHandle* handle, uint32_t baud) { + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + uint32_t divisor = (uartclk / baud); + uint32_t prescaler = 0; + if(handle->id == FuriHalSerialIdUsart) { + if(FURI_HAL_SERIAL_USART_OVERSAMPLING == LL_USART_OVERSAMPLING_16) { + divisor = (divisor / 16) >> 12; + } else { + divisor = (divisor / 8) >> 12; + } + if(divisor < 1) { + prescaler = LL_USART_PRESCALER_DIV1; + } else if(divisor < 2) { + prescaler = LL_USART_PRESCALER_DIV2; + } else if(divisor < 4) { + prescaler = LL_USART_PRESCALER_DIV4; + } else if(divisor < 6) { + prescaler = LL_USART_PRESCALER_DIV6; + } else if(divisor < 8) { + prescaler = LL_USART_PRESCALER_DIV8; + } else if(divisor < 10) { + prescaler = LL_USART_PRESCALER_DIV10; + } else if(divisor < 12) { + prescaler = LL_USART_PRESCALER_DIV12; + } else if(divisor < 16) { + prescaler = LL_USART_PRESCALER_DIV16; + } else if(divisor < 32) { + prescaler = LL_USART_PRESCALER_DIV32; + } else if(divisor < 64) { + prescaler = LL_USART_PRESCALER_DIV64; + } else if(divisor < 128) { + prescaler = LL_USART_PRESCALER_DIV128; + } else { + prescaler = LL_USART_PRESCALER_DIV256; + } + } else if(handle->id == FuriHalSerialIdLpuart) { + divisor >>= 12; + if(divisor < 1) { + prescaler = LL_LPUART_PRESCALER_DIV1; + } else if(divisor < 2) { + prescaler = LL_LPUART_PRESCALER_DIV2; + } else if(divisor < 4) { + prescaler = LL_LPUART_PRESCALER_DIV4; + } else if(divisor < 6) { + prescaler = LL_LPUART_PRESCALER_DIV6; + } else if(divisor < 8) { + prescaler = LL_LPUART_PRESCALER_DIV8; + } else if(divisor < 10) { + prescaler = LL_LPUART_PRESCALER_DIV10; + } else if(divisor < 12) { + prescaler = LL_LPUART_PRESCALER_DIV12; + } else if(divisor < 16) { + prescaler = LL_LPUART_PRESCALER_DIV16; + } else if(divisor < 32) { + prescaler = LL_LPUART_PRESCALER_DIV32; + } else if(divisor < 64) { + prescaler = LL_LPUART_PRESCALER_DIV64; + } else if(divisor < 128) { + prescaler = LL_LPUART_PRESCALER_DIV128; + } else { + prescaler = LL_LPUART_PRESCALER_DIV256; + } + } + + return prescaler; +} + +void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + uint32_t prescaler = furi_hal_serial_get_prescaler(handle, baud); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1)) { + // Wait for transfer complete flag + while(!LL_USART_IsActiveFlag_TC(USART1)) + ; + LL_USART_Disable(USART1); + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + LL_USART_SetPrescaler(USART1, prescaler); + LL_USART_SetBaudRate( + USART1, uartclk, prescaler, FURI_HAL_SERIAL_USART_OVERSAMPLING, baud); + LL_USART_Enable(USART1); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1)) { + // Wait for transfer complete flag + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + LL_LPUART_Disable(LPUART1); + uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE); + LL_LPUART_SetPrescaler(LPUART1, prescaler); + LL_LPUART_SetBaudRate(LPUART1, uartclk, prescaler, baud); + LL_LPUART_Enable(LPUART1); + } + } +} + +void furi_hal_serial_deinit(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_async_rx_configure(handle, NULL, NULL); + if(handle->id == FuriHalSerialIdUsart) { + if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) { + furi_hal_bus_disable(FuriHalBusUSART1); + } + if(LL_USART_IsEnabled(USART1)) { + LL_USART_Disable(USART1); + } + furi_hal_serial_usart_deinit_dma_rx(); + furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(handle->id == FuriHalSerialIdLpuart) { + if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) { + furi_hal_bus_disable(FuriHalBusLPUART1); + } + if(LL_LPUART_IsEnabled(LPUART1)) { + LL_LPUART_Disable(LPUART1); + } + furi_hal_serial_lpuart_deinit_dma_rx(); + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else { + furi_crash(); + } + furi_hal_serial[handle->id].enabled = false; +} + +void furi_hal_serial_suspend(FuriHalSerialHandle* handle) { + furi_check(handle); + if(handle->id == FuriHalSerialIdLpuart && LL_LPUART_IsEnabled(LPUART1)) { + LL_LPUART_Disable(LPUART1); + } else if(handle->id == FuriHalSerialIdUsart && LL_USART_IsEnabled(USART1)) { + LL_USART_Disable(USART1); + } + furi_hal_serial[handle->id].enabled = false; +} + +void furi_hal_serial_resume(FuriHalSerialHandle* handle) { + furi_check(handle); + if(!furi_hal_serial[handle->id].enabled) { + if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_Enable(LPUART1); + } else if(handle->id == FuriHalSerialIdUsart) { + LL_USART_Enable(USART1); + } + furi_hal_serial[handle->id].enabled = true; + } +} + +void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size) { + furi_check(handle); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1) == 0) return; + + while(buffer_size > 0) { + while(!LL_USART_IsActiveFlag_TXE(USART1)) + ; + + LL_USART_TransmitData8(USART1, *buffer); + buffer++; + buffer_size--; + } + + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1) == 0) return; + + while(buffer_size > 0) { + while(!LL_LPUART_IsActiveFlag_TXE(LPUART1)) + ; + + LL_LPUART_TransmitData8(LPUART1, *buffer); + + buffer++; + buffer_size--; + } + } +} + +void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle) { + furi_check(handle); + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabled(USART1) == 0) return; + + while(!LL_USART_IsActiveFlag_TC(USART1)) + ; + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabled(LPUART1) == 0) return; + + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + } +} + +static void furi_hal_serial_event_init(FuriHalSerialHandle* handle, bool report_errors) { + if(handle->id == FuriHalSerialIdUsart) { + LL_USART_EnableIT_IDLE(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_EnableIT_IDLE(LPUART1); + } + + if(report_errors) { + if(handle->id == FuriHalSerialIdUsart) { + LL_USART_EnableIT_ERROR(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_EnableIT_ERROR(LPUART1); + } + } +} + +static void furi_hal_serial_event_deinit(FuriHalSerialHandle* handle) { + if(handle->id == FuriHalSerialIdUsart) { + if(LL_USART_IsEnabledIT_IDLE(USART1)) LL_USART_DisableIT_IDLE(USART1); + if(LL_USART_IsEnabledIT_ERROR(USART1)) LL_USART_DisableIT_ERROR(USART1); + } else if(handle->id == FuriHalSerialIdLpuart) { + if(LL_LPUART_IsEnabledIT_IDLE(LPUART1)) LL_LPUART_DisableIT_IDLE(LPUART1); + if(LL_LPUART_IsEnabledIT_ERROR(LPUART1)) LL_LPUART_DisableIT_ERROR(LPUART1); + } +} + +static void furi_hal_serial_async_rx_configure( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context) { + if(handle->id == FuriHalSerialIdUsart) { + if(callback) { + furi_hal_serial_usart_deinit_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); + LL_USART_EnableIT_RXNE_RXFNE(USART1); + } else { + furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); + furi_hal_serial_usart_deinit_dma_rx(); + LL_USART_DisableIT_RXNE_RXFNE(USART1); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(callback) { + furi_hal_serial_lpuart_deinit_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + } else { + furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); + furi_hal_serial_lpuart_deinit_dma_rx(); + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + } + } + furi_hal_serial[handle->id].rx_byte_callback = callback; + furi_hal_serial[handle->id].handle = handle; + furi_hal_serial[handle->id].rx_dma_callback = NULL; + furi_hal_serial[handle->id].context = context; +} + +void furi_hal_serial_async_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context, + bool report_errors) { + furi_check(handle); + furi_check(callback); + + furi_hal_serial_event_init(handle, report_errors); + furi_hal_serial_async_rx_configure(handle, callback, context); + + // Assign different functions to different UARTs + furi_check( + furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback != + furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback); +} + +void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_event_deinit(handle); + furi_hal_serial_async_rx_configure(handle, NULL, NULL); +} + +uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle) { + furi_check(FURI_IS_IRQ_MODE()); + furi_assert(handle->id < FuriHalSerialIdMax); + + if(handle->id == FuriHalSerialIdUsart) { + return LL_USART_ReceiveData8(USART1); + } + return LL_LPUART_ReceiveData8(LPUART1); +} + +static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch) { + size_t dma_remain = 0; + if(ch == FuriHalSerialIdUsart) { + dma_remain = LL_DMA_GetDataLength( + FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL); + } else if(ch == FuriHalSerialIdLpuart) { + dma_remain = LL_DMA_GetDataLength( + FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL); + } else { + furi_crash(); + } + + furi_hal_serial[ch].buffer_rx_index_write = FURI_HAL_SERIAL_DMA_BUFFER_SIZE - dma_remain; + if(furi_hal_serial[ch].buffer_rx_index_write >= furi_hal_serial[ch].buffer_rx_index_read) { + return furi_hal_serial[ch].buffer_rx_index_write - + furi_hal_serial[ch].buffer_rx_index_read; + } else { + return FURI_HAL_SERIAL_DMA_BUFFER_SIZE - furi_hal_serial[ch].buffer_rx_index_read + + furi_hal_serial[ch].buffer_rx_index_write; + } +} + +static uint8_t furi_hal_serial_dma_rx_read_byte(FuriHalSerialHandle* handle) { + uint8_t data = 0; + data = + furi_hal_serial[handle->id].buffer_rx_ptr[furi_hal_serial[handle->id].buffer_rx_index_read]; + furi_hal_serial[handle->id].buffer_rx_index_read++; + if(furi_hal_serial[handle->id].buffer_rx_index_read >= FURI_HAL_SERIAL_DMA_BUFFER_SIZE) { + furi_hal_serial[handle->id].buffer_rx_index_read = 0; + } + return data; +} + +size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len) { + furi_check(FURI_IS_IRQ_MODE()); + furi_assert(furi_hal_serial[handle->id].buffer_rx_ptr != NULL); + size_t i = 0; + size_t available = furi_hal_serial_dma_bytes_available(handle->id); + if(available < len) { + len = available; + } + for(i = 0; i < len; i++) { + data[i] = furi_hal_serial_dma_rx_read_byte(handle); + } + return i; +} + +static void furi_hal_serial_dma_configure( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context) { + furi_check(handle); + + if(handle->id == FuriHalSerialIdUsart) { + if(callback) { + furi_hal_serial_usart_init_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); + } else { + LL_USART_DisableIT_RXNE_RXFNE(USART1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); + furi_hal_serial_usart_deinit_dma_rx(); + } + } else if(handle->id == FuriHalSerialIdLpuart) { + if(callback) { + furi_hal_serial_lpuart_init_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); + } else { + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); + furi_hal_serial_lpuart_deinit_dma_rx(); + } + } + furi_hal_serial[handle->id].rx_byte_callback = NULL; + furi_hal_serial[handle->id].handle = handle; + furi_hal_serial[handle->id].rx_dma_callback = callback; + furi_hal_serial[handle->id].context = context; +} + +void furi_hal_serial_dma_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context, + bool report_errors) { + furi_check(handle); + furi_check(callback); + + furi_hal_serial_event_init(handle, report_errors); + furi_hal_serial_dma_configure(handle, callback, context); + + // Assign different functions to different UARTs + furi_check( + furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback != + furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback); +} + +void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle) { + furi_check(handle); + furi_hal_serial_event_deinit(handle); + furi_hal_serial_dma_configure(handle, NULL, NULL); +} diff --git a/targets/f7/furi_hal/furi_hal_serial.h b/targets/f7/furi_hal/furi_hal_serial.h new file mode 100644 index 000000000..19cea2a7a --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial.h @@ -0,0 +1,189 @@ +/** + * @file furi_hal_serial.h + * + * Serial HAL API + */ +#pragma once + +#include +#include + +#include "furi_hal_serial_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize Serial + * + * Configures GPIO, configures and enables transceiver. + * + * @param handle Serial handle + * @param baud baud rate + */ +void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud); + +/** De-initialize Serial + * + * Configures GPIO to analog, clears callback and callback context, disables + * hardware + * + * @param handle Serial handle + */ +void furi_hal_serial_deinit(FuriHalSerialHandle* handle); + +/** Suspend operation + * + * Suspend hardware, settings and callbacks are preserved + * + * @param handle Serial handle + */ +void furi_hal_serial_suspend(FuriHalSerialHandle* handle); + +/** Resume operation + * + * Resumes hardware from suspended state + * + * @param handle Serial handle + */ +void furi_hal_serial_resume(FuriHalSerialHandle* handle); + +/** Changes baud rate + * + * @param handle Serial handle + * @param baud baud rate + */ +void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud); + +/** Transmits data in semi-blocking mode + * + * Fills transmission pipe with data, returns as soon as all bytes from buffer + * are in the pipe. + * + * Real transmission will be completed later. Use + * `furi_hal_serial_tx_wait_complete` to wait for completion if you need it. + * + * @param handle Serial handle + * @param buffer data + * @param buffer_size data size (in bytes) + */ +void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size); + +/** Wait until transmission is completed + * + * Ensures that all data has been sent. + * + * @param handle Serial handle + */ +void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle); + +/** Serial RX events */ +typedef enum { + FuriHalSerialRxEventData = (1 << 0), /**< Data: new data available */ + FuriHalSerialRxEventIdle = (1 << 1), /**< Idle: bus idle detected */ + FuriHalSerialRxEventFrameError = (1 << 2), /**< Framing Error: incorrect frame detected */ + FuriHalSerialRxEventNoiseError = (1 << 3), /**< Noise Error: noise on the line detected */ + FuriHalSerialRxEventOverrunError = (1 << 4), /**< Overrun Error: no space for received data */ +} FuriHalSerialRxEvent; + +/** Receive callback + * + * @warning Callback will be called in interrupt context, ensure thread + * safety on your side. + * @param handle Serial handle + * @param event FuriHalSerialRxEvent + * @param context Callback context provided earlier + */ +typedef void (*FuriHalSerialAsyncRxCallback)( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context); + +/** Start and sets Serial Receive callback + * + * @warning Callback will be called in interrupt context, ensure thread + * safety on your side + * + * @param handle Serial handle + * @param callback callback pointer + * @param context callback context + * @param[in] report_errors report RX error + */ +void furi_hal_serial_async_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialAsyncRxCallback callback, + void* context, + bool report_errors); + +/** Stop Serial Receive + * + * @param handle Serial handle + */ +void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle); + +/** Get data Serial receive + * + * @warning This function must be called only from the callback + * FuriHalSerialAsyncRxCallback + * + * @param handle Serial handle + * + * @return data + */ +uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle); + +/* DMA based Serial API */ + +#define FURI_HAL_SERIAL_DMA_BUFFER_SIZE (256u) + +/** Receive DMA callback + * + * @warning DMA Callback will be called in interrupt context, ensure thread + * safety on your side. + * + * @param handle Serial handle + * @param event FuriHalSerialDmaRxEvent + * @param data_len Received data + * @param context Callback context provided earlier + */ +typedef void (*FuriHalSerialDmaRxCallback)( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + size_t data_len, + void* context); + +/** Start and sets Serial event callback receive DMA + * + * @param handle Serial handle + * @param callback callback pointer + * @param context callback context + * @param[in] report_errors report RX error + */ +void furi_hal_serial_dma_rx_start( + FuriHalSerialHandle* handle, + FuriHalSerialDmaRxCallback callback, + void* context, + bool report_errors); + +/** Stop Serial receive DMA + * + * @param handle Serial handle + */ +void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle); + +/** Get data Serial receive DMA + * + * @warning This function must be called only from the callback + * FuriHalSerialDmaRxCallback + * + * @param handle Serial handle + * @param data pointer to data buffer + * @param len get data size (in bytes) + * + * @return size actual data receive (in bytes) + */ +size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c new file mode 100644 index 000000000..28c32e203 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -0,0 +1,233 @@ +#include "furi_hal_serial_control.h" +#include "furi_hal_serial_types_i.h" +#include "furi_hal_serial.h" + +#include +#include + +#define TAG "FuriHalSerialControl" + +typedef enum { + FuriHalSerialControlMessageTypeStop, + FuriHalSerialControlMessageTypeSuspend, + FuriHalSerialControlMessageTypeResume, + FuriHalSerialControlMessageTypeAcquire, + FuriHalSerialControlMessageTypeRelease, + FuriHalSerialControlMessageTypeLogging, +} FuriHalSerialControlMessageType; + +typedef struct { + FuriHalSerialControlMessageType type; + FuriApiLock api_lock; + void* input; + void* output; +} FuriHalSerialControlMessage; + +typedef struct { + const FuriHalSerialId id; + const uint32_t baud_rate; +} FuriHalSerialControlMessageInputLogging; + +typedef struct { + FuriHalSerialHandle handles[FuriHalSerialIdMax]; + FuriMessageQueue* queue; + FuriThread* thread; + + // Logging + FuriHalSerialId log_config_serial_id; + uint32_t log_config_serial_baud_rate; + FuriLogHandler log_handler; + FuriHalSerialHandle* log_serial; +} FuriHalSerialControl; + +FuriHalSerialControl* furi_hal_serial_control = NULL; + +static void furi_hal_serial_control_log_callback(const uint8_t* data, size_t size, void* context) { + FuriHalSerialHandle* handle = context; + furi_hal_serial_tx(handle, data, size); +} + +static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) { + if(furi_hal_serial_control->log_serial) { + furi_log_remove_handler(furi_hal_serial_control->log_handler); + furi_hal_serial_deinit(furi_hal_serial_control->log_serial); + furi_hal_serial_control->log_serial = NULL; + } + + if(handle) { + furi_hal_serial_control->log_serial = handle; + furi_hal_serial_init( + furi_hal_serial_control->log_serial, + furi_hal_serial_control->log_config_serial_baud_rate); + furi_hal_serial_control->log_handler.callback = furi_hal_serial_control_log_callback; + furi_hal_serial_control->log_handler.context = furi_hal_serial_control->log_serial; + furi_log_add_handler(furi_hal_serial_control->log_handler); + } +} + +static int32_t furi_hal_serial_control_thread(void* args) { + UNUSED(args); + + bool should_continue = true; + while(should_continue || furi_message_queue_get_count(furi_hal_serial_control->queue) > 0) { + FuriHalSerialControlMessage message = {0}; + FuriStatus status = + furi_message_queue_get(furi_hal_serial_control->queue, &message, FuriWaitForever); + furi_check(status == FuriStatusOk); + + if(message.type == FuriHalSerialControlMessageTypeStop) { + should_continue = false; + } else if(message.type == FuriHalSerialControlMessageTypeSuspend) { + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); + furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeResume) { + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeAcquire) { + FuriHalSerialId serial_id = *(FuriHalSerialId*)message.input; + if(furi_hal_serial_control->handles[serial_id].in_use) { + *(FuriHalSerialHandle**)message.output = NULL; + } else { + // Logging + if(furi_hal_serial_control->log_config_serial_id == serial_id) { + furi_hal_serial_control_log_set_handle(NULL); + } + // Return handle + furi_hal_serial_control->handles[serial_id].in_use = true; + *(FuriHalSerialHandle**)message.output = + &furi_hal_serial_control->handles[serial_id]; + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeRelease) { + FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)message.input; + furi_assert(handle->in_use); + furi_hal_serial_deinit(handle); + handle->in_use = false; + + // Return back logging + if(furi_hal_serial_control->log_config_serial_id == handle->id) { + furi_hal_serial_control_log_set_handle(handle); + } + api_lock_unlock(message.api_lock); + } else if(message.type == FuriHalSerialControlMessageTypeLogging) { + // Set new configuration + FuriHalSerialControlMessageInputLogging* message_input = message.input; + furi_hal_serial_control->log_config_serial_id = message_input->id; + furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; + // Apply new configuration + FuriHalSerialHandle* handle = NULL; + if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { + handle = &furi_hal_serial_control + ->handles[furi_hal_serial_control->log_config_serial_id]; + } + furi_hal_serial_control_log_set_handle(handle); + api_lock_unlock(message.api_lock); + } else { + furi_crash("Invalid parameter"); + } + } + + return 0; +} + +void furi_hal_serial_control_init(void) { + furi_check(furi_hal_serial_control == NULL); + // Allocate resources + furi_hal_serial_control = malloc(sizeof(FuriHalSerialControl)); + furi_hal_serial_control->handles[FuriHalSerialIdUsart].id = FuriHalSerialIdUsart; + furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart; + furi_hal_serial_control->queue = + furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage)); + furi_hal_serial_control->thread = + furi_thread_alloc_ex("SerialControlDriver", 512, furi_hal_serial_control_thread, NULL); + furi_thread_mark_as_service(furi_hal_serial_control->thread); + furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest); + furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax; + // Start control plane thread + furi_thread_start(furi_hal_serial_control->thread); +} + +void furi_hal_serial_control_deinit(void) { + furi_check(furi_hal_serial_control); + // Stop control plane thread + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeStop; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + furi_thread_join(furi_hal_serial_control->thread); + // Release resources + furi_thread_free(furi_hal_serial_control->thread); + furi_message_queue_free(furi_hal_serial_control->queue); + free(furi_hal_serial_control); +} + +void furi_hal_serial_control_suspend(void) { + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeSuspend; + message.api_lock = api_lock_alloc_locked(); + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +void furi_hal_serial_control_resume(void) { + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeResume; + message.api_lock = api_lock_alloc_locked(); + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id) { + furi_check(furi_hal_serial_control); + + FuriHalSerialHandle* output = NULL; + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeAcquire; + message.api_lock = api_lock_alloc_locked(); + message.input = &serial_id; + message.output = &output; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + + return output; +} + +void furi_hal_serial_control_release(FuriHalSerialHandle* handle) { + furi_check(furi_hal_serial_control); + furi_check(handle); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeRelease; + message.api_lock = api_lock_alloc_locked(); + message.input = &handle; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} + +void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate) { + furi_check(serial_id <= FuriHalSerialIdMax); + furi_check(baud_rate >= 9600 && baud_rate <= 4000000); + + // Very special case of updater, where RTC initialized before kernel start + if(!furi_hal_serial_control) return; + + FuriHalSerialControlMessageInputLogging message_input = { + .id = serial_id, + .baud_rate = baud_rate, + }; + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeLogging; + message.api_lock = api_lock_alloc_locked(); + message.input = &message_input; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} diff --git a/targets/f7/furi_hal/furi_hal_serial_control.h b/targets/f7/furi_hal/furi_hal_serial_control.h new file mode 100644 index 000000000..6b42281bf --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_control.h @@ -0,0 +1,46 @@ +#pragma once + +#include "furi_hal_serial_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize Serial Control */ +void furi_hal_serial_control_init(void); + +/** De-Initialize Serial Control */ +void furi_hal_serial_control_deinit(void); + +/** Suspend All Serial Interfaces */ +void furi_hal_serial_control_suspend(void); + +/** Resume All Serial Interfaces */ +void furi_hal_serial_control_resume(void); + +/** Acquire Serial Interface Handler + * + * @param[in] serial_id The serial transceiver identifier + * + * @return The Serial Interface Handle or null if interfaces is in use + */ +FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id); + +/** Release Serial Interface Handler + * + * @param handle The handle + */ +void furi_hal_serial_control_release(FuriHalSerialHandle* handle); + +/** Acquire Serial Interface Handler + * + * @param[in] serial_id The serial transceiver identifier. Use FuriHalSerialIdMax to disable logging. + * @param[in] baud_rate The baud rate + * + * @return The Serial Interface Handle or null if interfaces is in use + */ +void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_serial_types.h b/targets/f7/furi_hal/furi_hal_serial_types.h new file mode 100644 index 000000000..d5db36b29 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_types.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +/** + * UART channels + */ +typedef enum { + FuriHalSerialIdUsart, + FuriHalSerialIdLpuart, + + FuriHalSerialIdMax, +} FuriHalSerialId; + +typedef struct FuriHalSerialHandle FuriHalSerialHandle; diff --git a/targets/f7/furi_hal/furi_hal_serial_types_i.h b/targets/f7/furi_hal/furi_hal_serial_types_i.h new file mode 100644 index 000000000..9528e35eb --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_serial_types_i.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct FuriHalSerialHandle { + FuriHalSerialId id; + bool in_use; +}; diff --git a/targets/f7/furi_hal/furi_hal_uart.c b/targets/f7/furi_hal/furi_hal_uart.c deleted file mode 100644 index 209c6be6a..000000000 --- a/targets/f7/furi_hal/furi_hal_uart.c +++ /dev/null @@ -1,244 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -static bool furi_hal_usart_prev_enabled[2]; - -static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); -static void* irq_ctx[2]; - -static void furi_hal_usart_init(uint32_t baud) { - furi_hal_bus_enable(FuriHalBusUSART1); - LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); - - furi_hal_gpio_init_ex( - &gpio_usart_tx, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn7USART1); - furi_hal_gpio_init_ex( - &gpio_usart_rx, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn7USART1); - - LL_USART_InitTypeDef USART_InitStruct; - USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; - USART_InitStruct.BaudRate = baud; - USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; - USART_InitStruct.StopBits = LL_USART_STOPBITS_1; - USART_InitStruct.Parity = LL_USART_PARITY_NONE; - USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; - USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; - USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; - LL_USART_Init(USART1, &USART_InitStruct); - LL_USART_EnableFIFO(USART1); - LL_USART_ConfigAsyncMode(USART1); - - LL_USART_Enable(USART1); - - while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) - ; - - LL_USART_DisableIT_ERROR(USART1); - - NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); -} - -static void furi_hal_lpuart_init(uint32_t baud) { - furi_hal_bus_enable(FuriHalBusLPUART1); - LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); - - furi_hal_gpio_init_ex( - &gpio_ext_pc0, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn8LPUART1); - furi_hal_gpio_init_ex( - &gpio_ext_pc1, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn8LPUART1); - - LL_LPUART_InitTypeDef LPUART_InitStruct; - LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; - LPUART_InitStruct.BaudRate = 115200; - LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; - LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; - LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; - LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; - LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; - LL_LPUART_Init(LPUART1, &LPUART_InitStruct); - LL_LPUART_EnableFIFO(LPUART1); - - LL_LPUART_Enable(LPUART1); - - while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) - ; - - furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); - LL_LPUART_DisableIT_ERROR(LPUART1); - - NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); -} - -void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) { - if(ch == FuriHalUartIdLPUART1) { - furi_hal_lpuart_init(baud); - } else if(ch == FuriHalUartIdUSART1) { - furi_hal_usart_init(baud); - } -} - -void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { - if(ch == FuriHalUartIdUSART1) { - if(LL_USART_IsEnabled(USART1)) { - // Wait for transfer complete flag - while(!LL_USART_IsActiveFlag_TC(USART1)) - ; - LL_USART_Disable(USART1); - uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); - LL_USART_SetBaudRate( - USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); - LL_USART_Enable(USART1); - } - } else if(ch == FuriHalUartIdLPUART1) { - if(LL_LPUART_IsEnabled(LPUART1)) { - // Wait for transfer complete flag - while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) - ; - LL_LPUART_Disable(LPUART1); - uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE); - if(uartclk / baud > 4095) { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); - } else { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); - } - LL_LPUART_Enable(LPUART1); - } - } -} - -void furi_hal_uart_deinit(FuriHalUartId ch) { - furi_hal_uart_set_irq_cb(ch, NULL, NULL); - if(ch == FuriHalUartIdUSART1) { - if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) { - furi_hal_bus_disable(FuriHalBusUSART1); - } - furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } else if(ch == FuriHalUartIdLPUART1) { - if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) { - furi_hal_bus_disable(FuriHalBusLPUART1); - } - furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } -} - -void furi_hal_uart_suspend(FuriHalUartId channel) { - if(channel == FuriHalUartIdLPUART1 && LL_LPUART_IsEnabled(LPUART1)) { - LL_LPUART_Disable(LPUART1); - furi_hal_usart_prev_enabled[channel] = true; - } else if(channel == FuriHalUartIdUSART1 && LL_USART_IsEnabled(USART1)) { - LL_USART_Disable(USART1); - furi_hal_usart_prev_enabled[channel] = true; - } -} - -void furi_hal_uart_resume(FuriHalUartId channel) { - if(!furi_hal_usart_prev_enabled[channel]) { - return; - } else if(channel == FuriHalUartIdLPUART1) { - LL_LPUART_Enable(LPUART1); - } else if(channel == FuriHalUartIdUSART1) { - LL_USART_Enable(USART1); - } - - furi_hal_usart_prev_enabled[channel] = false; -} - -void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { - if(ch == FuriHalUartIdUSART1) { - if(LL_USART_IsEnabled(USART1) == 0) return; - - while(buffer_size > 0) { - while(!LL_USART_IsActiveFlag_TXE(USART1)) - ; - - LL_USART_TransmitData8(USART1, *buffer); - buffer++; - buffer_size--; - } - - } else if(ch == FuriHalUartIdLPUART1) { - if(LL_LPUART_IsEnabled(LPUART1) == 0) return; - - while(buffer_size > 0) { - while(!LL_LPUART_IsActiveFlag_TXE(LPUART1)) - ; - - LL_LPUART_TransmitData8(LPUART1, *buffer); - - buffer++; - buffer_size--; - } - } -} - -void furi_hal_uart_set_irq_cb( - FuriHalUartId ch, - void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), - void* ctx) { - if(cb == NULL) { - if(ch == FuriHalUartIdUSART1) { - NVIC_DisableIRQ(USART1_IRQn); - LL_USART_DisableIT_RXNE_RXFNE(USART1); - } else if(ch == FuriHalUartIdLPUART1) { - NVIC_DisableIRQ(LPUART1_IRQn); - LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); - } - irq_cb[ch] = cb; - irq_ctx[ch] = ctx; - } else { - irq_ctx[ch] = ctx; - irq_cb[ch] = cb; - if(ch == FuriHalUartIdUSART1) { - NVIC_EnableIRQ(USART1_IRQn); - LL_USART_EnableIT_RXNE_RXFNE(USART1); - } else if(ch == FuriHalUartIdLPUART1) { - NVIC_EnableIRQ(LPUART1_IRQn); - LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); - } - } -} - -void LPUART1_IRQHandler(void) { - if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { - uint8_t data = LL_LPUART_ReceiveData8(LPUART1); - irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]); - } else if(LL_LPUART_IsActiveFlag_ORE(LPUART1)) { - LL_LPUART_ClearFlag_ORE(LPUART1); - } -} - -void USART1_IRQHandler(void) { - if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { - uint8_t data = LL_USART_ReceiveData8(USART1); - irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdUSART1]); - } else if(LL_USART_IsActiveFlag_ORE(USART1)) { - LL_USART_ClearFlag_ORE(USART1); - } -} diff --git a/targets/f7/furi_hal/furi_hal_uart.h b/targets/f7/furi_hal/furi_hal_uart.h deleted file mode 100644 index 3ba4dc483..000000000 --- a/targets/f7/furi_hal/furi_hal_uart.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @file furi_hal_uart.h - * @version 1.0 - * @date 2021-11-19 - * - * UART HAL api interface - */ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UART channels - */ -typedef enum { - FuriHalUartIdUSART1, - FuriHalUartIdLPUART1, -} FuriHalUartId; - -/** - * UART events - */ -typedef enum { - UartIrqEventRXNE, -} UartIrqEvent; - -/** - * Init UART - * Configures GPIO to UART function, configures UART hardware, enables UART hardware - * @param channel UART channel - * @param baud baudrate - */ -void furi_hal_uart_init(FuriHalUartId channel, uint32_t baud); - -/** - * Deinit UART - * Configures GPIO to analog, clears callback and callback context, disables UART hardware - * @param channel UART channel - */ -void furi_hal_uart_deinit(FuriHalUartId channel); - -/** - * Suspend UART operation - * Disables UART hardware, settings and callbacks are preserved - * @param channel UART channel - */ -void furi_hal_uart_suspend(FuriHalUartId channel); - -/** - * Resume UART operation - * Resumes UART hardware from suspended state - * @param channel UART channel - */ -void furi_hal_uart_resume(FuriHalUartId channel); - -/** - * Changes UART baudrate - * @param channel UART channel - * @param baud baudrate - */ -void furi_hal_uart_set_br(FuriHalUartId channel, uint32_t baud); - -/** - * Transmits data - * @param channel UART channel - * @param buffer data - * @param buffer_size data size (in bytes) - */ -void furi_hal_uart_tx(FuriHalUartId channel, uint8_t* buffer, size_t buffer_size); - -/** - * Sets UART event callback - * @param channel UART channel - * @param callback callback pointer - * @param context callback context - */ -void furi_hal_uart_set_irq_cb( - FuriHalUartId channel, - void (*callback)(UartIrqEvent event, uint8_t data, void* context), - void* context); - -#ifdef __cplusplus -} -#endif diff --git a/targets/furi_hal_include/furi_hal.h b/targets/furi_hal_include/furi_hal.h index e6fd9eb1c..4f8aad6bd 100644 --- a/targets/furi_hal_include/furi_hal.h +++ b/targets/furi_hal_include/furi_hal.h @@ -14,7 +14,6 @@ struct STOP_EXTERNING_ME {}; #include #include #include -#include #include #include #include @@ -36,7 +35,8 @@ struct STOP_EXTERNING_ME {}; #include #include #include -#include +#include +#include #include #include #include From 8d60c0ff217e1725d83c71510b4f171468c03a69 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:21:37 +0300 Subject: [PATCH 07/15] fix typo --- applications/settings/system/system_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 72036a647..5d7f3193d 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -123,7 +123,7 @@ const uint32_t measurement_units_value[] = { LocaleMeasurementUnitsImperial, }; -static void mesurement_units_changed(VariableItem* item) { +static void measurement_units_changed(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, measurement_units_text[index]); locale_set_measurement_unit(measurement_units_value[index]); From 11ecb54576bfa83ade1ef95a0cfb03cef39b6c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 16 Jan 2024 08:17:07 +0900 Subject: [PATCH 08/15] FuriHal: interrupt priorities and documentation (#3366) * FuriHal: interrupt priorities and documentation * FuriHal: wording * FuriHal: update interrupt docs * FuriHal: add more interrupt priority levels * FuriHal: proper furi_check in interrupts, shift default level to 10 --------- Co-authored-by: hedger --- lib/signal_reader/signal_reader.c | 5 ++- targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 2 +- targets/f7/furi_hal/furi_hal_infrared.c | 7 +++- targets/f7/furi_hal/furi_hal_interrupt.c | 15 +++++--- targets/f7/furi_hal/furi_hal_interrupt.h | 47 +++++++++++++++++++----- 6 files changed, 58 insertions(+), 20 deletions(-) diff --git a/lib/signal_reader/signal_reader.c b/lib/signal_reader/signal_reader.c index 7c4d0bae7..1c08d29f4 100644 --- a/lib/signal_reader/signal_reader.c +++ b/lib/signal_reader/signal_reader.c @@ -278,7 +278,10 @@ void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, // Start DMA irq, higher priority than normal furi_hal_interrupt_set_isr_ex( - SIGNAL_READER_DMA_GPIO_IRQ, 14, furi_hal_sw_digital_pin_dma_rx_isr, instance); + SIGNAL_READER_DMA_GPIO_IRQ, + FuriHalInterruptPriorityHighest, + furi_hal_sw_digital_pin_dma_rx_isr, + instance); // Start DMA Sync timer LL_DMA_EnableChannel(SIGNAL_READER_DMA_CNT_SYNC_DEF); diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 960cee658..7bbb6b13f 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1149,7 +1149,7 @@ Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" -Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*" Function,+,furi_hal_light_blink_set_color,void,Light Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" Function,+,furi_hal_light_blink_stop,void, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index ddfbf9b63..c71304068 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1286,7 +1286,7 @@ Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" -Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*" Function,+,furi_hal_light_blink_set_color,void,Light Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" Function,+,furi_hal_light_blink_stop,void, diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c index 9c0d84c55..03a16b70f 100644 --- a/targets/f7/furi_hal/furi_hal_infrared.c +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -411,7 +411,10 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { LL_DMA_EnableIT_TC(INFRARED_DMA_CH1_DEF); furi_hal_interrupt_set_isr_ex( - INFRARED_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); + INFRARED_DMA_CH1_IRQ, + FuriHalInterruptPriorityKamiSama, + furi_hal_infrared_tx_dma_polarity_isr, + NULL); } static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { @@ -441,7 +444,7 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { LL_DMA_EnableIT_HT(INFRARED_DMA_CH2_DEF); LL_DMA_EnableIT_TE(INFRARED_DMA_CH2_DEF); - furi_hal_interrupt_set_isr_ex(INFRARED_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); + furi_hal_interrupt_set_isr(INFRARED_DMA_CH2_IRQ, furi_hal_infrared_tx_dma_isr, NULL); } static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index 6410b1090..a9cd4e7aa 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -11,7 +11,7 @@ #define TAG "FuriHalInterrupt" -#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) +#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 5) typedef struct { FuriHalInterruptISR isr; @@ -126,16 +126,21 @@ void furi_hal_interrupt_init() { } void furi_hal_interrupt_set_isr(FuriHalInterruptId index, FuriHalInterruptISR isr, void* context) { - furi_hal_interrupt_set_isr_ex(index, FURI_HAL_INTERRUPT_DEFAULT_PRIORITY, isr, context); + furi_hal_interrupt_set_isr_ex(index, FuriHalInterruptPriorityNormal, isr, context); } void furi_hal_interrupt_set_isr_ex( FuriHalInterruptId index, - uint16_t priority, + FuriHalInterruptPriority priority, FuriHalInterruptISR isr, void* context) { furi_check(index < FuriHalInterruptIdMax); - furi_check(priority <= 15); + furi_check( + (priority >= FuriHalInterruptPriorityLowest && + priority <= FuriHalInterruptPriorityHighest) || + priority == FuriHalInterruptPriorityKamiSama); + + uint16_t real_priority = FURI_HAL_INTERRUPT_DEFAULT_PRIORITY - priority; if(isr) { // Pre ISR set @@ -153,7 +158,7 @@ void furi_hal_interrupt_set_isr_ex( if(isr) { // Post ISR set furi_hal_interrupt_clear_pending(index); - furi_hal_interrupt_enable(index, priority); + furi_hal_interrupt_enable(index, real_priority); } else { // Post ISR clear } diff --git a/targets/f7/furi_hal/furi_hal_interrupt.h b/targets/f7/furi_hal/furi_hal_interrupt.h index 80a6323bd..03d7850f9 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.h +++ b/targets/f7/furi_hal/furi_hal_interrupt.h @@ -59,27 +59,54 @@ typedef enum { FuriHalInterruptIdMax, } FuriHalInterruptId; +typedef enum { + FuriHalInterruptPriorityLowest = + -3, /**< Lowest priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityLower = + -2, /**< Lower priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityLow = + -1, /**< Low priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityNormal = + 0, /**< Normal(default) priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHigh = + 1, /**< High priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHigher = + 2, /**< Higher priority level, you can use ISR-safe OS primitives */ + FuriHalInterruptPriorityHighest = + 3, /**< Highest priority level, you can use ISR-safe OS primitives */ + + /* Special group, read docs first(ALL OF THEM: especially FreeRTOS configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY) */ + FuriHalInterruptPriorityKamiSama = + 6, /**< Forget about thread safety, you are god now. No one can prevent you from messing with OS critical section. You are not allowed to use any OS primitives, but who can stop you? Use this priority only for direct hardware interaction with LL HAL. */ +} FuriHalInterruptPriority; + /** Initialize interrupt subsystem */ void furi_hal_interrupt_init(); /** Set ISR and enable interrupt with default priority - * We don't clear interrupt flags for you, do it by your self. - * @param index - interrupt ID - * @param isr - your interrupt service routine or use NULL to clear - * @param context - isr context + * + * @warning Interrupt flags are not cleared automatically. You may want to + * ensure that your peripheral status flags are cleared. + * + * @param index - interrupt ID + * @param isr - your interrupt service routine or use NULL to clear + * @param context - isr context */ void furi_hal_interrupt_set_isr(FuriHalInterruptId index, FuriHalInterruptISR isr, void* context); /** Set ISR and enable interrupt with custom priority - * We don't clear interrupt flags for you, do it by your self. - * @param index - interrupt ID - * @param priority - 0 to 15, 0 highest - * @param isr - your interrupt service routine or use NULL to clear - * @param context - isr context + * + * @warning Interrupt flags are not cleared automatically. You may want to + * ensure that your peripheral status flags are cleared. + * + * @param index - interrupt ID + * @param priority - One of FuriHalInterruptPriority + * @param isr - your interrupt service routine or use NULL to clear + * @param context - isr context */ void furi_hal_interrupt_set_isr_ex( FuriHalInterruptId index, - uint16_t priority, + FuriHalInterruptPriority priority, FuriHalInterruptISR isr, void* context); From f37d00a8bad78eb1a4815be809bd7121373b9e5f Mon Sep 17 00:00:00 2001 From: John Scarfone Date: Tue, 16 Jan 2024 03:31:50 -0500 Subject: [PATCH 09/15] Bugfix: Strip last parity bit from decoded FDX-B data (#3199) * remove last parity bit from buffer * add unit tests * zap old debug logging --------- Co-authored-by: Sergei Gavrilov --- .../unit_tests/lfrfid/lfrfid_protocols.c | 89 +++++++++++++++++++ lib/lfrfid/protocols/protocol_fdx_b.c | 4 +- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c b/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c index 4401cbb4d..d5c2433ba 100644 --- a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c +++ b/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c @@ -209,6 +209,25 @@ const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = { -1, 1, -1, 1, -1, 1, -1, 1, }; +#define FDXB_TEST_DATA \ + { 0x44, 0x88, 0x23, 0xF2, 0x5A, 0x6F, 0x00, 0x01, 0x00, 0x00, 0x00 } +#define FDXB_TEST_DATA_SIZE 11 +#define FDXB_TEST_EMULATION_TIMINGS_COUNT (206) + +const int8_t fdxb_test_timings[FDXB_TEST_EMULATION_TIMINGS_COUNT] = { + 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -16, 16, -32, 16, -16, 32, -16, 16, -16, 16, -16, 16, -32, 16, -16, 16, -16, 32, -32, + 16, -16, 16, -16, 16, -16, 32, -16, 16, -16, 16, -16, 16, -32, 16, -16, 16, -16, 32, + -16, 16, -16, 16, -16, 16, -32, 32, -32, 32, -32, 32, -32, 16, -16, 16, -16, 32, -16, + 16, -32, 16, -16, 32, -16, 16, -32, 32, -16, 16, -32, 16, -16, 32, -16, 16, -32, 32, + -16, 16, -32, 32, -32, 32, -32, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, + 16, -16, 16, -16, 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -32, 32, -32, 32, -32, 32, -32, 16, -16, 32, -32, 32, -16, 16, -16, 16, -32, 32, -32, + 32, -32, 32, -32, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, + -16, 32, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -32, + 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, 16, -16, +}; + MU_TEST(test_lfrfid_protocol_em_read_simple) { ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); @@ -445,6 +464,73 @@ MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) { protocol_dict_free(dict); } +MU_TEST(test_lfrfid_protocol_fdxb_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(FDXB_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("FDX-B", protocol_dict_get_name(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("ISO", protocol_dict_get_manufacturer(dict, LFRFIDProtocolFDXB)); + + const uint8_t data[FDXB_TEST_DATA_SIZE] = FDXB_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolFDXB, data, FDXB_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolFDXB)); + + for(size_t i = 0; i < FDXB_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolFDXB); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq(fdxb_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq(fdxb_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_fdxb_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(FDXB_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("FDX-B", protocol_dict_get_name(dict, LFRFIDProtocolFDXB)); + mu_assert_string_eq("ISO", protocol_dict_get_manufacturer(dict, LFRFIDProtocolFDXB)); + + const uint8_t data[FDXB_TEST_DATA_SIZE] = FDXB_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < FDXB_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + fdxb_test_timings[i % FDXB_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(fdxb_test_timings[i % FDXB_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolFDXB, protocol); + uint8_t received_data[FDXB_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, FDXB_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, FDXB_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + MU_TEST_SUITE(test_lfrfid_protocols_suite) { MU_RUN_TEST(test_lfrfid_protocol_em_read_simple); MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple); @@ -456,6 +542,9 @@ MU_TEST_SUITE(test_lfrfid_protocols_suite) { MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple); MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_fdxb_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_fdxb_emulate_simple); } int run_minunit_test_lfrfid_protocols() { diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c index 04386a675..a3ab56f25 100644 --- a/lib/lfrfid/protocols/protocol_fdx_b.c +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -101,7 +101,7 @@ static bool protocol_fdx_b_can_be_decoded(ProtocolFDXB* protocol) { void protocol_fdx_b_decode(ProtocolFDXB* protocol) { // remove parity - bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 13 * 9, 9); + bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 14 * 9, 9); // remove header pattern for(size_t i = 0; i < 11; i++) @@ -119,7 +119,7 @@ void protocol_fdx_b_decode(ProtocolFDXB* protocol) { // 72 xxxxxxxx // 80 eeeeeeee 24 bits of extra data if present. // 88 eeeeeeee eg. $123456. - // 92 eeeeeeee + // 96 eeeeeeee // copy data without checksum bit_lib_copy_bits(protocol->data, 0, 64, protocol->encoded_data, 0); From c27494ac39d8cbed7bafe5c85a246a1b4db595a5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:22:48 +0300 Subject: [PATCH 10/15] [FL-3669] Expansion module protocol (#3250) * ApiSymbols: add furi_record_destroy * FuriHal: cleanup serial API, add logging configuration in RTC * FuriHal: hide private part in _i header. Toolbox: cleanup value index. SystemSettings: logging device and baudrate. * FuriHal: RTC logging method documentation * Synchronize API Symbols * Furi: mark HEAP_PRINT_DEBUG as broken * FuriHal: furi_hal_serial, add custom IRQ func * Fix PR review issues * Implement basic external module detection and echo * Update api symbols for f18 * Minimally working implementation (can create directory via rpc) * Make expansion protocol parser a header-only library * Rename a function * Improve thread syncronisation * Implement multi-packet transmissions * Improve test application * Clean up expansion worker code * Send heartbeat when host is ready * Update API symbols * Add draft documentation * Expansion worker: proper timeout and error handling * Expansion worker: correct TX, do not disable expansion callback * Expansion protocol: pc side test script * PC side expansion test: trying to change baudrate * Working comms between 2 flippers * Cleaner exit from expansion worker thread * Better checks * Add debug logs * Remove unneeded delays * Use USART as default expansion port * Refactor furi_hal_serial_control, fix crash * Improve furi_hal abstraction, wait for stable rx pin * Remove rogue include * Set proper exit reason on RPC error * Remove rogue comment * Remove RX stability check as potentially problematic * Improve expansion_test application * Remove rogue define * Give up on TODO * Implement expansion protocol checksum support * Update ExpansionModules.md * RPC: reverse input * Assets: sync protobuf * Fix typos * FuriHal: UART add reception DMA (#3220) * FuriHal: add DMA serial rx mode * usb_uart_bridge: switch to working with DMA * Sync api symbol versions * FuriHal: update serial docs and api * FuriHal: Selial added similar API for simple reception mode as with DMA * FuriHal: Update API target H18 * API: ver API H7 * FuriHal: Serial error processing * FuriHal: fix furi_hal_serial set baudrate * Sync api symbols * FuriHal: cleanup serial isr and various flag handling procedures * FuriHal: cleanup and simplify serial API * Debug: update UART Echo serial related flags * FuriHal: update serial API symbols naming * Make expansion_test compile * Remove unneeded file * Make PVS-studio happy * Optimise stack usage * Optimise heap usage, improve api signature * Fix typo * Clean up code * Update expansion_protocol.h * Fix unit tests * Add doxygen comments to expansion.h * Update/add doxygen comments * Update ExpansionModules.md * Github: new global code owner * FuriHal: naming in serial control * Expansion: check mutex acquire return result Co-authored-by: Aleksandr Kutuzov Co-authored-by: hedger Co-authored-by: SkorP Co-authored-by: SG Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com> --- .../debug/expansion_test/application.fam | 12 + .../debug/expansion_test/assets/test.txt | 9 + .../debug/expansion_test/expansion_test.c | 454 ++++++++++++++++++ .../unit_tests/expansion/expansion_test.c | 157 ++++++ applications/debug/unit_tests/test_index.c | 2 + applications/services/application.fam | 1 + .../services/expansion/application.fam | 12 + applications/services/expansion/expansion.c | 437 +++++++++++++++++ applications/services/expansion/expansion.h | 50 ++ .../services/expansion/expansion_protocol.h | 338 +++++++++++++ .../services/expansion/expansion_settings.c | 30 ++ .../services/expansion/expansion_settings.h | 43 ++ .../expansion/expansion_settings_filename.h | 9 + applications/services/rpc/rpc.c | 7 +- applications/services/rpc/rpc.h | 5 +- applications/services/rpc/rpc_gui.c | 6 +- .../expansion_settings_app/application.fam | 9 + .../expansion_settings_app.c | 91 ++++ .../expansion_settings_app.h | 23 + documentation/ExpansionModules.md | 164 +++++++ furi/core/log.c | 2 +- furi/core/stream_buffer.h | 2 +- targets/f18/api_symbols.csv | 18 +- targets/f7/api_symbols.csv | 18 +- targets/f7/furi_hal/furi_hal_serial.c | 99 ++++ targets/f7/furi_hal/furi_hal_serial.h | 45 ++ targets/f7/furi_hal/furi_hal_serial_control.c | 236 +++++++-- targets/f7/furi_hal/furi_hal_serial_control.h | 21 + targets/f7/furi_hal/furi_hal_serial_types.h | 7 + 29 files changed, 2236 insertions(+), 71 deletions(-) create mode 100644 applications/debug/expansion_test/application.fam create mode 100644 applications/debug/expansion_test/assets/test.txt create mode 100644 applications/debug/expansion_test/expansion_test.c create mode 100644 applications/debug/unit_tests/expansion/expansion_test.c create mode 100644 applications/services/expansion/application.fam create mode 100644 applications/services/expansion/expansion.c create mode 100644 applications/services/expansion/expansion.h create mode 100644 applications/services/expansion/expansion_protocol.h create mode 100644 applications/services/expansion/expansion_settings.c create mode 100644 applications/services/expansion/expansion_settings.h create mode 100644 applications/services/expansion/expansion_settings_filename.h create mode 100644 applications/settings/expansion_settings_app/application.fam create mode 100644 applications/settings/expansion_settings_app/expansion_settings_app.c create mode 100644 applications/settings/expansion_settings_app/expansion_settings_app.h create mode 100644 documentation/ExpansionModules.md diff --git a/applications/debug/expansion_test/application.fam b/applications/debug/expansion_test/application.fam new file mode 100644 index 000000000..9bc4b2fc2 --- /dev/null +++ b/applications/debug/expansion_test/application.fam @@ -0,0 +1,12 @@ +App( + appid="expansion_test", + name="Expansion Module Test", + apptype=FlipperAppType.DEBUG, + entry_point="expansion_test_app", + requires=["expansion_start"], + fap_libs=["assets"], + stack_size=1 * 1024, + order=20, + fap_category="Debug", + fap_file_assets="assets", +) diff --git a/applications/debug/expansion_test/assets/test.txt b/applications/debug/expansion_test/assets/test.txt new file mode 100644 index 000000000..e39b1eec5 --- /dev/null +++ b/applications/debug/expansion_test/assets/test.txt @@ -0,0 +1,9 @@ +"Did you ever hear the tragedy of Darth Plagueis the Wise?" +"No." +"I thought not. It's not a story the Jedi would tell you. It's a Sith legend. Darth Plagueis... was a Dark Lord of the Sith so powerful and so wise, he could use the Force to influence the midi-chlorians... to create... life. He had such a knowledge of the dark side, he could even keep the ones he cared about... from dying." +"He could actually... save people from death?" +"The dark side of the Force is a pathway to many abilities... some consider to be unnatural." +"Wh– What happened to him?" +"He became so powerful, the only thing he was afraid of was... losing his power. Which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew. Then his apprentice killed him in his sleep. It's ironic. He could save others from death, but not himself." +"Is it possible to learn this power?" +"Not from a Jedi." diff --git a/applications/debug/expansion_test/expansion_test.c b/applications/debug/expansion_test/expansion_test.c new file mode 100644 index 000000000..73863798e --- /dev/null +++ b/applications/debug/expansion_test/expansion_test.c @@ -0,0 +1,454 @@ +/** + * @file expansion_test.c + * @brief Expansion module support testing application. + * + * Before running, connect pins using the following scheme: + * 13 -> 16 (USART TX to LPUART RX) + * 14 -> 15 (USART RX to LPUART TX) + * + * What this application does: + * + * - Enables module support and emulates the module on a single device + * (hence the above connection), + * - Connects to the expansion module service, sets baud rate, + * - Starts the RPC session, + * - Creates a directory at `/ext/ExpansionTest` and writes a file + * named `test.txt` under it, + * - Plays an audiovisual alert (sound and blinking display), + * - Waits 10 cycles of idle loop, + * - Stops the RPC session, + * - Waits another 10 cycles of idle loop, + * - Exits (plays a sound if any of the above steps failed). + */ +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define TAG "ExpansionTest" + +#define TEST_DIR_PATH EXT_PATH(TAG) +#define TEST_FILE_NAME "test.txt" +#define TEST_FILE_PATH EXT_PATH(TAG "/" TEST_FILE_NAME) + +#define HOST_SERIAL_ID (FuriHalSerialIdLpuart) +#define MODULE_SERIAL_ID (FuriHalSerialIdUsart) + +#define RECEIVE_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)) + +typedef enum { + ExpansionTestAppFlagData = 1U << 0, + ExpansionTestAppFlagExit = 1U << 1, +} ExpansionTestAppFlag; + +#define EXPANSION_TEST_APP_ALL_FLAGS (ExpansionTestAppFlagData | ExpansionTestAppFlagExit) + +typedef struct { + FuriThreadId thread_id; + Expansion* expansion; + FuriHalSerialHandle* handle; + FuriStreamBuffer* buf; + ExpansionFrame frame; + PB_Main msg; + Storage* storage; +} ExpansionTestApp; + +static void expansion_test_app_serial_rx_callback( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + furi_assert(handle); + furi_assert(context); + ExpansionTestApp* app = context; + + if(event == FuriHalSerialRxEventData) { + const uint8_t data = furi_hal_serial_async_rx(handle); + furi_stream_buffer_send(app->buf, &data, sizeof(data), 0); + furi_thread_flags_set(app->thread_id, ExpansionTestAppFlagData); + } +} + +static ExpansionTestApp* expansion_test_app_alloc() { + ExpansionTestApp* instance = malloc(sizeof(ExpansionTestApp)); + instance->buf = furi_stream_buffer_alloc(RECEIVE_BUFFER_SIZE, 1); + return instance; +} + +static void expansion_test_app_free(ExpansionTestApp* instance) { + furi_stream_buffer_free(instance->buf); + free(instance); +} + +static void expansion_test_app_start(ExpansionTestApp* instance) { + instance->thread_id = furi_thread_get_current_id(); + instance->expansion = furi_record_open(RECORD_EXPANSION); + instance->handle = furi_hal_serial_control_acquire(MODULE_SERIAL_ID); + // Configure the serial port + furi_hal_serial_init(instance->handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); + // Start waiting for the initial pulse + expansion_enable(instance->expansion, HOST_SERIAL_ID); + + furi_hal_serial_async_rx_start( + instance->handle, expansion_test_app_serial_rx_callback, instance, false); +} + +static void expansion_test_app_stop(ExpansionTestApp* instance) { + // Give back the module handle + furi_hal_serial_control_release(instance->handle); + // Turn expansion module support off + expansion_disable(instance->expansion); + furi_record_close(RECORD_EXPANSION); +} + +static inline bool expansion_test_app_is_success_response(const ExpansionFrame* response) { + return response->header.type == ExpansionFrameTypeStatus && + response->content.status.error == ExpansionFrameErrorNone; +} + +static inline bool expansion_test_app_is_success_rpc_message(const PB_Main* message) { + return (message->command_status == PB_CommandStatus_OK || + message->command_status == PB_CommandStatus_ERROR_STORAGE_EXIST) && + (message->which_content == PB_Main_empty_tag); +} + +static size_t expansion_test_app_receive_callback(uint8_t* data, size_t data_size, void* context) { + ExpansionTestApp* instance = context; + + size_t received_size = 0; + + while(true) { + received_size += furi_stream_buffer_receive( + instance->buf, data + received_size, data_size - received_size, 0); + if(received_size == data_size) break; + + const uint32_t flags = furi_thread_flags_wait( + EXPANSION_TEST_APP_ALL_FLAGS, FuriFlagWaitAny, EXPANSION_PROTOCOL_TIMEOUT_MS); + + // Exit on any error + if(flags & FuriFlagError) break; + } + + return received_size; +} + +static size_t + expansion_test_app_send_callback(const uint8_t* data, size_t data_size, void* context) { + ExpansionTestApp* instance = context; + + furi_hal_serial_tx(instance->handle, data, data_size); + furi_hal_serial_tx_wait_complete(instance->handle); + + return data_size; +} + +static bool expansion_test_app_receive_frame(ExpansionTestApp* instance, ExpansionFrame* frame) { + return expansion_protocol_decode(frame, expansion_test_app_receive_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool + expansion_test_app_send_status_response(ExpansionTestApp* instance, ExpansionFrameError error) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeStatus, + .content.status.error = error, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_heartbeat(ExpansionTestApp* instance) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeHeartbeat, + .content.heartbeat = {}, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool + expansion_test_app_send_baud_rate_request(ExpansionTestApp* instance, uint32_t baud_rate) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeBaudRate, + .content.baud_rate.baud = baud_rate, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_control_request( + ExpansionTestApp* instance, + ExpansionFrameControlCommand command) { + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeControl, + .content.control.command = command, + }; + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_send_data_request( + ExpansionTestApp* instance, + const uint8_t* data, + size_t data_size) { + furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE); + + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeData, + .content.data.size = data_size, + }; + + memcpy(frame.content.data.bytes, data, data_size); + return expansion_protocol_encode(&frame, expansion_test_app_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_test_app_rpc_encode_callback( + pb_ostream_t* stream, + const pb_byte_t* data, + size_t data_size) { + ExpansionTestApp* instance = stream->state; + + size_t size_sent = 0; + + while(size_sent < data_size) { + const size_t current_size = MIN(data_size - size_sent, EXPANSION_PROTOCOL_MAX_DATA_SIZE); + if(!expansion_test_app_send_data_request(instance, data + size_sent, current_size)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + size_sent += current_size; + } + + return size_sent == data_size; +} + +static bool expansion_test_app_send_rpc_request(ExpansionTestApp* instance, PB_Main* message) { + pb_ostream_t stream = { + .callback = expansion_test_app_rpc_encode_callback, + .state = instance, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL, + }; + + const bool success = pb_encode_ex(&stream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); + pb_release(&PB_Main_msg, message); + return success; +} + +static bool expansion_test_app_receive_rpc_request(ExpansionTestApp* instance, PB_Main* message) { + bool success = false; + + do { + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_send_status_response(instance, ExpansionFrameErrorNone)) break; + if(instance->frame.header.type != ExpansionFrameTypeData) break; + pb_istream_t stream = pb_istream_from_buffer( + instance->frame.content.data.bytes, instance->frame.content.data.size); + if(!pb_decode_ex(&stream, &PB_Main_msg, message, PB_DECODE_DELIMITED)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_send_presence(ExpansionTestApp* instance) { + // Send pulses to emulate module insertion + const uint8_t init = 0xAA; + furi_hal_serial_tx(instance->handle, &init, sizeof(init)); + furi_hal_serial_tx_wait_complete(instance->handle); + return true; +} + +static bool expansion_test_app_wait_ready(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(instance->frame.header.type != ExpansionFrameTypeHeartbeat) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_handshake(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_baud_rate_request(instance, 230400)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + furi_hal_serial_set_br(instance->handle, 230400); + furi_delay_ms(EXPANSION_PROTOCOL_BAUD_CHANGE_DT_MS); + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_start_rpc(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_control_request(instance, ExpansionFrameControlCommandStartRpc)) + break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_rpc_mkdir(ExpansionTestApp* instance) { + bool success = false; + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_storage_mkdir_request_tag; + instance->msg.has_next = false; + instance->msg.content.storage_mkdir_request.path = TEST_DIR_PATH; + + do { + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_is_success_rpc_message(&instance->msg)) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_rpc_write(ExpansionTestApp* instance) { + bool success = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + do { + if(!storage_file_open(file, APP_ASSETS_PATH(TEST_FILE_NAME), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + + const uint64_t file_size = storage_file_size(file); + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_storage_write_request_tag; + instance->msg.has_next = false; + instance->msg.content.storage_write_request.path = TEST_FILE_PATH; + instance->msg.content.storage_write_request.has_file = true; + instance->msg.content.storage_write_request.file.data = + malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(file_size)); + instance->msg.content.storage_write_request.file.data->size = file_size; + + const size_t bytes_read = storage_file_read( + file, instance->msg.content.storage_write_request.file.data->bytes, file_size); + + if(bytes_read != file_size) { + pb_release(&PB_Main_msg, &instance->msg); + break; + } + + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_is_success_rpc_message(&instance->msg)) break; + success = true; + } while(false); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return success; +} + +static bool expansion_test_app_rpc_alert(ExpansionTestApp* instance) { + bool success = false; + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_system_play_audiovisual_alert_request_tag; + instance->msg.has_next = false; + + do { + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(instance->msg.which_content != PB_Main_empty_tag) break; + if(instance->msg.command_status != PB_CommandStatus_OK) break; + success = true; + } while(false); + + return success; +} + +static bool expansion_test_app_idle(ExpansionTestApp* instance, uint32_t num_cycles) { + uint32_t num_cycles_done; + for(num_cycles_done = 0; num_cycles_done < num_cycles; ++num_cycles_done) { + if(!expansion_test_app_send_heartbeat(instance)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(instance->frame.header.type != ExpansionFrameTypeHeartbeat) break; + furi_delay_ms(EXPANSION_PROTOCOL_TIMEOUT_MS - 50); + } + + return num_cycles_done == num_cycles; +} + +static bool expansion_test_app_stop_rpc(ExpansionTestApp* instance) { + bool success = false; + + do { + if(!expansion_test_app_send_control_request(instance, ExpansionFrameControlCommandStopRpc)) + break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + success = true; + } while(false); + + return success; +} + +int32_t expansion_test_app(void* p) { + UNUSED(p); + + ExpansionTestApp* instance = expansion_test_app_alloc(); + expansion_test_app_start(instance); + + bool success = false; + + do { + if(!expansion_test_app_send_presence(instance)) break; + if(!expansion_test_app_wait_ready(instance)) break; + if(!expansion_test_app_handshake(instance)) break; + if(!expansion_test_app_start_rpc(instance)) break; + if(!expansion_test_app_rpc_mkdir(instance)) break; + if(!expansion_test_app_rpc_write(instance)) break; + if(!expansion_test_app_rpc_alert(instance)) break; + if(!expansion_test_app_idle(instance, 10)) break; + if(!expansion_test_app_stop_rpc(instance)) break; + if(!expansion_test_app_idle(instance, 10)) break; + success = true; + } while(false); + + expansion_test_app_stop(instance); + expansion_test_app_free(instance); + + if(!success) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_error); + furi_record_close(RECORD_NOTIFICATION); + } + + return 0; +} diff --git a/applications/debug/unit_tests/expansion/expansion_test.c b/applications/debug/unit_tests/expansion/expansion_test.c new file mode 100644 index 000000000..0513da537 --- /dev/null +++ b/applications/debug/unit_tests/expansion/expansion_test.c @@ -0,0 +1,157 @@ +#include "../minunit.h" + +#include +#include + +MU_TEST(test_expansion_encoded_size) { + ExpansionFrame frame = {}; + + frame.header.type = ExpansionFrameTypeHeartbeat; + mu_assert_int_eq(1, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeStatus; + mu_assert_int_eq(2, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeBaudRate; + mu_assert_int_eq(5, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeControl; + mu_assert_int_eq(2, expansion_frame_get_encoded_size(&frame)); + + frame.header.type = ExpansionFrameTypeData; + for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) { + frame.content.data.size = i; + mu_assert_int_eq(i + 2, expansion_frame_get_encoded_size(&frame)); + } +} + +MU_TEST(test_expansion_remaining_size) { + ExpansionFrame frame = {}; + + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + + frame.header.type = ExpansionFrameTypeHeartbeat; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeStatus; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 2)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeBaudRate; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(4, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 5)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeControl; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 2)); + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); + + frame.header.type = ExpansionFrameTypeData; + frame.content.data.size = EXPANSION_PROTOCOL_MAX_DATA_SIZE; + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 0)); + mu_assert_int_eq(1, expansion_frame_get_remaining_size(&frame, 1)); + mu_assert_int_eq( + EXPANSION_PROTOCOL_MAX_DATA_SIZE, expansion_frame_get_remaining_size(&frame, 2)); + for(size_t i = 0; i <= EXPANSION_PROTOCOL_MAX_DATA_SIZE; ++i) { + mu_assert_int_eq( + EXPANSION_PROTOCOL_MAX_DATA_SIZE - i, + expansion_frame_get_remaining_size(&frame, i + 2)); + } + mu_assert_int_eq(0, expansion_frame_get_remaining_size(&frame, 100)); +} + +typedef struct { + void* data_out; + size_t size_available; + size_t size_sent; +} TestExpansionSendStream; + +static size_t test_expansion_send_callback(const uint8_t* data, size_t data_size, void* context) { + TestExpansionSendStream* stream = context; + const size_t size_sent = MIN(data_size, stream->size_available); + + memcpy(stream->data_out + stream->size_sent, data, size_sent); + + stream->size_available -= size_sent; + stream->size_sent += size_sent; + + return size_sent; +} + +typedef struct { + const void* data_in; + size_t size_available; + size_t size_received; +} TestExpansionReceiveStream; + +static size_t test_expansion_receive_callback(uint8_t* data, size_t data_size, void* context) { + TestExpansionReceiveStream* stream = context; + const size_t size_received = MIN(data_size, stream->size_available); + + memcpy(data, stream->data_in + stream->size_received, size_received); + + stream->size_available -= size_received; + stream->size_received += size_received; + + return size_received; +} + +MU_TEST(test_expansion_encode_decode_frame) { + const ExpansionFrame frame_in = { + .header.type = ExpansionFrameTypeData, + .content.data.size = 8, + .content.data.bytes = {0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xca, 0xfe}, + }; + + uint8_t encoded_data[sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)]; + memset(encoded_data, 0, sizeof(encoded_data)); + + TestExpansionSendStream send_stream = { + .data_out = &encoded_data, + .size_available = sizeof(encoded_data), + .size_sent = 0, + }; + + const size_t encoded_size = expansion_frame_get_encoded_size(&frame_in); + + mu_assert_int_eq( + expansion_protocol_encode(&frame_in, test_expansion_send_callback, &send_stream), + ExpansionProtocolStatusOk); + mu_assert_int_eq(encoded_size + sizeof(ExpansionFrameChecksum), send_stream.size_sent); + mu_assert_int_eq( + expansion_protocol_get_checksum((const uint8_t*)&frame_in, encoded_size), + encoded_data[encoded_size]); + mu_assert_mem_eq(&frame_in, &encoded_data, encoded_size); + + TestExpansionReceiveStream stream = { + .data_in = encoded_data, + .size_available = send_stream.size_sent, + .size_received = 0, + }; + + ExpansionFrame frame_out; + + mu_assert_int_eq( + expansion_protocol_decode(&frame_out, test_expansion_receive_callback, &stream), + ExpansionProtocolStatusOk); + mu_assert_int_eq(encoded_size + sizeof(ExpansionFrameChecksum), stream.size_received); + mu_assert_mem_eq(&frame_in, &frame_out, encoded_size); +} + +MU_TEST_SUITE(test_expansion_suite) { + MU_RUN_TEST(test_expansion_encoded_size); + MU_RUN_TEST(test_expansion_remaining_size); + MU_RUN_TEST(test_expansion_encode_decode_frame); +} + +int run_minunit_test_expansion() { + MU_RUN_SUITE(test_expansion_suite); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index d7afaa3c4..7ae9ca03d 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -29,6 +29,7 @@ int run_minunit_test_bit_lib(); int run_minunit_test_float_tools(); int run_minunit_test_bt(); int run_minunit_test_dialogs_file_browser_options(); +int run_minunit_test_expansion(); typedef int (*UnitTestEntry)(); @@ -60,6 +61,7 @@ const UnitTest unit_tests[] = { {.name = "bt", .entry = run_minunit_test_bt}, {.name = "dialogs_file_browser_options", .entry = run_minunit_test_dialogs_file_browser_options}, + {.name = "expansion", .entry = run_minunit_test_expansion}, }; void minunit_print_progress() { diff --git a/applications/services/application.fam b/applications/services/application.fam index 0b5009096..90631408a 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -5,6 +5,7 @@ App( provides=[ "crypto_start", "rpc_start", + "expansion_start", "bt", "desktop", "loader", diff --git a/applications/services/expansion/application.fam b/applications/services/expansion/application.fam new file mode 100644 index 000000000..1402e8413 --- /dev/null +++ b/applications/services/expansion/application.fam @@ -0,0 +1,12 @@ +App( + appid="expansion_start", + apptype=FlipperAppType.STARTUP, + entry_point="expansion_on_system_start", + cdefines=["SRV_EXPANSION"], + sdk_headers=[ + "expansion.h", + ], + requires=["rpc_start"], + provides=["expansion_settings"], + order=10, +) diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c new file mode 100644 index 000000000..ca3b71444 --- /dev/null +++ b/applications/services/expansion/expansion.c @@ -0,0 +1,437 @@ +#include "expansion.h" + +#include +#include +#include + +#include + +#include + +#include "expansion_settings.h" +#include "expansion_protocol.h" + +#define TAG "ExpansionSrv" + +#define EXPANSION_BUFFER_SIZE (sizeof(ExpansionFrame) + sizeof(ExpansionFrameChecksum)) + +typedef enum { + ExpansionStateDisabled, + ExpansionStateEnabled, + ExpansionStateRunning, +} ExpansionState; + +typedef enum { + ExpansionSessionStateHandShake, + ExpansionSessionStateConnected, + ExpansionSessionStateRpcActive, +} ExpansionSessionState; + +typedef enum { + ExpansionSessionExitReasonUnknown, + ExpansionSessionExitReasonUser, + ExpansionSessionExitReasonError, + ExpansionSessionExitReasonTimeout, +} ExpansionSessionExitReason; + +typedef enum { + ExpansionFlagStop = 1 << 0, + ExpansionFlagData = 1 << 1, + ExpansionFlagError = 1 << 2, +} ExpansionFlag; + +#define EXPANSION_ALL_FLAGS (ExpansionFlagData | ExpansionFlagStop) + +struct Expansion { + ExpansionState state; + ExpansionSessionState session_state; + ExpansionSessionExitReason exit_reason; + FuriStreamBuffer* rx_buf; + FuriSemaphore* tx_semaphore; + FuriMutex* state_mutex; + FuriThread* worker_thread; + FuriHalSerialId serial_id; + FuriHalSerialHandle* serial_handle; + RpcSession* rpc_session; +}; + +static void expansion_detect_callback(void* context); + +// Called in UART IRQ context +static void expansion_serial_rx_callback( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent event, + void* context) { + furi_assert(handle); + furi_assert(context); + + Expansion* instance = context; + + if(event == FuriHalSerialRxEventData) { + const uint8_t data = furi_hal_serial_async_rx(handle); + furi_stream_buffer_send(instance->rx_buf, &data, sizeof(data), 0); + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagData); + } +} + +static size_t expansion_receive_callback(uint8_t* data, size_t data_size, void* context) { + Expansion* instance = context; + + size_t received_size = 0; + + while(true) { + received_size += furi_stream_buffer_receive( + instance->rx_buf, data + received_size, data_size - received_size, 0); + + if(received_size == data_size) break; + + const uint32_t flags = furi_thread_flags_wait( + EXPANSION_ALL_FLAGS, FuriFlagWaitAny, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)); + + if(flags & FuriFlagError) { + if(flags == (unsigned)FuriFlagErrorTimeout) { + // Exiting due to timeout + instance->exit_reason = ExpansionSessionExitReasonTimeout; + } else { + // Exiting due to an unspecified error + instance->exit_reason = ExpansionSessionExitReasonError; + } + break; + } else if(flags & ExpansionFlagStop) { + // Exiting due to explicit request + instance->exit_reason = ExpansionSessionExitReasonUser; + break; + } else if(flags & ExpansionFlagError) { + // Exiting due to RPC error + instance->exit_reason = ExpansionSessionExitReasonError; + break; + } else if(flags & ExpansionFlagData) { + // Go to buffer reading + continue; + } + } + + return received_size; +} + +static inline bool expansion_receive_frame(Expansion* instance, ExpansionFrame* frame) { + return expansion_protocol_decode(frame, expansion_receive_callback, instance) == + ExpansionProtocolStatusOk; +} + +static size_t expansion_send_callback(const uint8_t* data, size_t data_size, void* context) { + Expansion* instance = context; + furi_hal_serial_tx(instance->serial_handle, data, data_size); + furi_hal_serial_tx_wait_complete(instance->serial_handle); + return data_size; +} + +static inline bool expansion_send_frame(Expansion* instance, const ExpansionFrame* frame) { + return expansion_protocol_encode(frame, expansion_send_callback, instance) == + ExpansionProtocolStatusOk; +} + +static bool expansion_send_heartbeat(Expansion* instance) { + const ExpansionFrame frame = { + .header.type = ExpansionFrameTypeHeartbeat, + .content.heartbeat = {}, + }; + + return expansion_send_frame(instance, &frame); +} + +static bool expansion_send_status_response(Expansion* instance, ExpansionFrameError error) { + const ExpansionFrame frame = { + .header.type = ExpansionFrameTypeStatus, + .content.status.error = error, + }; + + return expansion_send_frame(instance, &frame); +} + +static bool + expansion_send_data_response(Expansion* instance, const uint8_t* data, size_t data_size) { + furi_assert(data_size <= EXPANSION_PROTOCOL_MAX_DATA_SIZE); + + ExpansionFrame frame = { + .header.type = ExpansionFrameTypeData, + .content.data.size = data_size, + }; + + memcpy(frame.content.data.bytes, data, data_size); + return expansion_send_frame(instance, &frame); +} + +// Called in Rpc session thread context +static void expansion_rpc_send_callback(void* context, uint8_t* data, size_t data_size) { + Expansion* instance = context; + + for(size_t sent_data_size = 0; sent_data_size < data_size;) { + if(furi_semaphore_acquire( + instance->tx_semaphore, furi_ms_to_ticks(EXPANSION_PROTOCOL_TIMEOUT_MS)) != + FuriStatusOk) { + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagError); + break; + } + + const size_t current_data_size = + MIN(data_size - sent_data_size, EXPANSION_PROTOCOL_MAX_DATA_SIZE); + if(!expansion_send_data_response(instance, data + sent_data_size, current_data_size)) + break; + sent_data_size += current_data_size; + } +} + +static bool expansion_rpc_session_open(Expansion* instance) { + Rpc* rpc = furi_record_open(RECORD_RPC); + instance->rpc_session = rpc_session_open(rpc, RpcOwnerUart); + + if(instance->rpc_session) { + instance->tx_semaphore = furi_semaphore_alloc(1, 1); + rpc_session_set_context(instance->rpc_session, instance); + rpc_session_set_send_bytes_callback(instance->rpc_session, expansion_rpc_send_callback); + } + + return instance->rpc_session != NULL; +} + +static void expansion_rpc_session_close(Expansion* instance) { + if(instance->rpc_session) { + rpc_session_close(instance->rpc_session); + furi_semaphore_free(instance->tx_semaphore); + } + + furi_record_close(RECORD_RPC); +} + +static bool + expansion_handle_session_state_handshake(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type != ExpansionFrameTypeBaudRate) break; + const uint32_t baud_rate = rx_frame->content.baud_rate.baud; + + FURI_LOG_D(TAG, "Proposed baud rate: %lu", baud_rate); + + if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) { + instance->session_state = ExpansionSessionStateConnected; + // Send response at previous baud rate + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + furi_hal_serial_set_br(instance->serial_handle, baud_rate); + + } else { + if(!expansion_send_status_response(instance, ExpansionFrameErrorBaudRate)) break; + FURI_LOG_E(TAG, "Bad baud rate"); + } + success = true; + } while(false); + + return success; +} + +static bool + expansion_handle_session_state_connected(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type == ExpansionFrameTypeControl) { + if(rx_frame->content.control.command != ExpansionFrameControlCommandStartRpc) break; + instance->session_state = ExpansionSessionStateRpcActive; + if(!expansion_rpc_session_open(instance)) break; + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { + if(!expansion_send_heartbeat(instance)) break; + + } else { + break; + } + success = true; + } while(false); + + return success; +} + +static bool + expansion_handle_session_state_rpc_active(Expansion* instance, const ExpansionFrame* rx_frame) { + bool success = false; + + do { + if(rx_frame->header.type == ExpansionFrameTypeData) { + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + const size_t size_consumed = rpc_session_feed( + instance->rpc_session, + rx_frame->content.data.bytes, + rx_frame->content.data.size, + EXPANSION_PROTOCOL_TIMEOUT_MS); + if(size_consumed != rx_frame->content.data.size) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeControl) { + if(rx_frame->content.control.command != ExpansionFrameControlCommandStopRpc) break; + instance->session_state = ExpansionSessionStateConnected; + expansion_rpc_session_close(instance); + if(!expansion_send_status_response(instance, ExpansionFrameErrorNone)) break; + + } else if(rx_frame->header.type == ExpansionFrameTypeStatus) { + if(rx_frame->content.status.error != ExpansionFrameErrorNone) break; + furi_semaphore_release(instance->tx_semaphore); + + } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { + if(!expansion_send_heartbeat(instance)) break; + + } else { + break; + } + success = true; + } while(false); + + return success; +} + +static inline void expansion_state_machine(Expansion* instance) { + typedef bool (*ExpansionSessionStateHandler)(Expansion*, const ExpansionFrame*); + + static const ExpansionSessionStateHandler expansion_handlers[] = { + [ExpansionSessionStateHandShake] = expansion_handle_session_state_handshake, + [ExpansionSessionStateConnected] = expansion_handle_session_state_connected, + [ExpansionSessionStateRpcActive] = expansion_handle_session_state_rpc_active, + }; + + ExpansionFrame rx_frame; + + while(true) { + if(!expansion_receive_frame(instance, &rx_frame)) break; + if(!expansion_handlers[instance->session_state](instance, &rx_frame)) break; + } +} + +static void expansion_worker_pending_callback(void* context, uint32_t arg) { + furi_assert(context); + UNUSED(arg); + + Expansion* instance = context; + furi_thread_join(instance->worker_thread); + + // Do not re-enable detection interrupt on user-requested exit + if(instance->exit_reason != ExpansionSessionExitReasonUser) { + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + instance->state = ExpansionStateEnabled; + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + furi_mutex_release(instance->state_mutex); + } +} + +static int32_t expansion_worker(void* context) { + furi_assert(context); + Expansion* instance = context; + + furi_hal_power_insomnia_enter(); + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + + instance->serial_handle = furi_hal_serial_control_acquire(instance->serial_id); + furi_check(instance->serial_handle); + + FURI_LOG_D(TAG, "Service started"); + + instance->rx_buf = furi_stream_buffer_alloc(EXPANSION_BUFFER_SIZE, 1); + instance->session_state = ExpansionSessionStateHandShake; + instance->exit_reason = ExpansionSessionExitReasonUnknown; + + furi_hal_serial_init(instance->serial_handle, EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE); + + furi_hal_serial_async_rx_start( + instance->serial_handle, expansion_serial_rx_callback, instance, false); + + if(expansion_send_heartbeat(instance)) { + expansion_state_machine(instance); + } + + if(instance->session_state == ExpansionSessionStateRpcActive) { + expansion_rpc_session_close(instance); + } + + FURI_LOG_D(TAG, "Service stopped"); + + furi_hal_serial_control_release(instance->serial_handle); + furi_stream_buffer_free(instance->rx_buf); + + furi_hal_power_insomnia_exit(); + furi_timer_pending_callback(expansion_worker_pending_callback, instance, 0); + + return 0; +} + +// Called from the serial control thread +static void expansion_detect_callback(void* context) { + furi_assert(context); + Expansion* instance = context; + + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + if(instance->state == ExpansionStateEnabled) { + instance->state = ExpansionStateRunning; + furi_thread_start(instance->worker_thread); + } + + furi_mutex_release(instance->state_mutex); +} + +static Expansion* expansion_alloc() { + Expansion* instance = malloc(sizeof(Expansion)); + + instance->state_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + instance->worker_thread = furi_thread_alloc_ex(TAG, 768, expansion_worker, instance); + + return instance; +} + +void expansion_on_system_start(void* arg) { + UNUSED(arg); + + Expansion* instance = expansion_alloc(); + furi_record_create(RECORD_EXPANSION, instance); + + ExpansionSettings settings = {}; + if(!expansion_settings_load(&settings)) { + expansion_settings_save(&settings); + } else if(settings.uart_index < FuriHalSerialIdMax) { + expansion_enable(instance, settings.uart_index); + } +} + +// Public API functions + +void expansion_enable(Expansion* instance, FuriHalSerialId serial_id) { + expansion_disable(instance); + + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + instance->serial_id = serial_id; + instance->state = ExpansionStateEnabled; + + furi_hal_serial_control_set_expansion_callback( + instance->serial_id, expansion_detect_callback, instance); + + furi_mutex_release(instance->state_mutex); + + FURI_LOG_D(TAG, "Detection enabled"); +} + +void expansion_disable(Expansion* instance) { + furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk); + + if(instance->state == ExpansionStateRunning) { + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), ExpansionFlagStop); + furi_thread_join(instance->worker_thread); + } else if(instance->state == ExpansionStateEnabled) { + FURI_LOG_D(TAG, "Detection disabled"); + furi_hal_serial_control_set_expansion_callback(instance->serial_id, NULL, NULL); + } + + instance->state = ExpansionStateDisabled; + + furi_mutex_release(instance->state_mutex); +} diff --git a/applications/services/expansion/expansion.h b/applications/services/expansion/expansion.h new file mode 100644 index 000000000..5e4a03f83 --- /dev/null +++ b/applications/services/expansion/expansion.h @@ -0,0 +1,50 @@ +/** + * @file expansion.h + * @brief Expansion module support library. + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief FURI record key to access the expansion object. + */ +#define RECORD_EXPANSION "expansion" + +/** + * @brief Expansion opaque type declaration. + */ +typedef struct Expansion Expansion; + +/** + * @brief Enable support for expansion modules on designated serial port. + * + * Only one serial port can be used to communicate with an expansion + * module at a time. + * + * Calling this function when expansion module support is already enabled + * will first disable the previous setting, then enable the current one. + * + * @param[in,out] instance pointer to the Expansion instance. + * @param[in] serial_id numerical identifier of the serial. + */ +void expansion_enable(Expansion* instance, FuriHalSerialId serial_id); + +/** + * @brief Disable support for expansion modules. + * + * Calling this function will cease all communications with the + * expansion module (if any), release the serial handle and + * reset the respective pins to the default state. + * + * @param[in,out] instance pointer to the Expansion instance. + */ +void expansion_disable(Expansion* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_protocol.h b/applications/services/expansion/expansion_protocol.h new file mode 100644 index 000000000..37c56f15b --- /dev/null +++ b/applications/services/expansion/expansion_protocol.h @@ -0,0 +1,338 @@ +/** + * @file expansion_protocol.h + * @brief Flipper Expansion Protocol parser reference implementation. + * + * This file is licensed separately under The Unlicense. + * See https://unlicense.org/ for more details. + * + * This parser is written with low-spec hardware in mind. It does not use + * dynamic memory allocation or Flipper-specific libraries and can be + * included directly into any module's firmware's sources. + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default baud rate to start all communications at. + */ +#define EXPANSION_PROTOCOL_DEFAULT_BAUD_RATE (9600UL) + +/** + * @brief Maximum data size per frame, in bytes. + */ +#define EXPANSION_PROTOCOL_MAX_DATA_SIZE (64U) + +/** + * @brief Maximum allowed inactivity period, in milliseconds. + */ +#define EXPANSION_PROTOCOL_TIMEOUT_MS (250U) + +/** + * @brief Dead time after changing connection baud rate. + */ +#define EXPANSION_PROTOCOL_BAUD_CHANGE_DT_MS (25U) + +/** + * @brief Enumeration of supported frame types. + */ +typedef enum { + ExpansionFrameTypeHeartbeat = 1, /**< Heartbeat frame. */ + ExpansionFrameTypeStatus = 2, /**< Status report frame. */ + ExpansionFrameTypeBaudRate = 3, /**< Baud rate negotiation frame. */ + ExpansionFrameTypeControl = 4, /**< Control frame. */ + ExpansionFrameTypeData = 5, /**< Data frame. */ + ExpansionFrameTypeReserved, /**< Special value. */ +} ExpansionFrameType; + +/** + * @brief Enumeration of possible error types. + */ +typedef enum { + ExpansionFrameErrorNone = 0x00, /**< No error occurred. */ + ExpansionFrameErrorUnknown = 0x01, /**< An unknown error has occurred (generic response). */ + ExpansionFrameErrorBaudRate = 0x02, /**< Requested baud rate is not supported. */ +} ExpansionFrameError; + +/** + * @brief Enumeration of suported control commands. + */ +typedef enum { + ExpansionFrameControlCommandStartRpc = 0x00, /**< Start an RPC session. */ + ExpansionFrameControlCommandStopRpc = 0x01, /**< Stop an open RPC session. */ +} ExpansionFrameControlCommand; + +#pragma pack(push, 1) + +/** + * @brief Frame header structure. + */ +typedef struct { + uint8_t type; /**< Type of the frame. @see ExpansionFrameType. */ +} ExpansionFrameHeader; + +/** + * @brief Heartbeat frame contents. + */ +typedef struct { + /** Empty. */ +} ExpansionFrameHeartbeat; + +/** + * @brief Status frame contents. + */ +typedef struct { + uint8_t error; /**< Reported error code. @see ExpansionFrameError. */ +} ExpansionFrameStatus; + +/** + * @brief Baud rate frame contents. + */ +typedef struct { + uint32_t baud; /**< Requested baud rate. */ +} ExpansionFrameBaudRate; + +/** + * @brief Control frame contents. + */ +typedef struct { + uint8_t command; /**< Control command number. @see ExpansionFrameControlCommand. */ +} ExpansionFrameControl; + +/** + * @brief Data frame contents. + */ +typedef struct { + /** Size of the data. Must be less than EXPANSION_PROTOCOL_MAX_DATA_SIZE. */ + uint8_t size; + /** Data bytes. Valid only up to ExpansionFrameData::size bytes. */ + uint8_t bytes[EXPANSION_PROTOCOL_MAX_DATA_SIZE]; +} ExpansionFrameData; + +/** + * @brief Expansion protocol frame structure. + */ +typedef struct { + ExpansionFrameHeader header; /**< Header of the frame. Required. */ + union { + ExpansionFrameHeartbeat heartbeat; /**< Heartbeat frame contents. */ + ExpansionFrameStatus status; /**< Status frame contents. */ + ExpansionFrameBaudRate baud_rate; /**< Baud rate frame contents. */ + ExpansionFrameControl control; /**< Control frame contents. */ + ExpansionFrameData data; /**< Data frame contents. */ + } content; /**< Contents of the frame. */ +} ExpansionFrame; + +#pragma pack(pop) + +/** + * @brief Expansion checksum type. + */ +typedef uint8_t ExpansionFrameChecksum; + +/** + * @brief Receive function type declaration. + * + * @see expansion_frame_decode(). + * + * @param[out] data pointer to the buffer to reveive the data into. + * @param[in] data_size maximum output buffer capacity, in bytes. + * @param[in,out] context pointer to a user-defined context object. + * @returns number of bytes written into the output buffer. + */ +typedef size_t (*ExpansionFrameReceiveCallback)(uint8_t* data, size_t data_size, void* context); + +/** + * @brief Send function type declaration. + * + * @see expansion_frame_encode(). + * + * @param[in] data pointer to the buffer containing the data to be sent. + * @param[in] data_size size of the data to send, in bytes. + * @param[in,out] context pointer to a user-defined context object. + * @returns number of bytes actually sent. + */ +typedef size_t (*ExpansionFrameSendCallback)(const uint8_t* data, size_t data_size, void* context); + +/** + * @brief Get encoded frame size. + * + * The frame MUST be complete and properly formed. + * + * @param[in] frame pointer to the frame to be evaluated. + * @returns encoded frame size, in bytes. + */ +static inline size_t expansion_frame_get_encoded_size(const ExpansionFrame* frame) { + switch(frame->header.type) { + case ExpansionFrameTypeHeartbeat: + return sizeof(frame->header); + case ExpansionFrameTypeStatus: + return sizeof(frame->header) + sizeof(frame->content.status); + case ExpansionFrameTypeBaudRate: + return sizeof(frame->header) + sizeof(frame->content.baud_rate); + case ExpansionFrameTypeControl: + return sizeof(frame->header) + sizeof(frame->content.control); + case ExpansionFrameTypeData: + return sizeof(frame->header) + sizeof(frame->content.data.size) + frame->content.data.size; + default: + return 0; + } +} + +/** + * @brief Get remaining number of bytes needed to properly decode a frame. + * + * The return value will vary depending on the received_size parameter value. + * The frame is considered complete when the function returns 0. + * + * @param[in] frame pointer to the frame to be evaluated. + * @param[in] received_size number of bytes currently availabe for evaluation. + * @returns number of bytes needed for a complete frame. + */ +static inline size_t + expansion_frame_get_remaining_size(const ExpansionFrame* frame, size_t received_size) { + if(received_size < sizeof(ExpansionFrameHeader)) return sizeof(ExpansionFrameHeader); + + const size_t received_content_size = received_size - sizeof(ExpansionFrameHeader); + size_t content_size; + + switch(frame->header.type) { + case ExpansionFrameTypeHeartbeat: + content_size = 0; + break; + case ExpansionFrameTypeStatus: + content_size = sizeof(frame->content.status); + break; + case ExpansionFrameTypeBaudRate: + content_size = sizeof(frame->content.baud_rate); + break; + case ExpansionFrameTypeControl: + content_size = sizeof(frame->content.control); + break; + case ExpansionFrameTypeData: + if(received_content_size < sizeof(frame->content.data.size)) { + content_size = sizeof(frame->content.data.size); + } else { + content_size = sizeof(frame->content.data.size) + frame->content.data.size; + } + break; + default: + return SIZE_MAX; + } + + return content_size > received_content_size ? content_size - received_content_size : 0; +} + +/** + * @brief Enumeration of protocol parser statuses. + */ +typedef enum { + ExpansionProtocolStatusOk, /**< No error has occurred. */ + ExpansionProtocolStatusErrorFormat, /**< Invalid frame type. */ + ExpansionProtocolStatusErrorChecksum, /**< Checksum mismatch. */ + ExpansionProtocolStatusErrorCommunication, /**< Input/output error. */ +} ExpansionProtocolStatus; + +/** + * @brief Get the checksum byte corresponding to the frame + * + * Lightweight XOR checksum algorithm for basic error detection. + * + * @param[in] data pointer to a byte buffer containing the data. + * @param[in] data_size size of the data buffer. + * @returns checksum byte of the frame. + */ +static inline ExpansionFrameChecksum + expansion_protocol_get_checksum(const uint8_t* data, size_t data_size) { + ExpansionFrameChecksum checksum = 0; + for(size_t i = 0; i < data_size; ++i) { + checksum ^= data[i]; + } + return checksum; +} + +/** + * @brief Receive and decode a frame. + * + * Will repeatedly call the receive callback function until enough data is received. + * + * @param[out] frame pointer to the frame to contain decoded data. + * @param[in] receive pointer to the function used to receive data. + * @param[in,out] context pointer to a user-defined context object. Will be passed to the receive callback function. + * @returns ExpansionProtocolStatusOk on success, any other error code on failure. + */ +static inline ExpansionProtocolStatus expansion_protocol_decode( + ExpansionFrame* frame, + ExpansionFrameReceiveCallback receive, + void* context) { + size_t total_size = 0; + size_t remaining_size; + + while(true) { + remaining_size = expansion_frame_get_remaining_size(frame, total_size); + + if(remaining_size == SIZE_MAX) { + return ExpansionProtocolStatusErrorFormat; + } else if(remaining_size == 0) { + break; + } + + const size_t received_size = + receive((uint8_t*)frame + total_size, remaining_size, context); + + if(received_size == 0) { + return ExpansionProtocolStatusErrorCommunication; + } + + total_size += received_size; + } + + ExpansionFrameChecksum checksum; + const size_t received_size = receive(&checksum, sizeof(checksum), context); + + if(received_size != sizeof(checksum)) { + return ExpansionProtocolStatusErrorCommunication; + } else if(checksum != expansion_protocol_get_checksum((const uint8_t*)frame, total_size)) { + return ExpansionProtocolStatusErrorChecksum; + } else { + return ExpansionProtocolStatusOk; + } +} + +/** + * @brief Encode and send a frame. + * + * @param[in] frame pointer to the frame to be encoded and sent. + * @param[in] send pointer to the function used to send data. + * @param[in,out] context pointer to a user-defined context object. Will be passed to the send callback function. + * @returns ExpansionProtocolStatusOk on success, any other error code on failure. + */ +static inline ExpansionProtocolStatus expansion_protocol_encode( + const ExpansionFrame* frame, + ExpansionFrameSendCallback send, + void* context) { + const size_t encoded_size = expansion_frame_get_encoded_size(frame); + if(encoded_size == 0) { + return ExpansionProtocolStatusErrorFormat; + } + + const ExpansionFrameChecksum checksum = + expansion_protocol_get_checksum((const uint8_t*)frame, encoded_size); + + if((send((const uint8_t*)frame, encoded_size, context) != encoded_size) || + (send(&checksum, sizeof(checksum), context) != sizeof(checksum))) { + return ExpansionProtocolStatusErrorCommunication; + } else { + return ExpansionProtocolStatusOk; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_settings.c b/applications/services/expansion/expansion_settings.c new file mode 100644 index 000000000..586bf6e9c --- /dev/null +++ b/applications/services/expansion/expansion_settings.c @@ -0,0 +1,30 @@ +#include "expansion_settings.h" + +#include +#include + +#include "expansion_settings_filename.h" + +#define EXPANSION_SETTINGS_PATH INT_PATH(EXPANSION_SETTINGS_FILE_NAME) +#define EXPANSION_SETTINGS_VERSION (0) +#define EXPANSION_SETTINGS_MAGIC (0xEA) + +bool expansion_settings_load(ExpansionSettings* settings) { + furi_assert(settings); + return saved_struct_load( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION); +} + +bool expansion_settings_save(ExpansionSettings* settings) { + furi_assert(settings); + return saved_struct_save( + EXPANSION_SETTINGS_PATH, + settings, + sizeof(ExpansionSettings), + EXPANSION_SETTINGS_MAGIC, + EXPANSION_SETTINGS_VERSION); +} diff --git a/applications/services/expansion/expansion_settings.h b/applications/services/expansion/expansion_settings.h new file mode 100644 index 000000000..e7663f1b9 --- /dev/null +++ b/applications/services/expansion/expansion_settings.h @@ -0,0 +1,43 @@ +/** + * @file expansion_settings.h + * @brief Expansion module support settings. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Expansion module support settings storage type. + */ +typedef struct { + /** + * Numerical index of serial port used to communicate + * with expansion modules. + */ + uint8_t uart_index; +} ExpansionSettings; + +/** + * @brief Load expansion module support settings from file. + * + * @param[out] settings pointer to an ExpansionSettings instance to load settings into. + * @returns true if the settings were successfully loaded, false otherwise. + */ +bool expansion_settings_load(ExpansionSettings* settings); + +/** + * @brief Save expansion module support settings to file. + * + * @param[in] settings pointer to an ExpansionSettings instance to save settings from. + * @returns true if the settings were successfully saved, false otherwise. + */ +bool expansion_settings_save(ExpansionSettings* settings); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/expansion/expansion_settings_filename.h b/applications/services/expansion/expansion_settings_filename.h new file mode 100644 index 000000000..23d6728e8 --- /dev/null +++ b/applications/services/expansion/expansion_settings_filename.h @@ -0,0 +1,9 @@ +/** + * @file expansion_settings_filename.h + */ +#pragma once + +/** + * @brief File name used for expansion settings. + */ +#define EXPANSION_SETTINGS_FILE_NAME ".expansion.settings" diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 50c4b3608..909d0d65d 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -160,8 +160,11 @@ void rpc_session_set_terminated_callback( * command is gets processed - it's safe either way. But case of it is quite * odd: client sends close request and sends command after. */ -size_t - rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, uint32_t timeout) { +size_t rpc_session_feed( + RpcSession* session, + const uint8_t* encoded_bytes, + size_t size, + uint32_t timeout) { furi_assert(session); furi_assert(encoded_bytes); diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index 863bca355..f7cda64f7 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -35,6 +35,7 @@ typedef enum { RpcOwnerUnknown = 0, RpcOwnerBle, RpcOwnerUsb, + RpcOwnerUart, RpcOwnerCount, } RpcOwner; @@ -124,7 +125,7 @@ void rpc_session_set_terminated_callback( * * @return actually consumed bytes */ -size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, uint32_t timeout); +size_t rpc_session_feed(RpcSession* session, const uint8_t* buffer, size_t size, uint32_t timeout); /** Get available size of RPC buffer * @@ -136,4 +137,4 @@ size_t rpc_session_get_available_size(RpcSession* session); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index dd219e2dc..98860332d 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -265,7 +265,7 @@ static void rpc_system_gui_virtual_display_input_callback(InputEvent* event, voi RpcGuiSystem* rpc_gui = context; RpcSession* session = rpc_gui->session; - FURI_LOG_D(TAG, "VirtulDisplay: SendInputEvent"); + FURI_LOG_D(TAG, "VirtualDisplay: SendInputEvent"); PB_Main rpc_message = { .command_id = 0, @@ -317,7 +317,7 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, rpc_gui); if(request->content.gui_start_virtual_display_request.send_input) { - FURI_LOG_D(TAG, "VirtulDisplay: input forwarding requested"); + FURI_LOG_D(TAG, "VirtualDisplay: input forwarding requested"); view_port_input_callback_set( rpc_gui->virtual_display_view_port, rpc_system_gui_virtual_display_input_callback, @@ -464,4 +464,4 @@ void rpc_system_gui_free(void* context) { } furi_record_close(RECORD_GUI); free(rpc_gui); -} \ No newline at end of file +} diff --git a/applications/settings/expansion_settings_app/application.fam b/applications/settings/expansion_settings_app/application.fam new file mode 100644 index 000000000..b253ad174 --- /dev/null +++ b/applications/settings/expansion_settings_app/application.fam @@ -0,0 +1,9 @@ +App( + appid="expansion_settings", + name="Expansion Modules", + apptype=FlipperAppType.SETTINGS, + entry_point="expansion_settings_app", + requires=["gui"], + stack_size=1 * 1024, + order=80, +) diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.c b/applications/settings/expansion_settings_app/expansion_settings_app.c new file mode 100644 index 000000000..894015712 --- /dev/null +++ b/applications/settings/expansion_settings_app/expansion_settings_app.c @@ -0,0 +1,91 @@ +#include "expansion_settings_app.h" + +static const char* const expansion_uart_text[] = { + "USART", + "LPUART", + "None", +}; + +static void expansion_settings_app_uart_changed(VariableItem* item) { + ExpansionSettingsApp* app = variable_item_get_context(item); + const uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, expansion_uart_text[index]); + app->settings.uart_index = index; + + if(index < FuriHalSerialIdMax) { + expansion_enable(app->expansion, index); + } else { + expansion_disable(app->expansion); + } +} + +static uint32_t expansion_settings_app_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static ExpansionSettingsApp* expansion_settings_app_alloc() { + ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp)); + + if(!expansion_settings_load(&app->settings)) { + expansion_settings_save(&app->settings); + } + + app->gui = furi_record_open(RECORD_GUI); + app->expansion = furi_record_open(RECORD_EXPANSION); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->var_item_list = variable_item_list_alloc(); + + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + app->var_item_list, + "Listen UART", + COUNT_OF(expansion_uart_text), + expansion_settings_app_uart_changed, + app); + value_index = app->settings.uart_index; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, expansion_uart_text[value_index]); + + view_set_previous_callback( + variable_item_list_get_view(app->var_item_list), expansion_settings_app_exit); + view_dispatcher_add_view( + app->view_dispatcher, + ExpansionSettingsViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + view_dispatcher_switch_to_view(app->view_dispatcher, ExpansionSettingsViewVarItemList); + + return app; +} + +static void expansion_settings_app_free(ExpansionSettingsApp* app) { + furi_assert(app); + + expansion_settings_save(&app->settings); + + view_dispatcher_remove_view(app->view_dispatcher, ExpansionSettingsViewVarItemList); + variable_item_list_free(app->var_item_list); + view_dispatcher_free(app->view_dispatcher); + + furi_record_close(RECORD_EXPANSION); + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t expansion_settings_app(void* p) { + UNUSED(p); + ExpansionSettingsApp* app = expansion_settings_app_alloc(); + view_dispatcher_run(app->view_dispatcher); + expansion_settings_app_free(app); + return 0; +} diff --git a/applications/settings/expansion_settings_app/expansion_settings_app.h b/applications/settings/expansion_settings_app/expansion_settings_app.h new file mode 100644 index 000000000..a43bf853f --- /dev/null +++ b/applications/settings/expansion_settings_app/expansion_settings_app.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + VariableItemList* var_item_list; + Expansion* expansion; + ExpansionSettings settings; +} ExpansionSettingsApp; + +typedef enum { + ExpansionSettingsViewVarItemList, +} ExpansionSettingsView; diff --git a/documentation/ExpansionModules.md b/documentation/ExpansionModules.md new file mode 100644 index 000000000..c757c0d2b --- /dev/null +++ b/documentation/ExpansionModules.md @@ -0,0 +1,164 @@ +# Expansion Module Protocol - Draft + +## Terms and definitions + +- Expansion Module: A third-party hardware unit meant for use with Flipper Zero by connecting it to its GPIO header. +- Expansion Module Protocol: A serial-based, byte-oriented, synchronous communication protocol described in this document. +- Host: Hardware unit tasked with serving requests. Used interchangeably with Flipper, Server, Host etc. throughout this document. +- Device: Used interchangeably with Expansion Module, Module, Client, etc. +- RPC: Remote Procedure Call, a protobuf-based communication protocol widely used by Flipper Zero companion applications. +- Timeout Interval: Period of inactivity to be treated as a loss of connection, also denoted as Tto. Equals to 250 ms. +- Baud Rate Switch Dead Time: Period of time after baud rate change during which no communication is allowed, also denoted Tdt. Equals to 25 ms. + +## Features + +- Automatic expansion module detection +- Baud rate negotiation +- Basic error detection +- Request-response communication flow +- Integration with Flipper RPC protocol + +## Hardware + +Depending on the UART selected for communication, the following pins area available for the expansion modules to connect to: + +| UART | Tx pin | Rx pin | +|--------|--------|--------| +| USART | 13 | 14 | +| LPUART | 15 | 16 | + +## Frame structure + +Each frame consists of a header (1 byte), contents (size depends of frame type) and checksum (1 byte) fields: + +| Header (1 byte) | Contents (0 or more bytes) | Checksum (1 byte) | +|-----------------|----------------------------|-------------------| +| Frame type | Frame payload | XOR checksum | + +### Heartbeat frame + +HEARTBEAT frames are used to maintain an idle connection. In the event of not receiving any frames within Tto, either side must cease all communications and be ready to initiate the connection again. + +| Header (1 byte) | Checksum (1 byte) | +|-----------------|-------------------| +| 0x01 | XOR checksum | + +Note that the contents field is not present (0 bytes length). + +### Status frame + +STATUS frames are used to report the status of a transaction. Every received frame MUST be confirmed by a matching STATUS response. + +| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) | +|-----------------|-------------------|-------------------| +| 0x02 | Error code | XOR checksum | + +The `Error code` field SHALL have one of the following values: + +| Error code | Meaning | +|------------|-------------------------| +| 0x00 | OK (No error) | +| 0x01 | Unknown error | +| 0x02 | Baud rate not supported | + +### Baud rate frame + +BAUD RATE frames are used to negotiate communication speed. The initial connection SHALL always happen at 9600 baud. The first message sent by the module MUST be a BAUD RATE frame, even if a different speed is not required. + +| Header (1 byte) | Contents (4 bytes) | Checksum (1 byte) | +|-----------------|--------------------|-------------------| +| 0x03 | Baud rate | XOR checksum | + +If the requested baud rate is supported by the host, it SHALL respond with a STATUS frame with an OK error code, otherwise the error code SHALL be 0x02 (Baud rate not supported). Until the negotiation succeeds, the speed SHALL remain at 9600 baud. The module MAY send additional BAUD RATE frames with alternative speeds in case the initial request was refused. No other frames are allowed until the speed negotiation succeeds. + +### Control frame + +CONTROL frames are used to control various aspects of the communication. As of now, the sole purpose of CONTROL frames is to start and stop the RPC session. + +| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) | +|-----------------|-------------------|-------------------| +| 0x04 | Command | XOR checksum | + +The `Command` field SHALL have one of the followind values: + +| Command | Meaning | +|---------|-------------------| +| 0x00 | Start RPC session | +| 0x01 | Stop RPC session | + +### Data frame + +DATA frames are used to transmit arbitrary data in either direction. Each DATA frame can hold up to 64 bytes. If an RPC session is curretly open, all received bytes are forwarded to it. + +| Header (1 byte) | Contents (1 to 65 byte(s)) | Checksum (1 byte) | +|-----------------|----------------------------|-------------------| +| 0x05 | Data | XOR checksum | + +The `Data` field SHALL have the following structure: + +| Data size (1 byte) | Data (0 to 64 bytes) | +|--------------------|----------------------| +| 0x00 ... 0x40 | Arbitrary data | + +## Communication flow + +In order for the host to be able to detect the module, the respective feature must be enabled first. This can be done via the GUI by going to `Settings -> Expansion Modules` and selecting the required `Listen UART` or programmatically by calling `expansion_enable()`. Likewise, disabling this feature via the same GUI or by calling `expansion_disable()` will result in ceasing all communications and not being able to detect any connected modules. + +The communication is always initiated by the module by the means of shortly pulling the RX pin down. The host SHALL respond with a HEARTBEAT frame indicating that it is ready to receive requests. The module then MUST issue a BAUDRATE request within Tto. Failure to do so will result in the host dropping the connection and returning to its initial state. + +``` + MODULE | FLIPPER +-----------------------------+--------------------------- + | (Start) +Pull down RX --> + <-- Heartbeat +Baud Rate --> + <-- Status [OK | Error] + | +(Module changes baud rate | (Flipper changes + and waits for Tdt) | baud rate) + | +Control [Start RPC] --> + <-- Status [OK | Error] +-----------------------------+--------------------------- (1) +Data [RPC Request] --> + <-- Status [OK | Error] + <-- Data [RPC Response] +Status [OK | Error] --> +-----------------------------+--------------------------- (2) +Data [RPC Request pt.1] --> + <-- Status [OK | Error] +Data [RPC Request pt.2] --> + <-- Status [OK | Error] +Data [RPC Request pt.3] --> + <-- Status [OK | Error] + <-- Data [RPC Response] +Status [OK | Error] --> +-----------------------------+--------------------------- (3) +Heartbeat --> + <-- Heartbeat +Heartbeat --> + <-- Heartbeat +-----------------------------+--------------------------- +Control [Stop RPC] --> + <-- Status [OK | Error] +(Module disconnected) | + | (No activity within Tto + | return to start) + +(1) The module MUST confirm all implicitly requested frames (e.g. DATA frames containing RPC responses) with a STATUS frame. +(2) RPC requests larger than 64 bytes are split into multiple frames. Every DATA frame MUST be confirmed with a STATUS frame. +(3) When the module has no data to send, it MUST send HEARTBEAT frames with a period < Tto in order to maintain the connection. + The host SHALL respond with a HEARTBEAT frame each time. +``` + +## Error detection + +Error detection is implemented via adding an extra checksum byte to every frame (see above). + +The checksum is calculated by bitwise XOR-ing every byte in the frame (excluding the checksum byte itself), with an initial value of 0. + +### Error recovery behaviour + +In the event of a detected error, the concerned side MUST cease all communications and reset to initial state. The other side will then experience +a communication timeout and the connection will be re-established automatically. diff --git a/furi/core/log.c b/furi/core/log.c index 4de850d6b..3d270816c 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -206,4 +206,4 @@ bool furi_log_level_from_string(const char* str, FuriLogLevel* level) { } } return false; -} \ No newline at end of file +} diff --git a/furi/core/stream_buffer.h b/furi/core/stream_buffer.h index d07f7e60b..5ddc49416 100644 --- a/furi/core/stream_buffer.h +++ b/furi/core/stream_buffer.h @@ -149,4 +149,4 @@ FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 7bbb6b13f..5259db0f3 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,10 +1,11 @@ entry,status,name,type,params -Version,+,51.0,, +Version,+,52.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/expansion/expansion.h,, Header,+,applications/services/gui/elements.h,, Header,+,applications/services/gui/gui.h,, Header,+,applications/services/gui/icon_i.h,, @@ -788,6 +789,8 @@ Function,-,exp10f,float,float Function,-,exp2,double,double Function,-,exp2f,float,float Function,-,exp2l,long double,long double +Function,+,expansion_disable,void,Expansion* +Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" @@ -1268,22 +1271,27 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId Function,+,furi_hal_serial_control_deinit,void, Function,+,furi_hal_serial_control_init,void, Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_expansion_callback,void,"FuriHalSerialId, FuriHalSerialControlExpansionCallback, void*" Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" Function,+,furi_hal_serial_control_suspend,void, Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_disable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_enable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" +Function,+,furi_hal_serial_get_gpio_pin,const GpioPin*,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_is_baud_rate_supported,_Bool,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" -Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" @@ -2098,7 +2106,7 @@ Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* -Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" +Function,+,rpc_session_feed,size_t,"RpcSession*, const uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* Function,+,rpc_session_get_owner,RpcOwner,RpcSession* Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index c71304068..acd954475 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,11 +1,12 @@ entry,status,name,type,params -Version,+,51.0,, +Version,+,52.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/expansion/expansion.h,, Header,+,applications/services/gui/elements.h,, Header,+,applications/services/gui/gui.h,, Header,+,applications/services/gui/icon_i.h,, @@ -892,6 +893,8 @@ Function,-,exp10f,float,float Function,-,exp2,double,double Function,-,exp2f,float,float Function,-,exp2l,long double,long double +Function,+,expansion_disable,void,Expansion* +Function,+,expansion_enable,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" @@ -1463,22 +1466,27 @@ Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, Function,+,furi_hal_sd_presence_init,void, Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* +Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" +Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId Function,+,furi_hal_serial_control_deinit,void, Function,+,furi_hal_serial_control_init,void, Function,+,furi_hal_serial_control_release,void,FuriHalSerialHandle* Function,+,furi_hal_serial_control_resume,void, +Function,+,furi_hal_serial_control_set_expansion_callback,void,"FuriHalSerialId, FuriHalSerialControlExpansionCallback, void*" Function,+,furi_hal_serial_control_set_logging_config,void,"FuriHalSerialId, uint32_t" Function,+,furi_hal_serial_control_suspend,void, Function,+,furi_hal_serial_deinit,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_disable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_dma_rx,size_t,"FuriHalSerialHandle*, uint8_t*, size_t" Function,+,furi_hal_serial_dma_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialDmaRxCallback, void*, _Bool" Function,+,furi_hal_serial_dma_rx_stop,void,FuriHalSerialHandle* +Function,+,furi_hal_serial_enable_direction,void,"FuriHalSerialHandle*, FuriHalSerialDirection" +Function,+,furi_hal_serial_get_gpio_pin,const GpioPin*,"FuriHalSerialHandle*, FuriHalSerialDirection" Function,+,furi_hal_serial_init,void,"FuriHalSerialHandle*, uint32_t" +Function,+,furi_hal_serial_is_baud_rate_supported,_Bool,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_resume,void,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle* -Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool" -Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle* Function,+,furi_hal_serial_set_br,void,"FuriHalSerialHandle*, uint32_t" Function,+,furi_hal_serial_suspend,void,FuriHalSerialHandle* Function,+,furi_hal_serial_tx,void,"FuriHalSerialHandle*, const uint8_t*, size_t" @@ -2723,7 +2731,7 @@ Function,-,round,double,double Function,+,roundf,float,float Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* -Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" +Function,+,rpc_session_feed,size_t,"RpcSession*, const uint8_t*, size_t, uint32_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* Function,+,rpc_session_get_owner,RpcOwner,RpcSession* Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" diff --git a/targets/f7/furi_hal/furi_hal_serial.c b/targets/f7/furi_hal/furi_hal_serial.c index 71dd6561e..1296ee620 100644 --- a/targets/f7/furi_hal/furi_hal_serial.c +++ b/targets/f7/furi_hal/furi_hal_serial.c @@ -31,6 +31,59 @@ typedef struct { void* context; } FuriHalSerial; +typedef void (*FuriHalSerialControlFunc)(USART_TypeDef*); + +typedef struct { + USART_TypeDef* periph; + GpioAltFn alt_fn; + const GpioPin* gpio[FuriHalSerialDirectionMax]; + FuriHalSerialControlFunc enable[FuriHalSerialDirectionMax]; + FuriHalSerialControlFunc disable[FuriHalSerialDirectionMax]; +} FuriHalSerialConfig; + +static const FuriHalSerialConfig furi_hal_serial_config[FuriHalSerialIdMax] = { + [FuriHalSerialIdUsart] = + { + .periph = USART1, + .alt_fn = GpioAltFn7USART1, + .gpio = + { + [FuriHalSerialDirectionTx] = &gpio_usart_tx, + [FuriHalSerialDirectionRx] = &gpio_usart_rx, + }, + .enable = + { + [FuriHalSerialDirectionTx] = LL_USART_EnableDirectionTx, + [FuriHalSerialDirectionRx] = LL_USART_EnableDirectionRx, + }, + .disable = + { + [FuriHalSerialDirectionTx] = LL_USART_DisableDirectionTx, + [FuriHalSerialDirectionRx] = LL_USART_DisableDirectionRx, + }, + }, + [FuriHalSerialIdLpuart] = + { + .periph = LPUART1, + .alt_fn = GpioAltFn8LPUART1, + .gpio = + { + [FuriHalSerialDirectionTx] = &gpio_ext_pc1, + [FuriHalSerialDirectionRx] = &gpio_ext_pc0, + }, + .enable = + { + [FuriHalSerialDirectionTx] = LL_LPUART_EnableDirectionTx, + [FuriHalSerialDirectionRx] = LL_LPUART_EnableDirectionRx, + }, + .disable = + { + [FuriHalSerialDirectionTx] = LL_LPUART_DisableDirectionTx, + [FuriHalSerialDirectionRx] = LL_LPUART_DisableDirectionRx, + }, + }, +}; + static FuriHalSerial furi_hal_serial[FuriHalSerialIdMax] = {0}; static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch); @@ -451,6 +504,11 @@ void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud) { } } +bool furi_hal_serial_is_baud_rate_supported(FuriHalSerialHandle* handle, uint32_t baud) { + furi_check(handle); + return baud >= 9600UL && baud <= 4000000UL; +} + static uint32_t furi_hal_serial_get_prescaler(FuriHalSerialHandle* handle, uint32_t baud) { uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); uint32_t divisor = (uartclk / baud); @@ -836,3 +894,44 @@ void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle) { furi_hal_serial_event_deinit(handle); furi_hal_serial_dma_configure(handle, NULL, NULL); } + +void furi_hal_serial_enable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + USART_TypeDef* periph = furi_hal_serial_config[handle->id].periph; + furi_hal_serial_config[handle->id].enable[direction](periph); + + const GpioPin* gpio = furi_hal_serial_config[handle->id].gpio[direction]; + const GpioAltFn alt_fn = furi_hal_serial_config[handle->id].alt_fn; + + furi_hal_gpio_init_ex( + gpio, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedVeryHigh, alt_fn); +} + +void furi_hal_serial_disable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + USART_TypeDef* periph = furi_hal_serial_config[handle->id].periph; + furi_hal_serial_config[handle->id].disable[direction](periph); + + const GpioPin* gpio = furi_hal_serial_config[handle->id].gpio[direction]; + + furi_hal_gpio_init(gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +const GpioPin* + furi_hal_serial_get_gpio_pin(FuriHalSerialHandle* handle, FuriHalSerialDirection direction) { + furi_check(handle); + furi_check(handle->id < FuriHalSerialIdMax); + furi_check(direction < FuriHalSerialDirectionMax); + + return furi_hal_serial_config[handle->id].gpio[direction]; +} diff --git a/targets/f7/furi_hal/furi_hal_serial.h b/targets/f7/furi_hal/furi_hal_serial.h index 19cea2a7a..975406670 100644 --- a/targets/f7/furi_hal/furi_hal_serial.h +++ b/targets/f7/furi_hal/furi_hal_serial.h @@ -48,6 +48,15 @@ void furi_hal_serial_suspend(FuriHalSerialHandle* handle); */ void furi_hal_serial_resume(FuriHalSerialHandle* handle); +/** + * @brief Determine whether a certain baud rate is supported + * + * @param handle Serial handle + * @param baud baud rate to be checked + * @returns true if baud rate is supported, false otherwise. + */ +bool furi_hal_serial_is_baud_rate_supported(FuriHalSerialHandle* handle, uint32_t baud); + /** Changes baud rate * * @param handle Serial handle @@ -152,6 +161,42 @@ typedef void (*FuriHalSerialDmaRxCallback)( size_t data_len, void* context); +/** + * @brief Enable an input/output directon + * + * Takes over the respective pin by reconfiguring it to + * the appropriate alternative function. + * + * @param handle Serial handle + * @param direction Direction to enable + */ +void furi_hal_serial_enable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction); + +/** + * @brief Disable an input/output directon + * + * Releases the respective pin by reconfiguring it to + * initial state, making possible its use for other purposes. + * + * @param handle Serial handle + * @param direction Direction to disable + */ +void furi_hal_serial_disable_direction( + FuriHalSerialHandle* handle, + FuriHalSerialDirection direction); + +/** + * @brief Get the GPIO pin associated with a serial + * + * @param handle Serial handle + * @param direction Direction to query + * @returns pointer to the respective pin instance + */ +const GpioPin* + furi_hal_serial_get_gpio_pin(FuriHalSerialHandle* handle, FuriHalSerialDirection direction); + /** Start and sets Serial event callback receive DMA * * @param handle Serial handle diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c index 28c32e203..0c95d12c1 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.c +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -14,6 +14,8 @@ typedef enum { FuriHalSerialControlMessageTypeAcquire, FuriHalSerialControlMessageTypeRelease, FuriHalSerialControlMessageTypeLogging, + FuriHalSerialControlMessageTypeExpansionSetCallback, + FuriHalSerialControlMessageTypeExpansionIrq, } FuriHalSerialControlMessageType; typedef struct { @@ -28,6 +30,12 @@ typedef struct { const uint32_t baud_rate; } FuriHalSerialControlMessageInputLogging; +typedef struct { + const FuriHalSerialId id; + const FuriHalSerialControlExpansionCallback callback; + void* context; +} FuriHalSerialControlMessageExpCallback; + typedef struct { FuriHalSerialHandle handles[FuriHalSerialIdMax]; FuriMessageQueue* queue; @@ -38,6 +46,10 @@ typedef struct { uint32_t log_config_serial_baud_rate; FuriLogHandler log_handler; FuriHalSerialHandle* log_serial; + + // Expansion detection + FuriHalSerialControlExpansionCallback expansion_cb; + void* expansion_ctx; } FuriHalSerialControl; FuriHalSerialControl* furi_hal_serial_control = NULL; @@ -65,6 +77,150 @@ static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) } } +static void furi_hal_serial_control_expansion_irq_callback(void* context) { + UNUSED(context); + + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeExpansionIrq; + message.api_lock = NULL; + furi_message_queue_put(furi_hal_serial_control->queue, &message, 0); +} + +static bool furi_hal_serial_control_handler_stop(void* input, void* output) { + UNUSED(input); + UNUSED(output); + return false; +} + +static bool furi_hal_serial_control_handler_suspend(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); + furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); + } + + return true; +} + +static bool furi_hal_serial_control_handler_resume(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + for(size_t i = 0; i < FuriHalSerialIdMax; i++) { + furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); + } + + return true; +} + +static bool furi_hal_serial_control_handler_acquire(void* input, void* output) { + FuriHalSerialId serial_id = *(FuriHalSerialId*)input; + if(furi_hal_serial_control->handles[serial_id].in_use) { + *(FuriHalSerialHandle**)output = NULL; + } else { + // Logging + if(furi_hal_serial_control->log_config_serial_id == serial_id) { + furi_hal_serial_control_log_set_handle(NULL); + } + // Return handle + furi_hal_serial_control->handles[serial_id].in_use = true; + *(FuriHalSerialHandle**)output = &furi_hal_serial_control->handles[serial_id]; + } + + return true; +} + +static bool furi_hal_serial_control_handler_release(void* input, void* output) { + UNUSED(output); + + FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)input; + furi_assert(handle->in_use); + furi_hal_serial_deinit(handle); + handle->in_use = false; + + // Return back logging + if(furi_hal_serial_control->log_config_serial_id == handle->id) { + furi_hal_serial_control_log_set_handle(handle); + } + + return true; +} + +static bool furi_hal_serial_control_handler_logging(void* input, void* output) { + UNUSED(output); + + // Set new configuration + FuriHalSerialControlMessageInputLogging* message_input = input; + furi_hal_serial_control->log_config_serial_id = message_input->id; + furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; + // Apply new configuration + FuriHalSerialHandle* handle = NULL; + if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { + if(!furi_hal_serial_control->handles[furi_hal_serial_control->log_config_serial_id].in_use) { + handle = + &furi_hal_serial_control->handles[furi_hal_serial_control->log_config_serial_id]; + } + } + + furi_hal_serial_control_log_set_handle(handle); + + return true; +} + +static bool furi_hal_serial_control_handler_expansion_set_callback(void* input, void* output) { + UNUSED(output); + + FuriHalSerialControlMessageExpCallback* message_input = input; + FuriHalSerialHandle* handle = &furi_hal_serial_control->handles[message_input->id]; + const GpioPin* gpio = furi_hal_serial_get_gpio_pin(handle, FuriHalSerialDirectionRx); + + if(message_input->callback) { + furi_check(furi_hal_serial_control->expansion_cb == NULL); + + furi_hal_serial_disable_direction(handle, FuriHalSerialDirectionRx); + furi_hal_gpio_add_int_callback(gpio, furi_hal_serial_control_expansion_irq_callback, NULL); + furi_hal_gpio_init(gpio, GpioModeInterruptFall, GpioPullUp, GpioSpeedLow); + } else { + furi_check(furi_hal_serial_control->expansion_cb != NULL); + + furi_hal_gpio_remove_int_callback(gpio); + furi_hal_serial_enable_direction(handle, FuriHalSerialDirectionRx); + } + + furi_hal_serial_control->expansion_cb = message_input->callback; + furi_hal_serial_control->expansion_ctx = message_input->context; + + return true; +} + +static bool furi_hal_serial_control_handler_expansion_irq(void* input, void* output) { + UNUSED(input); + UNUSED(output); + + if(furi_hal_serial_control->expansion_cb) { + void* context = furi_hal_serial_control->expansion_ctx; + furi_hal_serial_control->expansion_cb(context); + } + + return true; +} + +typedef bool (*FuriHalSerialControlCommandHandler)(void* input, void* output); + +static const FuriHalSerialControlCommandHandler furi_hal_serial_control_handlers[] = { + [FuriHalSerialControlMessageTypeStop] = furi_hal_serial_control_handler_stop, + [FuriHalSerialControlMessageTypeSuspend] = furi_hal_serial_control_handler_suspend, + [FuriHalSerialControlMessageTypeResume] = furi_hal_serial_control_handler_resume, + [FuriHalSerialControlMessageTypeAcquire] = furi_hal_serial_control_handler_acquire, + [FuriHalSerialControlMessageTypeRelease] = furi_hal_serial_control_handler_release, + [FuriHalSerialControlMessageTypeLogging] = furi_hal_serial_control_handler_logging, + [FuriHalSerialControlMessageTypeExpansionSetCallback] = + furi_hal_serial_control_handler_expansion_set_callback, + [FuriHalSerialControlMessageTypeExpansionIrq] = furi_hal_serial_control_handler_expansion_irq, +}; + static int32_t furi_hal_serial_control_thread(void* args) { UNUSED(args); @@ -74,61 +230,13 @@ static int32_t furi_hal_serial_control_thread(void* args) { FuriStatus status = furi_message_queue_get(furi_hal_serial_control->queue, &message, FuriWaitForever); furi_check(status == FuriStatusOk); + furi_check(message.type < COUNT_OF(furi_hal_serial_control_handlers)); - if(message.type == FuriHalSerialControlMessageTypeStop) { - should_continue = false; - } else if(message.type == FuriHalSerialControlMessageTypeSuspend) { - for(size_t i = 0; i < FuriHalSerialIdMax; i++) { - furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]); - furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]); - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeResume) { - for(size_t i = 0; i < FuriHalSerialIdMax; i++) { - furi_hal_serial_resume(&furi_hal_serial_control->handles[i]); - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeAcquire) { - FuriHalSerialId serial_id = *(FuriHalSerialId*)message.input; - if(furi_hal_serial_control->handles[serial_id].in_use) { - *(FuriHalSerialHandle**)message.output = NULL; - } else { - // Logging - if(furi_hal_serial_control->log_config_serial_id == serial_id) { - furi_hal_serial_control_log_set_handle(NULL); - } - // Return handle - furi_hal_serial_control->handles[serial_id].in_use = true; - *(FuriHalSerialHandle**)message.output = - &furi_hal_serial_control->handles[serial_id]; - } - api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeRelease) { - FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)message.input; - furi_assert(handle->in_use); - furi_hal_serial_deinit(handle); - handle->in_use = false; + should_continue = + furi_hal_serial_control_handlers[message.type](message.input, message.output); - // Return back logging - if(furi_hal_serial_control->log_config_serial_id == handle->id) { - furi_hal_serial_control_log_set_handle(handle); - } + if(message.api_lock != NULL) { api_lock_unlock(message.api_lock); - } else if(message.type == FuriHalSerialControlMessageTypeLogging) { - // Set new configuration - FuriHalSerialControlMessageInputLogging* message_input = message.input; - furi_hal_serial_control->log_config_serial_id = message_input->id; - furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate; - // Apply new configuration - FuriHalSerialHandle* handle = NULL; - if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) { - handle = &furi_hal_serial_control - ->handles[furi_hal_serial_control->log_config_serial_id]; - } - furi_hal_serial_control_log_set_handle(handle); - api_lock_unlock(message.api_lock); - } else { - furi_crash("Invalid parameter"); } } @@ -157,6 +265,7 @@ void furi_hal_serial_control_deinit(void) { // Stop control plane thread FuriHalSerialControlMessage message; message.type = FuriHalSerialControlMessageTypeStop; + message.api_lock = NULL; furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); furi_thread_join(furi_hal_serial_control->thread); // Release resources @@ -220,6 +329,9 @@ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint3 // Very special case of updater, where RTC initialized before kernel start if(!furi_hal_serial_control) return; + furi_check(furi_hal_serial_is_baud_rate_supported( + &furi_hal_serial_control->handles[serial_id], baud_rate)); + FuriHalSerialControlMessageInputLogging message_input = { .id = serial_id, .baud_rate = baud_rate, @@ -231,3 +343,23 @@ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint3 furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); api_lock_wait_unlock_and_free(message.api_lock); } + +void furi_hal_serial_control_set_expansion_callback( + FuriHalSerialId serial_id, + FuriHalSerialControlExpansionCallback callback, + void* context) { + furi_check(serial_id <= FuriHalSerialIdMax); + furi_check(furi_hal_serial_control); + + FuriHalSerialControlMessageExpCallback message_input = { + .id = serial_id, + .callback = callback, + .context = context, + }; + FuriHalSerialControlMessage message; + message.type = FuriHalSerialControlMessageTypeExpansionSetCallback; + message.api_lock = api_lock_alloc_locked(); + message.input = &message_input; + furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); +} diff --git a/targets/f7/furi_hal/furi_hal_serial_control.h b/targets/f7/furi_hal/furi_hal_serial_control.h index 6b42281bf..01fdf0a88 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.h +++ b/targets/f7/furi_hal/furi_hal_serial_control.h @@ -41,6 +41,27 @@ void furi_hal_serial_control_release(FuriHalSerialHandle* handle); */ void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate); +/** + * @brief Expansion module detection callback type. + * + * @param[in,out] context Pointer to the user-defined context object. + */ +typedef void (*FuriHalSerialControlExpansionCallback)(void* context); + +/** + * @brief Enable expansion module detection for a given serial interface. + * + * Passing NULL as the callback parameter disables external module detection. + * + * @param[in] serial_id Identifier of the serial interface to be used. + * @param[in] callback Pointer to the callback function to be called upon module detection. + * @param[in,out] context Pointer to the user-defined context object. Will be passed to the callback function. + */ +void furi_hal_serial_control_set_expansion_callback( + FuriHalSerialId serial_id, + FuriHalSerialControlExpansionCallback callback, + void* context); + #ifdef __cplusplus } #endif diff --git a/targets/f7/furi_hal/furi_hal_serial_types.h b/targets/f7/furi_hal/furi_hal_serial_types.h index d5db36b29..9f10102e1 100644 --- a/targets/f7/furi_hal/furi_hal_serial_types.h +++ b/targets/f7/furi_hal/furi_hal_serial_types.h @@ -12,4 +12,11 @@ typedef enum { FuriHalSerialIdMax, } FuriHalSerialId; +typedef enum { + FuriHalSerialDirectionTx, + FuriHalSerialDirectionRx, + + FuriHalSerialDirectionMax, +} FuriHalSerialDirection; + typedef struct FuriHalSerialHandle FuriHalSerialHandle; From 7981cb832ed069f554c5959aecc944f37bfb9b9a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:24:03 +0300 Subject: [PATCH 11/15] [FL-3678] NFC UI refactor (#3369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく Co-authored-by: gornekich --- .../protocol_support/nfc_protocol_support.c | 41 +++++++++++-------- .../nfc_scene_mf_classic_write_initial.c | 3 +- .../scenes/nfc_scene_mf_ultralight_write.c | 3 +- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index bc152a710..c87ee613f 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -391,12 +391,15 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance); // Trailer submenu items - submenu_add_item( - submenu, - "Info", - SubmenuIndexCommonInfo, - nfc_protocol_support_common_submenu_callback, - instance); + if(nfc_has_shadow_file(instance)) { + submenu_add_item( + submenu, + "Restore to Original State", + SubmenuIndexCommonRestore, + nfc_protocol_support_common_submenu_callback, + instance); + } + submenu_add_item( submenu, "Rename", @@ -409,15 +412,12 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { SubmenuIndexCommonDelete, nfc_protocol_support_common_submenu_callback, instance); - - if(nfc_has_shadow_file(instance)) { - submenu_add_item( - submenu, - "Restore Data Changes", - SubmenuIndexCommonRestore, - nfc_protocol_support_common_submenu_callback, - instance); - } + submenu_add_item( + submenu, + "Info", + SubmenuIndexCommonInfo, + nfc_protocol_support_common_submenu_callback, + instance); submenu_set_selected_item( instance->submenu, @@ -582,9 +582,14 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { } else { widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); - furi_string_set( - temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); - furi_string_cat_printf(temp_str, "\n%s", furi_string_get_cstr(instance->file_name)); + if(!furi_string_empty(instance->file_name)) { + furi_string_set(temp_str, instance->file_name); + } else { + furi_string_printf( + temp_str, + "Unsaved\n%s", + nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + } } widget_add_text_box_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c index 79f1def1d..da576a276 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c @@ -65,8 +65,9 @@ static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); popup_set_text( - instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c index b3c1beef5..157d6ce1b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c @@ -46,8 +46,9 @@ static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) { scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite); if(state == NfcSceneMfUltralightWriteStateCardSearch) { + popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter); popup_set_text( - instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); From 7db8d5aa824b541a244e908646038ecda9d521a7 Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 16 Jan 2024 13:41:51 +0400 Subject: [PATCH 12/15] [FL-3648] Mf DESFire fixes (#3367) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mf desfire: process loading applications with 0 files * mf desfire: add HID desfire support * nfc: fix mfdes loading and rendering crashes * mf desfire: change handling HID cards * mf desfire: fix PVS warnings * mf desfire: fix cmp logic Co-authored-by: あく --- lib/nfc/protocols/iso14443_4a/iso14443_4a.c | 7 +- lib/nfc/protocols/mf_desfire/mf_desfire_i.c | 91 ++++++++++++--------- 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c index 9c2a530d5..bfa2e71c6 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c @@ -252,7 +252,12 @@ const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uin furi_assert(count); *count = simple_array_get_count(data->ats_data.t1_tk); - return simple_array_cget_data(data->ats_data.t1_tk); + const uint8_t* hist_bytes = NULL; + if(*count > 0) { + hist_bytes = simple_array_cget_data(data->ats_data.t1_tk); + } + + return hist_bytes; } bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) { diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c index 8e65eca5a..646803e75 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -179,44 +179,53 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer const size_t data_size = bit_buffer_get_size_bytes(buf); const size_t min_data_size = sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData); + const size_t max_data_size = + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue); if(data_size < min_data_size) break; + if(data_size <= max_data_size) { + MfDesfireFileSettingsLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout)); - MfDesfireFileSettingsLayout layout; - bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout)); + data->type = layout.header.type; + data->comm = layout.header.comm; + data->access_rights = layout.header.access_rights; - data->type = layout.header.type; - data->comm = layout.header.comm; - data->access_rights = layout.header.access_rights; + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + if(data_size != min_data_size) break; - if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { - if(data_size != min_data_size) break; + data->data.size = layout.data.size; + } else if(data->type == MfDesfireFileTypeValue) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue)) + break; - data->data.size = layout.data.size; + data->value.lo_limit = layout.value.lo_limit; + data->value.hi_limit = layout.value.hi_limit; + data->value.limited_credit_value = layout.value.limited_credit_value; + data->value.limited_credit_enabled = layout.value.limited_credit_enabled; - } else if(data->type == MfDesfireFileTypeValue) { - if(data_size != - sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue)) + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord)) + break; + + data->record.size = layout.record.size; + data->record.max = layout.record.max; + data->record.cur = layout.record.cur; + + } else { break; - - data->value.lo_limit = layout.value.lo_limit; - data->value.hi_limit = layout.value.hi_limit; - data->value.limited_credit_value = layout.value.limited_credit_value; - data->value.limited_credit_enabled = layout.value.limited_credit_enabled; - - } else if( - data->type == MfDesfireFileTypeLinearRecord || - data->type == MfDesfireFileTypeCyclicRecord) { - if(data_size != - sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord)) - break; - - data->record.size = layout.record.size; - data->record.max = layout.record.max; - data->record.cur = layout.record.cur; - + } } else { - break; + // TODO FL-3750: process HID Desfire command response here + // Set default fields for now + data->type = 0; + data->comm = 0; + data->access_rights = 0; + data->data.size = 0; } parsed = true; @@ -478,19 +487,25 @@ bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, do { if(!mf_desfire_key_settings_load(&data->key_settings, prefix, ff)) break; - const uint32_t key_version_count = data->key_settings.max_keys; - simple_array_init(data->key_versions, key_version_count); - uint32_t i; - for(i = 0; i < key_version_count; ++i) { - if(!mf_desfire_key_version_load(simple_array_get(data->key_versions, i), prefix, i, ff)) - break; + const uint32_t key_version_count = data->key_settings.max_keys; + if(key_version_count) { + simple_array_init(data->key_versions, key_version_count); + + for(i = 0; i < key_version_count; ++i) { + if(!mf_desfire_key_version_load( + simple_array_get(data->key_versions, i), prefix, i, ff)) + break; + } + + if(i != key_version_count) break; } - if(i != key_version_count) break; - uint32_t file_count; - if(!mf_desfire_file_count_load(&file_count, prefix, ff)) break; + if(!mf_desfire_file_count_load(&file_count, prefix, ff)) { + success = true; + break; + } simple_array_init(data->file_ids, file_count); if(!mf_desfire_file_ids_load(simple_array_get_data(data->file_ids), file_count, prefix, ff)) From a4ebbeabd2610e0c0df46c5a79968d96a926c319 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:25:36 +0300 Subject: [PATCH 13/15] RFID CLI: better usage (#3376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/lfrfid/lfrfid_cli.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/main/lfrfid/lfrfid_cli.c index a57e40de9..af340008a 100644 --- a/applications/main/lfrfid/lfrfid_cli.c +++ b/applications/main/lfrfid/lfrfid_cli.c @@ -25,11 +25,13 @@ void lfrfid_on_system_start() { static void lfrfid_cli_print_usage() { printf("Usage:\r\n"); - printf("rfid read \r\n"); - printf("rfid \r\n"); - printf("rfid raw_read \r\n"); - printf("rfid raw_emulate \r\n"); - printf("rfid raw_analyze \r\n"); + printf("rfid read - read in ASK/PSK mode\r\n"); + printf("rfid - write or emulate a card\r\n"); + printf("rfid raw_read - read and save raw data to a file\r\n"); + printf( + "rfid raw_emulate - emulate raw data (not very useful, but helps debug protocols)\r\n"); + printf( + "rfid raw_analyze - outputs raw data to the cli and tries to decode it (useful for protocol development)\r\n"); }; typedef struct { From 39e9dd0ab0c136281ce9e37c31b9a762fa764d4d Mon Sep 17 00:00:00 2001 From: Methodius Date: Sun, 14 Jan 2024 20:24:45 +0900 Subject: [PATCH 14/15] T5577 write/clear with custom password option added --- applications/main/lfrfid/lfrfid.c | 24 ++++++++ applications/main/lfrfid/lfrfid_i.h | 4 ++ .../lfrfid/scenes/lfrfid_scene_clear_t5577.c | 19 ++++++- .../main/lfrfid/scenes/lfrfid_scene_config.h | 3 +- .../scenes/lfrfid_scene_enter_password.c | 55 +++++++++++++++++++ .../scenes/lfrfid_scene_extra_actions.c | 4 +- .../scenes/lfrfid_scene_saved_key_menu.c | 12 ++-- ...ss.c => lfrfid_scene_write_and_set_pass.c} | 26 +++------ lib/lfrfid/lfrfid_worker.c | 11 ++-- lib/lfrfid/lfrfid_worker.h | 4 +- lib/lfrfid/lfrfid_worker_i.h | 2 +- lib/lfrfid/lfrfid_worker_modes.c | 32 ++++++++--- lib/lfrfid/tools/t5577.c | 29 +--------- lib/lfrfid/tools/t5577.h | 4 +- targets/f7/api_symbols.csv | 5 +- 15 files changed, 157 insertions(+), 77 deletions(-) create mode 100644 applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c rename applications/main/lfrfid/scenes/{lfrfid_scene_write_with_pass.c => lfrfid_scene_write_and_set_pass.c} (79%) diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 8eb78a9c7..bd64c7906 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -1,6 +1,30 @@ #include "lfrfid_i.h" #include +//TODO: use .txt file in resources for passwords. +const uint32_t default_passwords[] = { + 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, 0x89A69E60, + 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, 0x4E457854, 0x44B44CAE, + 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666, 0x65857569, 0x5469616E, 0x7686962A, + 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de, 0x00000000, 0x11111111, 0x22222222, 0x33333333, + 0x44444444, 0x55555555, 0x66666666, 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, + 0xCCCCCCCC, 0xDDDDDDDD, 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, + 0x00000002, 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708, + 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F, 0x01234567, + 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, 0x60000000, 0x70000000, + 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000, 0xD0000000, 0xE0000000, 0xF0000000, + 0x10101010, 0x01010101, 0x11223344, 0x22334455, 0x33445566, 0x44556677, 0x55667788, 0x66778899, + 0x778899AA, 0x8899AABB, 0x99AABBCC, 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, + 0x87654321, 0x12341234, 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, + 0x10041004, 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149, + 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED, 0x69314718, + 0x57721566, 0x93C467E3, 0x27182818, 0x50415353}; + +const uint32_t* lfrfid_get_t5577_default_passwords(uint8_t* len) { + *len = sizeof(default_passwords) / sizeof(uint32_t); + return default_passwords; +} + static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { furi_assert(context); LfRfid* app = context; diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index fc9f861a5..cff2c6cc4 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -98,6 +98,8 @@ struct LfRfid { uint8_t* old_key_data; uint8_t* new_key_data; + uint8_t password[4]; + RpcAppSystem* rpc_ctx; LfRfidRpcState rpc_state; @@ -145,3 +147,5 @@ void lfrfid_popup_timeout_callback(void* context); void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context); void lfrfid_text_input_callback(void* context); + +const uint32_t* lfrfid_get_t5577_default_passwords(uint8_t* len); \ No newline at end of file diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index c42ad6acb..71b6b7aee 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include "tools/t5577.h" #define TAG "Clear T5577" static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { @@ -6,7 +7,7 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { char curr_buf[32] = {}; uint8_t default_passwords_len; - const uint32_t* default_passwords = t5577_get_default_passwords(&default_passwords_len); + const uint32_t* default_passwords = lfrfid_get_t5577_default_passwords(&default_passwords_len); popup_set_header(popup, "Removing\npassword", 90, 36, AlignCenter, AlignCenter); popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); @@ -15,14 +16,26 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { LFRFIDT5577 data = { .block[0] = 0b00000000000101001000000001000000, - .blocks_to_write = 1, + .block[7] = 0, + .mask = 0b10000001, }; + // Clear custom password + uint32_t custom_pass = (app->password[0] << 24) | (app->password[1] << 16) | + (app->password[2] << 8) | (app->password[3]); + snprintf(curr_buf, sizeof(curr_buf), "Custom password"); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + + t5577_write_with_mask(&data, 0, true, custom_pass); + + furi_delay_ms(8); + + // Clear default passwords for(uint8_t i = 0; i < default_passwords_len; i++) { snprintf(curr_buf, sizeof(curr_buf), "Pass %d of %d", i, default_passwords_len); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); - t5577_write_with_pass(&data, default_passwords[i]); + t5577_write_with_mask(&data, 0, true, default_passwords[i]); furi_delay_ms(8); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h index 0d7dfe46d..47844a8b3 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_config.h +++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h @@ -6,7 +6,7 @@ ADD_SCENE(lfrfid, exit_confirm, ExitConfirm) ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm) ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu) ADD_SCENE(lfrfid, write, Write) -ADD_SCENE(lfrfid, write_with_pass, WriteWithPass) +ADD_SCENE(lfrfid, write_and_set_pass, WriteAndSetPass) ADD_SCENE(lfrfid, write_success, WriteSuccess) ADD_SCENE(lfrfid, emulate, Emulate) ADD_SCENE(lfrfid, save_name, SaveName) @@ -18,6 +18,7 @@ ADD_SCENE(lfrfid, save_type, SaveType) ADD_SCENE(lfrfid, saved_info, SavedInfo) ADD_SCENE(lfrfid, clear_t5577, ClearT5577) ADD_SCENE(lfrfid, clear_t5577_confirm, ClearT5577Confirm) +ADD_SCENE(lfrfid, enter_password, EnterPassword) ADD_SCENE(lfrfid, delete_success, DeleteSuccess) ADD_SCENE(lfrfid, extra_actions, ExtraActions) ADD_SCENE(lfrfid, raw_info, RawInfo) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c b/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c new file mode 100644 index 000000000..486be136c --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_enter_password.c @@ -0,0 +1,55 @@ +#include "../lfrfid_i.h" +#include "gui/scene_manager.h" + +int next_scene; + +void lfrfid_scene_enter_password_on_enter(void* context) { + LfRfid* app = context; + ByteInput* byte_input = app->byte_input; + + // true - use password for write, false - use password for clear pass + next_scene = scene_manager_get_scene_state(app->scene_manager, LfRfidSceneEnterPassword); + + bool password_set = app->password[0] | app->password[1] | app->password[2] | app->password[3]; + + if(next_scene == LfRfidSceneWriteAndSetPass && !password_set) { + uint8_t password_list_size; + const uint32_t* password_list = lfrfid_get_t5577_default_passwords(&password_list_size); + uint32_t pass = password_list[furi_get_tick() % password_list_size]; + + for(uint8_t i = 0; i < 4; i++) app->password[i] = (pass >> (8 * i)) & 0xFF; + } + + byte_input_set_header_text(byte_input, "Enter the password in hex"); + + byte_input_set_result_callback( + byte_input, lfrfid_text_input_callback, NULL, app, app->password, 4); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewByteInput); +} + +bool lfrfid_scene_enter_password_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventNext) { + consumed = true; + + scene_manager_next_scene(scene_manager, next_scene); + scene_manager_set_scene_state(scene_manager, LfRfidSceneEnterPassword, 1); + } + } else if(event.type == SceneManagerEventTypeBack) { + uint32_t prev_scenes[] = {LfRfidSceneExtraActions, LfRfidSceneSavedKeyMenu}; + scene_manager_set_scene_state(scene_manager, LfRfidSceneEnterPassword, 0); + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, prev_scenes, sizeof(prev_scenes[0])); + } + + return consumed; +} + +void lfrfid_scene_enter_password_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index 3eb34fde9..db4458236 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -80,7 +80,9 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexClearT5577) { - scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577Confirm); + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneEnterPassword, LfRfidSceneClearT5577Confirm); + scene_manager_next_scene(app->scene_manager, LfRfidSceneEnterPassword); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index f01688a66..b4d6a6f43 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -4,7 +4,7 @@ typedef enum { SubmenuIndexEmulate, SubmenuIndexWrite, - SubmenuIndexWriteWithPass, + SubmenuIndexWriteAndSetPass, SubmenuIndexEdit, SubmenuIndexDelete, SubmenuIndexInfo, @@ -26,8 +26,8 @@ void lfrfid_scene_saved_key_menu_on_enter(void* context) { submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu_add_item( submenu, - "Write with pass", - SubmenuIndexWriteWithPass, + "Write and set pass", + SubmenuIndexWriteAndSetPass, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu_add_item( @@ -55,8 +55,10 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); consumed = true; - } else if(event.event == SubmenuIndexWriteWithPass) { - scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteWithPass); + } else if(event.event == SubmenuIndexWriteAndSetPass) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneEnterPassword, LfRfidSceneWriteAndSetPass); + scene_manager_next_scene(app->scene_manager, LfRfidSceneEnterPassword); consumed = true; } else if(event.event == SubmenuIndexEdit) { scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_and_set_pass.c similarity index 79% rename from applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c rename to applications/main/lfrfid/scenes/lfrfid_scene_write_and_set_pass.c index 263db5cde..275a3e889 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write_with_pass.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_and_set_pass.c @@ -1,6 +1,7 @@ #include "../lfrfid_i.h" +#include "gui/scene_manager.h" -static void lfrfid_write_with_pass_callback(LFRFIDWorkerWriteResult result, void* context) { +static void lfrfid_write_and_set_pass_callback(LFRFIDWorkerWriteResult result, void* context) { LfRfid* app = context; uint32_t event = 0; @@ -17,22 +18,11 @@ static void lfrfid_write_with_pass_callback(LFRFIDWorkerWriteResult result, void view_dispatcher_send_custom_event(app->view_dispatcher, event); } -void lfrfid_scene_write_with_pass_on_enter(void* context) { +void lfrfid_scene_write_and_set_pass_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop); - if(!furi_string_empty(app->file_name)) { - popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); - } else { - popup_set_text( - popup, - protocol_dict_get_name(app->dict, app->protocol_id), - 89, - 43, - AlignCenter, - AlignTop); - } + popup_set_header(popup, "Writing\nwith password", 89, 30, AlignCenter, AlignTop); popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); @@ -41,12 +31,12 @@ void lfrfid_scene_write_with_pass_on_enter(void* context) { protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); lfrfid_worker_start_thread(app->lfworker); - lfrfid_worker_write_with_pass_start( - app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_with_pass_callback, app); + lfrfid_worker_write_and_set_pass_start( + app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_and_set_pass_callback, app); notification_message(app->notifications, &sequence_blink_start_magenta); } -bool lfrfid_scene_write_with_pass_on_event(void* context, SceneManagerEvent event) { +bool lfrfid_scene_write_and_set_pass_on_event(void* context, SceneManagerEvent event) { LfRfid* app = context; Popup* popup = app->popup; bool consumed = false; @@ -82,7 +72,7 @@ bool lfrfid_scene_write_with_pass_on_event(void* context, SceneManagerEvent even return consumed; } -void lfrfid_scene_write_with_pass_on_exit(void* context) { +void lfrfid_scene_write_and_set_pass_on_exit(void* context) { LfRfid* app = context; notification_message(app->notifications, &sequence_blink_stop); popup_reset(app->popup); diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index 6b40924d2..7fda36f0b 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -8,13 +8,13 @@ typedef enum { LFRFIDEventStopMode = (1 << 1), LFRFIDEventRead = (1 << 2), LFRFIDEventWrite = (1 << 3), - LFRFIDEventWriteWithPass = (1 << 4), + LFRFIDEventWriteAndSetPass = (1 << 4), LFRFIDEventEmulate = (1 << 5), LFRFIDEventReadRaw = (1 << 6), LFRFIDEventEmulateRaw = (1 << 7), LFRFIDEventAll = (LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite | - LFRFIDEventWriteWithPass | LFRFIDEventEmulate | LFRFIDEventReadRaw | + LFRFIDEventWriteAndSetPass | LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw), } LFRFIDEventType; @@ -71,7 +71,7 @@ void lfrfid_worker_write_start( furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWrite); } -void lfrfid_worker_write_with_pass_start( +void lfrfid_worker_write_and_set_pass_start( LFRFIDWorker* worker, LFRFIDProtocol protocol, LFRFIDWorkerWriteCallback callback, @@ -80,7 +80,7 @@ void lfrfid_worker_write_with_pass_start( worker->protocol = protocol; worker->write_cb = callback; worker->cb_ctx = context; - furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWriteWithPass); + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWriteAndSetPass); } void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol) { @@ -159,7 +159,8 @@ static int32_t lfrfid_worker_thread(void* thread_context) { // switch mode if(flags & LFRFIDEventRead) worker->mode_index = LFRFIDWorkerRead; if(flags & LFRFIDEventWrite) worker->mode_index = LFRFIDWorkerWrite; - if(flags & LFRFIDEventWriteWithPass) worker->mode_index = LFRFIDWorkerWriteWithPass; + if(flags & LFRFIDEventWriteAndSetPass) + worker->mode_index = LFRFIDWorkerWriteAndSetPass; if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate; if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw; if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw; diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h index ed09d6143..7ec6b6008 100644 --- a/lib/lfrfid/lfrfid_worker.h +++ b/lib/lfrfid/lfrfid_worker.h @@ -107,14 +107,14 @@ void lfrfid_worker_write_start( void* context); /** - * @brief Start write with pass mode + * @brief Start write and set pass mode * * @param worker * @param protocol * @param callback * @param context */ -void lfrfid_worker_write_with_pass_start( +void lfrfid_worker_write_and_set_pass_start( LFRFIDWorker* worker, LFRFIDProtocol protocol, LFRFIDWorkerWriteCallback callback, diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h index 16d1f9716..7c31151bb 100644 --- a/lib/lfrfid/lfrfid_worker_i.h +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -22,7 +22,7 @@ typedef enum { LFRFIDWorkerIdle, LFRFIDWorkerRead, LFRFIDWorkerWrite, - LFRFIDWorkerWriteWithPass, + LFRFIDWorkerWriteAndSetPass, LFRFIDWorkerEmulate, LFRFIDWorkerReadRaw, LFRFIDWorkerEmulateRaw, diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 3db438eec..17e42ad97 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -1,3 +1,4 @@ +#include "lfrfid/lfrfid_i.h" #include #include #include "lfrfid_worker_i.h" @@ -48,6 +49,15 @@ void lfrfid_worker_delay(LFRFIDWorker* worker, uint32_t milliseconds) { } } +void t5577_trace(LFRFIDT5577 t5577, const char* message) { + if(furi_log_get_level() == FuriLogLevelTrace) { + FURI_LOG_T(TAG, "%s", message); + for(uint8_t i = 0; i < 8; i++) FURI_LOG_T(TAG, "\nBlock %u %08lX", i, t5577.block[i]); + FURI_LOG_T(TAG, "Mask: %u", t5577.mask); + FURI_LOG_T(TAG, "Blocks to write: %lu", t5577.blocks_to_write); + } +} + /**************************************************************************************************/ /********************************************** READ **********************************************/ /**************************************************************************************************/ @@ -574,7 +584,7 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { free(read_data); } -static void lfrfid_worker_mode_write_with_pass_process(LFRFIDWorker* worker) { +static void lfrfid_worker_mode_write_and_set_pass_process(LFRFIDWorker* worker) { LFRFIDProtocol protocol = worker->protocol; LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest)); request->write_type = LFRFIDWriteTypeT5577; @@ -592,18 +602,22 @@ static void lfrfid_worker_mode_write_with_pass_process(LFRFIDWorker* worker) { if(can_be_written) { while(!lfrfid_worker_check_for_stop(worker)) { - FURI_LOG_D(TAG, "Data write"); + FURI_LOG_D(TAG, "Data write with pass"); - uint8_t size; - const uint32_t* password_list = t5577_get_default_passwords(&size); + LfRfid* app = worker->cb_ctx; + uint32_t pass = (app->password[0] << 24) | (app->password[1] << 16) | + (app->password[2] << 8) | (app->password[3]); - uint32_t pass = password_list[rand() % size]; + request->t5577.mask = 0b10000001; + for(uint8_t i = 0; i < request->t5577.blocks_to_write; i++) + request->t5577.mask |= (1 << i); - request->t5577.mask = 0b1111111; - request->t5577.block[0] |= 0b10000; + request->t5577.block[0] |= (1 << 4); request->t5577.block[7] = pass; - t5577_write_with_mask(&request->t5577, 0, 0); + t5577_trace(request->t5577, "Write with password"); + + t5577_write_with_mask(&request->t5577, 0, true, 0); ProtocolId read_result = PROTOCOL_NO; LFRFIDWorkerReadState state = lfrfid_worker_read_internal( @@ -719,7 +733,7 @@ const LFRFIDWorkerModeType lfrfid_worker_modes[] = { [LFRFIDWorkerIdle] = {.process = NULL}, [LFRFIDWorkerRead] = {.process = lfrfid_worker_mode_read_process}, [LFRFIDWorkerWrite] = {.process = lfrfid_worker_mode_write_process}, - [LFRFIDWorkerWriteWithPass] = {.process = lfrfid_worker_mode_write_with_pass_process}, + [LFRFIDWorkerWriteAndSetPass] = {.process = lfrfid_worker_mode_write_and_set_pass_process}, [LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process}, [LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process}, [LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process}, diff --git a/lib/lfrfid/tools/t5577.c b/lib/lfrfid/tools/t5577.c index 83ae99989..7d6d6d298 100644 --- a/lib/lfrfid/tools/t5577.c +++ b/lib/lfrfid/tools/t5577.c @@ -1,6 +1,7 @@ #include "t5577.h" #include #include +#include #define T5577_TIMING_WAIT_TIME 400 #define T5577_TIMING_START_GAP 30 @@ -16,30 +17,6 @@ #define T5577_BLOCKS_IN_PAGE_0 8 #define T5577_BLOCKS_IN_PAGE_1 4 -//TODO: use .txt file in resources for passwords. -const uint32_t default_passwords[] = { - 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, 0x89A69E60, - 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, 0x4E457854, 0x44B44CAE, - 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666, 0x65857569, 0x5469616E, 0x7686962A, - 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de, 0x00000000, 0x11111111, 0x22222222, 0x33333333, - 0x44444444, 0x55555555, 0x66666666, 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, - 0xCCCCCCCC, 0xDDDDDDDD, 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, - 0x00000002, 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708, - 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F, 0x01234567, - 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, 0x60000000, 0x70000000, - 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000, 0xD0000000, 0xE0000000, 0xF0000000, - 0x10101010, 0x01010101, 0x11223344, 0x22334455, 0x33445566, 0x44556677, 0x55667788, 0x66778899, - 0x778899AA, 0x8899AABB, 0x99AABBCC, 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, - 0x87654321, 0x12341234, 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, - 0x10041004, 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149, - 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED, 0x69314718, - 0x57721566, 0x93C467E3, 0x27182818, 0x50415353}; - -const uint32_t* t5577_get_default_passwords(uint8_t* len) { - *len = sizeof(default_passwords) / sizeof(uint32_t); - return default_passwords; -} - static void t5577_start() { furi_hal_rfid_tim_read_start(125000, 0.5); @@ -145,7 +122,7 @@ void t5577_write_with_pass(LFRFIDT5577* data, uint32_t password) { t5577_stop(); } -void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password) { +void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, bool with_pass, uint32_t password) { t5577_start(); FURI_CRITICAL_ENTER(); @@ -157,7 +134,7 @@ void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password) { bool need_to_write = mask & 1; mask >>= 1; if(!need_to_write) continue; - t5577_write_block_pass(page, i, false, data->block[i], true, password); + t5577_write_block_pass(page, i, false, data->block[i], with_pass, password); } t5577_write_reset(); FURI_CRITICAL_EXIT(); diff --git a/lib/lfrfid/tools/t5577.h b/lib/lfrfid/tools/t5577.h index e78581ac0..f7b5cc4f5 100644 --- a/lib/lfrfid/tools/t5577.h +++ b/lib/lfrfid/tools/t5577.h @@ -45,8 +45,6 @@ typedef struct { uint8_t mask; } LFRFIDT5577; -const uint32_t* t5577_get_default_passwords(uint8_t* len); - /** * @brief Write T5577 tag data to tag * @@ -56,7 +54,7 @@ void t5577_write(LFRFIDT5577* data); void t5577_write_with_pass(LFRFIDT5577* data, uint32_t password); -void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, uint32_t password); +void t5577_write_with_mask(LFRFIDT5577* data, uint8_t page, bool with_pass, uint32_t password); #ifdef __cplusplus } diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index acd954475..11d4918cc 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2069,8 +2069,8 @@ Function,+,lfrfid_worker_read_start,void,"LFRFIDWorker*, LFRFIDWorkerReadType, L Function,+,lfrfid_worker_start_thread,void,LFRFIDWorker* Function,+,lfrfid_worker_stop,void,LFRFIDWorker* Function,+,lfrfid_worker_stop_thread,void,LFRFIDWorker* +Function,+,lfrfid_worker_write_and_set_pass_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" Function,+,lfrfid_worker_write_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" -Function,+,lfrfid_worker_write_with_pass_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" Function,-,lgamma,double,double Function,-,lgamma_r,double,"double, int*" Function,-,lgammaf,float,float @@ -3227,9 +3227,8 @@ Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_orientation,void,"Submenu*, ViewOrientation" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" Function,-,system,int,const char* -Function,+,t5577_get_default_passwords,const uint32_t*,uint8_t* Function,+,t5577_write,void,LFRFIDT5577* -Function,+,t5577_write_with_mask,void,"LFRFIDT5577*, uint8_t, uint32_t" +Function,+,t5577_write_with_mask,void,"LFRFIDT5577*, uint8_t, _Bool, uint32_t" Function,+,t5577_write_with_pass,void,"LFRFIDT5577*, uint32_t" Function,-,tan,double,double Function,-,tanf,float,float From 41c316d61297478415b675bf96055d0f4269289d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 20 Jan 2024 08:48:26 +0300 Subject: [PATCH 15/15] mac os music remote todo - add more buttons --- applications/system/hid_app/hid.c | 35 ++- applications/system/hid_app/hid.h | 2 + applications/system/hid_app/views.h | 1 + .../system/hid_app/views/hid_music_macos.c | 242 ++++++++++++++++++ .../system/hid_app/views/hid_music_macos.h | 13 + 5 files changed, 286 insertions(+), 7 deletions(-) create mode 100644 applications/system/hid_app/views/hid_music_macos.c create mode 100644 applications/system/hid_app/views/hid_music_macos.h diff --git a/applications/system/hid_app/hid.c b/applications/system/hid_app/hid.c index bf7c39914..eea9e60b6 100644 --- a/applications/system/hid_app/hid.c +++ b/applications/system/hid_app/hid.c @@ -11,6 +11,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeyboard, HidSubmenuIndexNumpad, HidSubmenuIndexMedia, + HidSubmenuIndexMusicMacOs, HidSubmenuIndexMovie, HidSubmenuIndexTikShorts, HidSubmenuIndexMouse, @@ -39,6 +40,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexMedia) { app->view_id = HidViewMedia; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); + } else if(index == HidSubmenuIndexMusicMacOs) { + app->view_id = HidViewMusicMacOs; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMusicMacOs); } else if(index == HidSubmenuIndexMovie) { app->view_id = HidViewMovie; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMovie); @@ -75,6 +79,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_numpad_set_connected_status(hid->hid_numpad, connected); hid_media_set_connected_status(hid->hid_media, connected); + hid_music_macos_set_connected_status(hid->hid_music_macos, connected); hid_movie_set_connected_status(hid->hid_movie, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected); hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); @@ -131,6 +136,12 @@ Hid* hid_alloc(HidTransport transport) { app->device_type_submenu, "Numpad", HidSubmenuIndexNumpad, hid_submenu_callback, app); submenu_add_item( app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "Apple Music macOS", + HidSubmenuIndexMusicMacOs, + hid_submenu_callback, + app); submenu_add_item( app->device_type_submenu, "Movie", HidSubmenuIndexMovie, hid_submenu_callback, app); submenu_add_item( @@ -156,7 +167,11 @@ Hid* hid_alloc(HidTransport transport) { hid_submenu_callback, app); submenu_add_item( - app->device_type_submenu, "PushToTalk", HidSubmenuIndexPushToTalk, hid_submenu_callback, app); + app->device_type_submenu, + "PushToTalk", + HidSubmenuIndexPushToTalk, + hid_submenu_callback, + app); view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); view_dispatcher_add_view( app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); @@ -192,7 +207,13 @@ Hid* hid_app_alloc_view(void* context) { view_set_previous_callback(hid_media_get_view(app->hid_media), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); - + + // Music MacOs view + app->hid_music_macos = hid_music_macos_alloc(app); + view_set_previous_callback(hid_music_macos_get_view(app->hid_music_macos), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMusicMacOs, hid_music_macos_get_view(app->hid_music_macos)); + // Movie view app->hid_movie = hid_movie_alloc(app); view_set_previous_callback(hid_movie_get_view(app->hid_movie), hid_menu_view); @@ -213,8 +234,7 @@ Hid* hid_app_alloc_view(void* context) { // Mouse clicker view app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); - view_set_previous_callback( - hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_menu_view); + view_set_previous_callback(hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMouseClicker, @@ -222,8 +242,7 @@ Hid* hid_app_alloc_view(void* context) { // Mouse jiggler view app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); - view_set_previous_callback( - hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_menu_view); + view_set_previous_callback(hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMouseJiggler, @@ -233,7 +252,7 @@ Hid* hid_app_alloc_view(void* context) { app->hid_ptt_menu = hid_ptt_menu_alloc(app); view_set_previous_callback(hid_ptt_menu_get_view(app->hid_ptt_menu), hid_menu_view); view_dispatcher_add_view( - app->view_dispatcher, HidViewPushToTalkMenu, hid_ptt_menu_get_view(app->hid_ptt_menu)); + app->view_dispatcher, HidViewPushToTalkMenu, hid_ptt_menu_get_view(app->hid_ptt_menu)); app->hid_ptt = hid_ptt_alloc(app); view_set_previous_callback(hid_ptt_get_view(app->hid_ptt), hid_ptt_menu_view); view_dispatcher_add_view( @@ -261,6 +280,8 @@ void hid_free(Hid* app) { hid_numpad_free(app->hid_numpad); view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); hid_media_free(app->hid_media); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMusicMacOs); + hid_music_macos_free(app->hid_music_macos); view_dispatcher_remove_view(app->view_dispatcher, HidViewMovie); hid_movie_free(app->hid_movie); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); diff --git a/applications/system/hid_app/hid.h b/applications/system/hid_app/hid.h index ccbbb02d7..3c0f0ae79 100644 --- a/applications/system/hid_app/hid.h +++ b/applications/system/hid_app/hid.h @@ -20,6 +20,7 @@ #include "views/hid_keyboard.h" #include "views/hid_numpad.h" #include "views/hid_media.h" +#include "views/hid_music_macos.h" #include "views/hid_movie.h" #include "views/hid_mouse.h" #include "views/hid_mouse_clicker.h" @@ -48,6 +49,7 @@ struct Hid { HidKeyboard* hid_keyboard; HidNumpad* hid_numpad; HidMedia* hid_media; + HidMusicMacos* hid_music_macos; HidMovie* hid_movie; HidMouse* hid_mouse; HidMouseClicker* hid_mouse_clicker; diff --git a/applications/system/hid_app/views.h b/applications/system/hid_app/views.h index 961faec52..583ddb4b9 100644 --- a/applications/system/hid_app/views.h +++ b/applications/system/hid_app/views.h @@ -4,6 +4,7 @@ typedef enum { HidViewKeyboard, HidViewNumpad, HidViewMedia, + HidViewMusicMacOs, HidViewMovie, HidViewMouse, HidViewMouseClicker, diff --git a/applications/system/hid_app/views/hid_music_macos.c b/applications/system/hid_app/views/hid_music_macos.c new file mode 100644 index 000000000..d5dd4dab3 --- /dev/null +++ b/applications/system/hid_app/views/hid_music_macos.c @@ -0,0 +1,242 @@ +#include "hid_music_macos.h" +#include +#include +#include +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMusicMacos" + +struct HidMusicMacos { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool back_pressed; + HidTransport transport; +} HidMusicMacosModel; + +static void hid_music_macos_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + canvas_draw_triangle(canvas, x, y, 5, 3, dir); + if(dir == CanvasDirectionBottomToTop) { + canvas_draw_dot(canvas, x, y - 1); + } else if(dir == CanvasDirectionTopToBottom) { + canvas_draw_dot(canvas, x, y + 1); + } else if(dir == CanvasDirectionRightToLeft) { + canvas_draw_dot(canvas, x - 1, y); + } else if(dir == CanvasDirectionLeftToRight) { + canvas_draw_dot(canvas, x + 1, y); + } +} + +static void hid_music_macos_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMusicMacosModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Music"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_music_macos_draw_arrow(canvas, 67, 28, CanvasDirectionRightToLeft); + hid_music_macos_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft); + canvas_draw_line(canvas, 64, 26, 64, 30); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_music_macos_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight); + hid_music_macos_draw_arrow(canvas, 99, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 102, 26, 102, 30); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_music_macos_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 84, 26, 84, 30); + canvas_draw_line(canvas, 86, 26, 86, 30); + canvas_set_color(canvas, ColorBlack); + + // Exit + if(model->back_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); +} + +static void hid_music_macos_process_press(HidMusicMacos* hid_music_macos, InputEvent* event) { + with_view_model( + hid_music_macos->view, + HidMusicMacosModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_keyboard_press( + hid_music_macos->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_UP_ARROW); + hid_hal_keyboard_release( + hid_music_macos->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_UP_ARROW); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_keyboard_press( + hid_music_macos->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_DOWN_ARROW); + hid_hal_keyboard_release( + hid_music_macos->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_DOWN_ARROW); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_consumer_key_press(hid_music_macos->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_music_macos->hid, HID_CONSUMER_SCAN_NEXT_TRACK); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_consumer_key_press(hid_music_macos->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + }, + true); +} + +static void hid_music_macos_process_release(HidMusicMacos* hid_music_macos, InputEvent* event) { + with_view_model( + hid_music_macos->view, + HidMusicMacosModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_consumer_key_release( + hid_music_macos->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_music_macos->hid, HID_CONSUMER_SCAN_NEXT_TRACK); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_consumer_key_release(hid_music_macos->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + }, + true); +} + +static bool hid_music_macos_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMusicMacos* hid_music_macos = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_music_macos->hid); + } else { + consumed = true; + if(event->type == InputTypePress) { + hid_music_macos_process_press(hid_music_macos, event); + } else if(event->type == InputTypeRelease) { + hid_music_macos_process_release(hid_music_macos, event); + } + } + return consumed; +} + +HidMusicMacos* hid_music_macos_alloc(Hid* hid) { + HidMusicMacos* hid_music_macos = malloc(sizeof(HidMusicMacos)); + hid_music_macos->view = view_alloc(); + hid_music_macos->hid = hid; + view_set_context(hid_music_macos->view, hid_music_macos); + view_allocate_model(hid_music_macos->view, ViewModelTypeLocking, sizeof(HidMusicMacosModel)); + view_set_draw_callback(hid_music_macos->view, hid_music_macos_draw_callback); + view_set_input_callback(hid_music_macos->view, hid_music_macos_input_callback); + + with_view_model( + hid_music_macos->view, + HidMusicMacosModel * model, + { model->transport = hid->transport; }, + true); + + return hid_music_macos; +} + +void hid_music_macos_free(HidMusicMacos* hid_music_macos) { + furi_assert(hid_music_macos); + view_free(hid_music_macos->view); + free(hid_music_macos); +} + +View* hid_music_macos_get_view(HidMusicMacos* hid_music_macos) { + furi_assert(hid_music_macos); + return hid_music_macos->view; +} + +void hid_music_macos_set_connected_status(HidMusicMacos* hid_music_macos, bool connected) { + furi_assert(hid_music_macos); + with_view_model( + hid_music_macos->view, HidMusicMacosModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_music_macos.h b/applications/system/hid_app/views/hid_music_macos.h new file mode 100644 index 000000000..9deac32eb --- /dev/null +++ b/applications/system/hid_app/views/hid_music_macos.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct HidMusicMacos HidMusicMacos; + +HidMusicMacos* hid_music_macos_alloc(); + +void hid_music_macos_free(HidMusicMacos* hid_music_macos); + +View* hid_music_macos_get_view(HidMusicMacos* hid_music_macos); + +void hid_music_macos_set_connected_status(HidMusicMacos* hid_music_macos, bool connected);