From 40479e1761158c2186fc0c369cdbf27b401b006e Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 7 Feb 2022 16:34:09 +0300 Subject: [PATCH 1/9] u2f: check files before register/login (#980) --- applications/u2f/scenes/u2f_scene_main.c | 7 ++++++- applications/u2f/u2f.c | 14 ++++++++++++++ applications/u2f/u2f.h | 1 + applications/u2f/u2f_app.c | 2 +- applications/u2f/u2f_app_i.h | 1 + applications/u2f/u2f_data.c | 16 ++++++++++++---- applications/u2f/u2f_data.h | 2 +- 7 files changed, 36 insertions(+), 7 deletions(-) diff --git a/applications/u2f/scenes/u2f_scene_main.c b/applications/u2f/scenes/u2f_scene_main.c index 4ff5e6013c1..ede8e305fec 100644 --- a/applications/u2f/scenes/u2f_scene_main.c +++ b/applications/u2f/scenes/u2f_scene_main.c @@ -28,6 +28,8 @@ static void u2f_scene_main_event_callback(U2fNotifyEvent evt, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventConnect); else if(evt == U2fNotifyDisconnect) view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventDisconnect); + else if(evt == U2fNotifyError) + view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventDataError); } static void u2f_scene_main_timer_callback(void* context) { @@ -75,10 +77,13 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { if(app->event_cur != U2fCustomEventNone) { u2f_confirm_user_present(app->u2f_instance); } + } else if(event.event == U2fCustomEventDataError) { + osTimerStop(app->timer); + u2f_view_set_state(app->u2f_view, U2fMsgError); } consumed = true; - } else if(event.type == SceneManagerEventTypeTick) { } + return consumed; } diff --git a/applications/u2f/u2f.c b/applications/u2f/u2f.c index 4f952e68022..2a1286ef4da 100644 --- a/applications/u2f/u2f.c +++ b/applications/u2f/u2f.c @@ -186,6 +186,13 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) { uint8_t hash[32]; uint8_t signature[64]; + if(u2f_data_check(false) == false) { + U2F->ready = false; + if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context); + memcpy(&buf[0], state_not_supported, 2); + return 2; + } + if(U2F->callback != NULL) U2F->callback(U2fNotifyRegister, U2F->context); if(U2F->user_present == false) { memcpy(&buf[0], state_user_missing, 2); @@ -250,6 +257,13 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) { uint8_t hash[32]; uint8_t signature[64]; + if(u2f_data_check(false) == false) { + U2F->ready = false; + if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context); + memcpy(&buf[0], state_not_supported, 2); + return 2; + } + if(U2F->callback != NULL) U2F->callback(U2fNotifyAuth, U2F->context); if(U2F->user_present == true) { flags |= 1; diff --git a/applications/u2f/u2f.h b/applications/u2f/u2f.h index 4e10a3ea787..dcd7c3ff2df 100644 --- a/applications/u2f/u2f.h +++ b/applications/u2f/u2f.h @@ -13,6 +13,7 @@ typedef enum { U2fNotifyWink, U2fNotifyConnect, U2fNotifyDisconnect, + U2fNotifyError, } U2fNotifyEvent; typedef struct U2fData U2fData; diff --git a/applications/u2f/u2f_app.c b/applications/u2f/u2f_app.c index f2e5573044b..9dde0f0db37 100644 --- a/applications/u2f/u2f_app.c +++ b/applications/u2f/u2f_app.c @@ -48,7 +48,7 @@ U2fApp* u2f_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view)); - if(u2f_data_check()) { + if(u2f_data_check(true)) { scene_manager_next_scene(app->scene_manager, U2fSceneMain); } else { scene_manager_next_scene(app->scene_manager, U2fSceneError); diff --git a/applications/u2f/u2f_app_i.h b/applications/u2f/u2f_app_i.h index 33092ba3c92..41048b31788 100644 --- a/applications/u2f/u2f_app_i.h +++ b/applications/u2f/u2f_app_i.h @@ -20,6 +20,7 @@ typedef enum { U2fCustomEventConnect, U2fCustomEventDisconnect, + U2fCustomEventDataError, U2fCustomEventRegister, U2fCustomEventAuth, diff --git a/applications/u2f/u2f_data.c b/applications/u2f/u2f_data.c index f517d22264e..e39eb95ce13 100644 --- a/applications/u2f/u2f_data.c +++ b/applications/u2f/u2f_data.c @@ -38,17 +38,25 @@ typedef struct { uint32_t control; } __attribute__((packed)) U2fCounterData; -bool u2f_data_check() { +bool u2f_data_check(bool cert_only) { bool state = false; Storage* fs_api = furi_record_open("storage"); File* file = storage_file_alloc(fs_api); - if(storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) { + do { + if(!storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break; storage_file_close(file); - if(storage_file_open(file, U2F_CERT_KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) { + if(!storage_file_open(file, U2F_CERT_KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break; + if(cert_only) { state = true; + break; } - } + storage_file_close(file); + if(!storage_file_open(file, U2F_KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break; + storage_file_close(file); + if(!storage_file_open(file, U2F_CNT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) break; + state = true; + } while(0); storage_file_close(file); storage_file_free(file); diff --git a/applications/u2f/u2f_data.h b/applications/u2f/u2f_data.h index a96704eb220..8d3923464a3 100644 --- a/applications/u2f/u2f_data.h +++ b/applications/u2f/u2f_data.h @@ -6,7 +6,7 @@ extern "C" { #include -bool u2f_data_check(); +bool u2f_data_check(bool cert_only); bool u2f_data_cert_check(); From 3f6deecdae3f4265d4f7680a5c9836f349c0527c Mon Sep 17 00:00:00 2001 From: gornekich Date: Mon, 7 Feb 2022 16:37:56 +0300 Subject: [PATCH 2/9] [FL-2243] Restart BT advertising after forgetting devices #982 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/bt/bt_service/bt_keys_storage.c | 3 ++- firmware/targets/f6/furi_hal/furi_hal_bt.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 2 +- firmware/targets/furi_hal_include/furi_hal_bt.h | 6 ++++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/applications/bt/bt_service/bt_keys_storage.c b/applications/bt/bt_service/bt_keys_storage.c index c712b65b82f..74df10209a3 100644 --- a/applications/bt/bt_service/bt_keys_storage.c +++ b/applications/bt/bt_service/bt_keys_storage.c @@ -43,10 +43,11 @@ bool bt_save_key_storage(Bt* bt) { bool bt_delete_key_storage(Bt* bt) { furi_assert(bt); bool delete_succeed = false; + bool bt_is_active = furi_hal_bt_is_active(); furi_hal_bt_stop_advertising(); delete_succeed = furi_hal_bt_clear_white_list(); - if(bt->bt_settings.enabled) { + if(bt_is_active) { furi_hal_bt_start_advertising(); } diff --git a/firmware/targets/f6/furi_hal/furi_hal_bt.c b/firmware/targets/f6/furi_hal/furi_hal_bt.c index 4465f15e5a0..9d0c36fc581 100755 --- a/firmware/targets/f6/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f6/furi_hal/furi_hal_bt.c @@ -246,7 +246,7 @@ bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, return ret; } -static bool furi_hal_bt_is_active() { +bool furi_hal_bt_is_active() { return gap_get_state() > GapStateIdle; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 4465f15e5a0..9d0c36fc581 100755 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -246,7 +246,7 @@ bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, return ret; } -static bool furi_hal_bt_is_active() { +bool furi_hal_bt_is_active() { return gap_get_state() > GapStateIdle; } diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index e10901b08de..912132d23fa 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -84,6 +84,12 @@ bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, */ void furi_hal_bt_update_battery_level(uint8_t battery_level); +/** Checks if BLE state is active + * + * @return true if device is connected or advertising, false otherwise + */ +bool furi_hal_bt_is_active(); + /** Start advertising */ void furi_hal_bt_start_advertising(); From b71f3c632355f466ebeb847559593a20383f7ef7 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue, 8 Feb 2022 22:41:10 +0400 Subject: [PATCH 3/9] [FL-2168] SubGhz: fix auto highlight name when saving RAW (#984) --- applications/subghz/helpers/subghz_custom_event.h | 1 + applications/subghz/scenes/subghz_scene_read_raw.c | 2 +- applications/subghz/scenes/subghz_scene_save_name.c | 12 ++++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/applications/subghz/helpers/subghz_custom_event.h b/applications/subghz/helpers/subghz_custom_event.h index 55363c41ba3..5fee3541e86 100644 --- a/applications/subghz/helpers/subghz_custom_event.h +++ b/applications/subghz/helpers/subghz_custom_event.h @@ -3,6 +3,7 @@ typedef enum { SubghzCustomEventManagerNoSet = 0, SubghzCustomEventManagerSet, + SubghzCustomEventManagerSetRAW, SubghzCustomEventSceneDeleteSuccess = 100, SubghzCustomEventSceneDelete, diff --git a/applications/subghz/scenes/subghz_scene_read_raw.c b/applications/subghz/scenes/subghz_scene_read_raw.c index 170d3c7d06b..b86590573ba 100644 --- a/applications/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/subghz/scenes/subghz_scene_read_raw.c @@ -261,7 +261,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { case SubghzCustomEventViewReadRAWSave: if(subghz_scene_read_raw_update_filename(subghz)) { scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet); + subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSetRAW); subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); } diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c index ee1df00f598..27db3ca6f93 100644 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ b/applications/subghz/scenes/subghz_scene_save_name.c @@ -23,9 +23,13 @@ void subghz_scene_save_name_on_enter(void* context) { dev_name_empty = true; } else { strcpy(subghz->file_name_tmp, subghz->file_name); - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == - SubghzCustomEventManagerSet) { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubghzCustomEventManagerNoSet) { subghz_get_next_name_file(subghz); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == + SubghzCustomEventManagerSetRAW) { + dev_name_empty = true; + } } } @@ -62,8 +66,8 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { subghz_save_protocol_to_file(subghz, subghz->file_name); } - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == - SubghzCustomEventManagerSet) { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubghzCustomEventManagerNoSet) { subghz_protocol_raw_set_last_file_name( (SubGhzProtocolRAW*)subghz->txrx->protocol_result, subghz->file_name); scene_manager_set_scene_state( From 2876499c4fae5e85067c3d7c53cbbab145c22828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 9 Feb 2022 14:57:44 +0300 Subject: [PATCH 4/9] [FL-2249] Bluetooth: update status bar icon #985 --- applications/bt/bt_service/bt.c | 8 ++++---- assets/compiled/assets_icons.c | 14 +++++++------- assets/compiled/assets_icons.h | 4 ++-- assets/icons/StatusBar/BT_Pair_9x8.png | Bin 2072 -> 0 bytes .../icons/StatusBar/Bluetooth_Connected_16x8.png | Bin 0 -> 3613 bytes ...{Bluetooth_5x8.png => Bluetooth_Idle_5x8.png} | Bin 6 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 assets/icons/StatusBar/BT_Pair_9x8.png create mode 100644 assets/icons/StatusBar/Bluetooth_Connected_16x8.png rename assets/icons/StatusBar/{Bluetooth_5x8.png => Bluetooth_Idle_5x8.png} (100%) diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index 32008f52306..75392caf3d8 100755 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -16,9 +16,9 @@ static void bt_draw_statusbar_callback(Canvas* canvas, void* context) { Bt* bt = context; if(bt->status == BtStatusAdvertising) { - canvas_draw_icon(canvas, 0, 0, &I_Bluetooth_5x8); + canvas_draw_icon(canvas, 0, 0, &I_Bluetooth_Idle_5x8); } else if(bt->status == BtStatusConnected) { - canvas_draw_icon(canvas, 0, 0, &I_BT_Pair_9x8); + canvas_draw_icon(canvas, 0, 0, &I_Bluetooth_Connected_16x8); } } @@ -257,10 +257,10 @@ static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void static void bt_statusbar_update(Bt* bt) { if(bt->status == BtStatusAdvertising) { - view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_5x8)); + view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_Idle_5x8)); view_port_enabled_set(bt->statusbar_view_port, true); } else if(bt->status == BtStatusConnected) { - view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_BT_Pair_9x8)); + view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_Connected_16x8)); view_port_enabled_set(bt->statusbar_view_port, true); } else { view_port_enabled_set(bt->statusbar_view_port, false); diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index 409f7034cd9..1d395b96e4b 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -525,9 +525,6 @@ const uint8_t *_I_Cry_dolph_55x52[] = {_I_Cry_dolph_55x52_0}; const uint8_t _I_Attention_5x8_0[] = {0x00,0x0E,0x0A,0x0A,0x0A,0x0E,0x04,0x00,0x0E,}; const uint8_t *_I_Attention_5x8[] = {_I_Attention_5x8_0}; -const uint8_t _I_BT_Pair_9x8_0[] = {0x00,0x11,0x01,0x35,0x00,0x58,0x01,0x31,0x00,0x30,0x01,0x59,0x00,0x34,0x01,0x11,0x01,}; -const uint8_t *_I_BT_Pair_9x8[] = {_I_BT_Pair_9x8_0}; - const uint8_t _I_Background_128x11_0[] = {0x01,0x00,0x70,0x00,0xff,0x40,0x40,0xc9,0xe0,0xff,0x80,0x06,0x1e,0x08,0x38,0x0c,0x0c,0x1e,0x93,0x00,0x19,0x46,0x01,0x07,0x7d,0x83,0x03,0xd2,0x31,0xff,0xdb,0xd5,0x66,0x20,0x83,0xc0,0xff,0x05,0x24,0x00,0x1c,0x78,0x28,0xbc,0x40,0x72,0xbf,0xcf,0x47,0xeb,0x40,0xdb,0x7a,0xbf,0xf0,0x40,0x39,0x60,0x28,0x3f,0xe0,0xa0,0xea,0x80,0x63,0x3f,0x0b,0x17,0xe4,0x3e,0x5a,0xbc,0xf9,0x99,0x70,0x1f,0x81,0x50,0xc0,0x80,0xe7,0x3e,0x1e,0x9d,0x57,0xfb,0x7f,0x23,0x15,0xb0,0x12,0x5b,0x5b,0x02,0x1d,0x8c,0xc3,0x80,0x24,0x9e,0x03,0x80,0x5e,0x40,0x00,0xa1,0x88,0x0e,0x98,0x00,0x7b,0x07,0x08,0xb2,0x44,0x41,}; const uint8_t *_I_Background_128x11[] = {_I_Background_128x11_0}; @@ -540,8 +537,11 @@ const uint8_t *_I_Battery_19x8[] = {_I_Battery_19x8_0}; const uint8_t _I_Battery_26x8_0[] = {0x01,0x00,0x13,0x00,0xff,0x7f,0xef,0xf0,0x08,0x0c,0x03,0x00,0x03,0x38,0x18,0x0c,0xa0,0x40,0x36,0x05,0x98,0x6d,0x00,}; const uint8_t *_I_Battery_26x8[] = {_I_Battery_26x8_0}; -const uint8_t _I_Bluetooth_5x8_0[] = {0x00,0x04,0x0D,0x16,0x0C,0x0C,0x16,0x0D,0x04,}; -const uint8_t *_I_Bluetooth_5x8[] = {_I_Bluetooth_5x8_0}; +const uint8_t _I_Bluetooth_Connected_16x8_0[] = {0x00,0x04,0x00,0x0D,0x00,0x16,0x60,0x4C,0x97,0x4C,0x97,0x16,0x60,0x0D,0x00,0x04,0x00,}; +const uint8_t *_I_Bluetooth_Connected_16x8[] = {_I_Bluetooth_Connected_16x8_0}; + +const uint8_t _I_Bluetooth_Idle_5x8_0[] = {0x00,0x04,0x0D,0x16,0x0C,0x0C,0x16,0x0D,0x04,}; +const uint8_t *_I_Bluetooth_Idle_5x8[] = {_I_Bluetooth_Idle_5x8_0}; const uint8_t _I_Charging_lightning_9x10_0[] = {0x00,0x40,0x01,0xA0,0x00,0x50,0x00,0xE8,0x01,0x84,0x00,0x42,0x00,0x2F,0x00,0x14,0x00,0x0A,0x00,0x05,0x00,}; const uint8_t *_I_Charging_lightning_9x10[] = {_I_Charging_lightning_9x10_0}; @@ -753,12 +753,12 @@ const Icon I_SDError_43x35 = {.width=43,.height=35,.frame_count=1,.frame_rate=0, const Icon I_SDQuestion_35x43 = {.width=35,.height=43,.frame_count=1,.frame_rate=0,.frames=_I_SDQuestion_35x43}; const Icon I_Cry_dolph_55x52 = {.width=55,.height=52,.frame_count=1,.frame_rate=0,.frames=_I_Cry_dolph_55x52}; const Icon I_Attention_5x8 = {.width=5,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Attention_5x8}; -const Icon I_BT_Pair_9x8 = {.width=9,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_BT_Pair_9x8}; const Icon I_Background_128x11 = {.width=128,.height=11,.frame_count=1,.frame_rate=0,.frames=_I_Background_128x11}; const Icon I_BadUsb_9x8 = {.width=9,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_BadUsb_9x8}; const Icon I_Battery_19x8 = {.width=19,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Battery_19x8}; const Icon I_Battery_26x8 = {.width=26,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Battery_26x8}; -const Icon I_Bluetooth_5x8 = {.width=5,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Bluetooth_5x8}; +const Icon I_Bluetooth_Connected_16x8 = {.width=16,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Bluetooth_Connected_16x8}; +const Icon I_Bluetooth_Idle_5x8 = {.width=5,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Bluetooth_Idle_5x8}; const Icon I_Charging_lightning_9x10 = {.width=9,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_Charging_lightning_9x10}; const Icon I_Charging_lightning_mask_9x10 = {.width=9,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_Charging_lightning_mask_9x10}; const Icon I_Lock_8x8 = {.width=8,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Lock_8x8}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index 3b756c1b4fd..8fa6d6c6c76 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -136,12 +136,12 @@ extern const Icon I_SDError_43x35; extern const Icon I_SDQuestion_35x43; extern const Icon I_Cry_dolph_55x52; extern const Icon I_Attention_5x8; -extern const Icon I_BT_Pair_9x8; extern const Icon I_Background_128x11; extern const Icon I_BadUsb_9x8; extern const Icon I_Battery_19x8; extern const Icon I_Battery_26x8; -extern const Icon I_Bluetooth_5x8; +extern const Icon I_Bluetooth_Connected_16x8; +extern const Icon I_Bluetooth_Idle_5x8; extern const Icon I_Charging_lightning_9x10; extern const Icon I_Charging_lightning_mask_9x10; extern const Icon I_Lock_8x8; diff --git a/assets/icons/StatusBar/BT_Pair_9x8.png b/assets/icons/StatusBar/BT_Pair_9x8.png deleted file mode 100644 index 4382cc744eb14fdcdd6699c1cffac110b927fe04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2072 zcma)7eQXnD7{6f*-S{x#0z$+bM~DM#uXkO$_113bdM&N2ShfNyNZ{6c+rC+QSMRQC zI}mUP3I@se0|P@Aafu|8O+GZ@ki}_2SmYxSFpGo$hTuel3L|KS^S$e~4q?PU`td%$ z=lR|5`FNk(=3laCO2MoG48x{)J#01l%|{=dZUXvl>#V$vVNc(Ofm)^3SH*@H0JE8G1=)Yg`0W&yPDr3E*af*s!1srF%+08xT;4yw&zcA|tv6QFiSBMUwjLM>pN z65te33W#FNPB_$w#r-3R;52P8<3&C$1O+v<`5K`b#*z_nDBLT^3MYiXNI1-eo1Pf1HkR?jG>Fx*5DAb`okz-_!-8Y5 zY{xXrDaJ&p23>Mg6iQPw+H*$3P}LA*`VZuFI{lIuhB1W6)r%@|ughtntQL!&Hc;9P z#BHbsYE^&)8vyT?#Q5;NQ?2~?wW6PxEV{R9JRHAR)VT4ax*0}vd7z9&D5IIO>!Jp{ zo;_ucF{tUqaqU(zTu7Z?JCTq?J{bbMRuDv&MP)Ipq&NvUB4_~6Ooqb{O?oCfmN;dg zjHoOR@ui{wv5h~6S+Tf{R5#a%G#i;se~kF7!&}y3Prn^99_ofFuKlxfOfcq zmi4t4YSGIfZxcifeWZ~XR@9tqKDUrvT6n4={LWysu(zjf+H&WH3*Wl;c%9x^A5FHH zei-!M`Dx%vOL57l-v6VA8`^eHw*~w&YPMQvv)K_`*VSBlxLxR4{?`{94+#`kbnxJ_ zFD~CJ6zt4@_2rhmXIgsBESSBjuzdQxe#vpIe$~#hL0;~?_Dsuwr&QPB=EPrSoZ5K! zm0LkBW1Zbs<#=%U)CTVxM2SgX-}Rkka?e}3t6<;Kru+RN&xuyy@BZf5;Lmm4cV0TZ zxMN!9QZ}-!5LT@}Fukp@@>1K+`7>v3Zx|Z7chNKTZrR0KPx%Md2FVZnU*)yl+C6Ym z47N_Zdh+d~ug$OMp18U5KdN0ve1Ei6o+{2cLaa&V{aAK>-XHt7kONJTBb#U3Ifq`l`3X-@#;{4xsh>P-@4n}e zk@2#QfNoEG2Q>{d>Ux@nhD@th-Z(+0o9F00or3M!)ehf`57HOAR-Rqo4sY~!pY2<` etmHF`bR7#mVEZoI9oV71(7moDY)9qVrhfqn0J$*$ diff --git a/assets/icons/StatusBar/Bluetooth_Connected_16x8.png b/assets/icons/StatusBar/Bluetooth_Connected_16x8.png new file mode 100644 index 0000000000000000000000000000000000000000..c77bc1494f92b16d0a078b47acfa6df6a45890f7 GIT binary patch literal 3613 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 X0xtk%_Z)B;2L)JL*qN7_d0hG*G(b{c literal 0 HcmV?d00001 diff --git a/assets/icons/StatusBar/Bluetooth_5x8.png b/assets/icons/StatusBar/Bluetooth_Idle_5x8.png similarity index 100% rename from assets/icons/StatusBar/Bluetooth_5x8.png rename to assets/icons/StatusBar/Bluetooth_Idle_5x8.png From 6b78a8ccfe34ea479fa1b2796edf627102f1c914 Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 9 Feb 2022 15:03:27 +0300 Subject: [PATCH 5/9] [FL-2244] Companion reboot fix (#988) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * bt: don't open rpc session if it is busy * bq27220: fix incorrect checksum calculation Co-authored-by: あく --- applications/bt/bt_service/bt.c | 17 +++++++++++------ firmware/targets/f6/furi_hal/furi_hal_bt.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 2 +- lib/drivers/bq27220.c | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index 75392caf3d8..0015c08cf1e 100755 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -199,13 +199,18 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); if(bt->profile == BtProfileSerial) { // Open RPC session - FURI_LOG_I(TAG, "Open RPC connection"); bt->rpc_session = rpc_session_open(bt->rpc); - rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); - rpc_session_set_buffer_is_empty_callback( - bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty); - rpc_session_set_context(bt->rpc_session, bt); - furi_hal_bt_serial_set_event_callback(RPC_BUFFER_SIZE, bt_serial_event_callback, bt); + if(bt->rpc_session) { + FURI_LOG_I(TAG, "Open RPC connection"); + rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); + rpc_session_set_buffer_is_empty_callback( + bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty); + rpc_session_set_context(bt->rpc_session, bt); + furi_hal_bt_serial_set_event_callback( + RPC_BUFFER_SIZE, bt_serial_event_callback, bt); + } else { + FURI_LOG_W(TAG, "RPC is busy, failed to open new session"); + } } // Update battery level PowerInfo info; diff --git a/firmware/targets/f6/furi_hal/furi_hal_bt.c b/firmware/targets/f6/furi_hal/furi_hal_bt.c index 9d0c36fc581..1bd244a9718 100755 --- a/firmware/targets/f6/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f6/furi_hal/furi_hal_bt.c @@ -47,7 +47,7 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = { .conn_int_min = 0x08, .conn_int_max = 0x18, .slave_latency = 0, - .supervisor_timeout = 50, + .supervisor_timeout = 0, }, }, }, diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 9d0c36fc581..1bd244a9718 100755 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -47,7 +47,7 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = { .conn_int_min = 0x08, .conn_int_max = 0x18, .slave_latency = 0, - .supervisor_timeout = 50, + .supervisor_timeout = 0, }, }, }, diff --git a/lib/drivers/bq27220.c b/lib/drivers/bq27220.c index bcdf45356fb..a0401af7ce2 100644 --- a/lib/drivers/bq27220.c +++ b/lib/drivers/bq27220.c @@ -44,7 +44,7 @@ bool bq27220_set_parameter_u16(FuriHalI2cBusHandle* handle, uint16_t address, ui delay_us(10000); - uint8_t checksum = bq27220_get_checksum(&buffer[1], 4); + uint8_t checksum = bq27220_get_checksum(buffer, 4); buffer[0] = checksum; buffer[1] = 6; ret = furi_hal_i2c_write_mem( From df2d1ad13f505332661b0bcc7605744a6a55b7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 10 Feb 2022 14:20:50 +0300 Subject: [PATCH 6/9] [FL-2219, FL-2251] System, FuriCore, FuriHal: various bug fixes and improvements (#986) * Replace irq shenanigans with critical section * Power: halt system on power off instead of crash. * Gui: properly handle input event on NULL current_view * FuriHal: correct gpio configuration sequence * FuriHal: cleanup uart initialization. Makefile: allow to disable thread support. * Loader: improve locking, fix simultaneous app start crash, full command line args support for gui apps, more consistent insomnia * Loader: correct spelling * FuriHal: increase gpio configuration readability * FuriHal: correct gpio configuration error when mode is GpioModeEventRiseFall Co-authored-by: DrZlo13 --- applications/accessor/helpers/wiegand.cpp | 6 +- .../accessor/scene/accessor_scene_start.cpp | 6 +- .../archive/scenes/archive_scene_browser.c | 9 +- .../desktop/scenes/desktop_scene_main.c | 8 +- applications/gui/view_dispatcher.c | 39 +- applications/ibutton/helpers/key_reader.cpp | 24 +- applications/ibutton/helpers/key_writer.cpp | 26 +- applications/lfrfid/helpers/rfid_writer.cpp | 12 +- applications/loader/loader.c | 178 ++--- applications/loader/loader_i.h | 11 +- applications/power/power_cli.c | 83 ++- applications/power/power_service/power_api.c | 2 +- .../targets/f6/furi_hal/furi_hal_gpio.c | 78 ++- .../targets/f7/furi_hal/furi_hal_gpio.c | 79 ++- core/furi/check.c | 23 +- core/furi/check.h | 3 + .../targets/f6/cube/startup_stm32wb55xx_cm4.s | 608 +++++++++--------- firmware/targets/f6/furi_hal/furi_hal_gpio.c | 99 +-- firmware/targets/f6/furi_hal/furi_hal_irda.c | 16 +- firmware/targets/f6/furi_hal/furi_hal_os.c | 6 +- firmware/targets/f6/furi_hal/furi_hal_uart.c | 8 +- firmware/targets/f6/target.mk | 6 + .../targets/f7/cube/startup_stm32wb55xx_cm4.s | 608 +++++++++--------- firmware/targets/f7/furi_hal/furi_hal_gpio.c | 99 +-- firmware/targets/f7/furi_hal/furi_hal_irda.c | 16 +- firmware/targets/f7/furi_hal/furi_hal_os.c | 6 +- firmware/targets/f7/furi_hal/furi_hal_uart.c | 8 +- firmware/targets/f7/target.mk | 6 + lib/FreeRTOS-glue/cmsis_os2.c | 9 +- lib/cyfral/cyfral_emulator.h | 96 --- lib/cyfral/cyfral_reader.h | 272 -------- lib/cyfral/cyfral_reader_comp.h | 283 -------- lib/onewire/blanks_writer.cpp | 320 --------- lib/onewire/blanks_writer.h | 40 -- lib/onewire/one_wire_slave.cpp | 6 +- 35 files changed, 1141 insertions(+), 1958 deletions(-) delete mode 100644 lib/cyfral/cyfral_emulator.h delete mode 100644 lib/cyfral/cyfral_reader.h delete mode 100644 lib/cyfral/cyfral_reader_comp.h delete mode 100644 lib/onewire/blanks_writer.cpp delete mode 100644 lib/onewire/blanks_writer.h diff --git a/applications/accessor/helpers/wiegand.cpp b/applications/accessor/helpers/wiegand.cpp index 0ab43f2df30..3b2fcf96a86 100644 --- a/applications/accessor/helpers/wiegand.cpp +++ b/applications/accessor/helpers/wiegand.cpp @@ -31,9 +31,9 @@ int WIEGAND::getWiegandType() { bool WIEGAND::available() { bool ret; - __disable_irq(); + FURI_CRITICAL_ENTER(); ret = DoWiegandConversion(); - __enable_irq(); + FURI_CRITICAL_EXIT(); return ret; } @@ -221,4 +221,4 @@ bool WIEGAND::DoWiegandConversion() { } } else return false; -} \ No newline at end of file +} diff --git a/applications/accessor/scene/accessor_scene_start.cpp b/applications/accessor/scene/accessor_scene_start.cpp index 5f49666fbaa..a82fc275b17 100644 --- a/applications/accessor/scene/accessor_scene_start.cpp +++ b/applications/accessor/scene/accessor_scene_start.cpp @@ -37,7 +37,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8); } } else { - __disable_irq(); + FURI_CRITICAL_ENTER(); if(onewire->reset()) { type = 255; onewire->write(0x33); @@ -49,7 +49,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { data[i] = data[i + 1]; } } - __enable_irq(); + FURI_CRITICAL_EXIT(); } if(type > 0) { @@ -85,4 +85,4 @@ void AccessorSceneStart::on_exit(AccessorApp* app) { Popup* popup = app->get_view_manager()->get_popup(); popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); -} \ No newline at end of file +} diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/archive/scenes/archive_scene_browser.c index d33aa23e687..88a058bbc67 100644 --- a/applications/archive/scenes/archive_scene_browser.c +++ b/applications/archive/scenes/archive_scene_browser.c @@ -4,6 +4,8 @@ #include "../helpers/archive_browser.h" #include "../views/archive_browser_view.h" +#define TAG "ArchiveSceneBrowser" + static const char* flipper_app_name[] = { [ArchiveFileTypeIButton] = "iButton", [ArchiveFileTypeNFC] = "NFC", @@ -25,7 +27,12 @@ static void archive_run_in_app( } else { string_init_set(full_path, selected->name); } - loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); + + LoaderStatus status = + loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); + if(status != LoaderStatusOk) { + FURI_LOG_E(TAG, "loader_start failed: %d", status); + } string_clear(full_path); furi_record_close("loader"); diff --git a/applications/desktop/scenes/desktop_scene_main.c b/applications/desktop/scenes/desktop_scene_main.c index 7faa50fe887..0db7508dcab 100644 --- a/applications/desktop/scenes/desktop_scene_main.c +++ b/applications/desktop/scenes/desktop_scene_main.c @@ -9,6 +9,8 @@ #include "desktop_scene.h" #include "desktop_scene_i.h" +#define TAG "DesktopSrv" + #define MAIN_VIEW_DEFAULT (0UL) static void desktop_scene_main_app_started_callback(const void* message, void* context) { @@ -142,10 +144,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { Loader* loader = furi_record_open("loader"); LoaderStatus status = loader_start(loader, FLIPPER_APPS[desktop->settings.favorite].name, NULL); - furi_check(status == LoaderStatusOk); + if(status != LoaderStatusOk) { + FURI_LOG_E(TAG, "loader_start failed: %d", status); + } furi_record_close("loader"); } else { - FURI_LOG_E("DesktopSrv", "Can't find favorite application"); + FURI_LOG_E(TAG, "Can't find favorite application"); } consumed = true; break; diff --git a/applications/gui/view_dispatcher.c b/applications/gui/view_dispatcher.c index 549ee622f70..a32dde97169 100644 --- a/applications/gui/view_dispatcher.c +++ b/applications/gui/view_dispatcher.c @@ -253,27 +253,26 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } // Deliver event - if(view_dispatcher->ongoing_input_view == view_dispatcher->current_view) { - bool is_consumed = false; - if(view_dispatcher->current_view) { - is_consumed = view_input(view_dispatcher->current_view, event); - } - if(!is_consumed && (event->type == InputTypeShort || event->type == InputTypeLong)) { - // TODO remove view navigation handlers - uint32_t view_id = VIEW_IGNORE; - if(event->key == InputKeyBack) { - view_id = view_previous(view_dispatcher->current_view); - if((view_id == VIEW_IGNORE) && (view_dispatcher->navigation_event_callback)) { - is_consumed = - view_dispatcher->navigation_event_callback(view_dispatcher->event_context); - if(!is_consumed) { - view_dispatcher_stop(view_dispatcher); - return; - } - } - } - if(!is_consumed) { + if(view_dispatcher->current_view && + view_dispatcher->ongoing_input_view == view_dispatcher->current_view) { + // Dispatch input to current view + bool is_consumed = view_input(view_dispatcher->current_view, event); + + // Navigate if input is not consumed + if(!is_consumed && (event->key == InputKeyBack) && + (event->type == InputTypeShort || event->type == InputTypeLong)) { + // Navigate to previous + uint32_t view_id = view_previous(view_dispatcher->current_view); + if(view_id != VIEW_IGNORE) { + // Switch to returned view view_dispatcher_switch_to_view(view_dispatcher, view_id); + } else if(view_dispatcher->navigation_event_callback) { + // Dispatch navigation event + if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) { + // TODO: should we allow view_dispatcher to stop without navigation_event_callback? + view_dispatcher_stop(view_dispatcher); + return; + } } } } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) { diff --git a/applications/ibutton/helpers/key_reader.cpp b/applications/ibutton/helpers/key_reader.cpp index eea4abd6b8e..9ed5b340f3d 100644 --- a/applications/ibutton/helpers/key_reader.cpp +++ b/applications/ibutton/helpers/key_reader.cpp @@ -59,9 +59,8 @@ KeyReader::~KeyReader() { bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) { bool readed = false; - switch(read_mode) { - case ReadMode::DALLAS: - __disable_irq(); + if(read_mode == ReadMode::DALLAS) { + FURI_CRITICAL_ENTER(); if(onewire_master->search(data)) { onewire_master->reset_search(); readed = true; @@ -69,9 +68,8 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s } else { onewire_master->reset_search(); } - __enable_irq(); - break; - case ReadMode::CYFRAL_METAKOM: + FURI_CRITICAL_EXIT(); + } else if(read_mode == ReadMode::CYFRAL_METAKOM) { if(cyfral_decoder.read(data, 2)) { readed = true; *key_type = iButtonKeyType::KeyCyfral; @@ -79,7 +77,6 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s readed = true; *key_type = iButtonKeyType::KeyMetakom; } - break; } return readed; @@ -88,10 +85,10 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size) { bool result = true; - switch(key_type) { - case iButtonKeyType::KeyDallas: + if(key_type == iButtonKeyType::KeyDallas) { switch_to(ReadMode::DALLAS); - __disable_irq(); + + FURI_CRITICAL_ENTER(); if(onewire_master->reset()) { onewire_master->write(DS1990::CMD_READ_ROM); for(uint8_t i = 0; i < data_size; i++) { @@ -101,14 +98,11 @@ bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, u } } else { result = false; - break; } - __enable_irq(); - break; + FURI_CRITICAL_EXIT(); - default: + } else { result = false; - break; } return result; diff --git a/applications/ibutton/helpers/key_writer.cpp b/applications/ibutton/helpers/key_writer.cpp index 689517bbdc8..39637b3a704 100644 --- a/applications/ibutton/helpers/key_writer.cpp +++ b/applications/ibutton/helpers/key_writer.cpp @@ -74,7 +74,7 @@ bool KeyWriter::compare_key_ds1990(iButtonKey* key) { bool result = false; if(key->get_key_type() == iButtonKeyType::KeyDallas) { - __disable_irq(); + FURI_CRITICAL_ENTER(); bool presence = onewire_master->reset(); if(presence) { @@ -89,7 +89,7 @@ bool KeyWriter::compare_key_ds1990(iButtonKey* key) { } } - __enable_irq(); + FURI_CRITICAL_EXIT(); } return result; @@ -99,7 +99,7 @@ bool KeyWriter::write_1990_1(iButtonKey* key) { bool result = false; if(key->get_key_type() == iButtonKeyType::KeyDallas) { - __disable_irq(); + FURI_CRITICAL_ENTER(); // unlock onewire_master->reset(); @@ -120,7 +120,7 @@ bool KeyWriter::write_1990_1(iButtonKey* key) { onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); onewire_write_one_bit(1); - __enable_irq(); + FURI_CRITICAL_EXIT(); if(compare_key_ds1990(key)) { result = true; @@ -134,7 +134,7 @@ bool KeyWriter::write_1990_2(iButtonKey* key) { bool result = false; if(key->get_key_type() == iButtonKeyType::KeyDallas) { - __disable_irq(); + FURI_CRITICAL_ENTER(); // unlock onewire_master->reset(); @@ -154,7 +154,7 @@ bool KeyWriter::write_1990_2(iButtonKey* key) { onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); onewire_write_one_bit(0); - __enable_irq(); + FURI_CRITICAL_EXIT(); if(compare_key_ds1990(key)) { result = true; @@ -169,7 +169,7 @@ bool KeyWriter::write_TM2004(iButtonKey* key) { bool result = true; if(key->get_key_type() == iButtonKeyType::KeyDallas) { - __disable_irq(); + FURI_CRITICAL_ENTER(); // write rom, addr is 0x0000 onewire_master->reset(); @@ -204,7 +204,7 @@ bool KeyWriter::write_TM2004(iButtonKey* key) { onewire_master->reset(); - __enable_irq(); + FURI_CRITICAL_EXIT(); } else { result = false; } @@ -216,7 +216,7 @@ bool KeyWriter::write_TM01(iButtonKey* key) { /*bool result = true; // TODO test and encoding - __disable_irq(); + FURI_CRITICAL_ENTER(); // unlock onewire_master->reset(); @@ -240,13 +240,13 @@ bool KeyWriter::write_TM01(iButtonKey* key) { onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); onewire_write_one_bit(0, 10000); - __enable_irq(); + FURI_CRITICAL_EXIT(); if(!compare_key_ds1990(key)) { result = false; } - __disable_irq(); + FURI_CRITICAL_ENTER(); if(key->get_key_type() == iButtonKeyType::KeyMetakom || key->get_key_type() == iButtonKeyType::KeyCyfral) { @@ -258,7 +258,7 @@ bool KeyWriter::write_TM01(iButtonKey* key) { onewire_write_one_bit(1); } - __enable_irq(); + FURI_CRITICAL_EXIT(); return result;*/ return false; @@ -275,4 +275,4 @@ void KeyWriter::write_byte_ds1990(uint8_t data) { delay_us(5000); data = data >> 1; } -} \ No newline at end of file +} diff --git a/applications/lfrfid/helpers/rfid_writer.cpp b/applications/lfrfid/helpers/rfid_writer.cpp index 90fc16517d7..b3b221ee108 100644 --- a/applications/lfrfid/helpers/rfid_writer.cpp +++ b/applications/lfrfid/helpers/rfid_writer.cpp @@ -121,12 +121,12 @@ void RfidWriter::write_em(const uint8_t em_data[5]) { em_card.encode(em_data, 5, reinterpret_cast(&em_encoded_data), sizeof(uint64_t)); const uint32_t em_config_block_data = 0b00000000000101001000000001000000; - __disable_irq(); + FURI_CRITICAL_ENTER(); write_block(0, 0, false, em_config_block_data); write_block(0, 1, false, em_encoded_data); write_block(0, 2, false, em_encoded_data >> 32); write_reset(); - __enable_irq(); + FURI_CRITICAL_EXIT(); } void RfidWriter::write_hid(const uint8_t hid_data[3]) { @@ -136,13 +136,13 @@ void RfidWriter::write_hid(const uint8_t hid_data[3]) { const uint32_t hid_config_block_data = 0b00000000000100000111000001100000; - __disable_irq(); + FURI_CRITICAL_ENTER(); write_block(0, 0, false, hid_config_block_data); write_block(0, 1, false, card_data[0]); write_block(0, 2, false, card_data[1]); write_block(0, 3, false, card_data[2]); write_reset(); - __enable_irq(); + FURI_CRITICAL_EXIT(); } void RfidWriter::write_indala(const uint8_t indala_data[3]) { @@ -153,10 +153,10 @@ void RfidWriter::write_indala(const uint8_t indala_data[3]) { const uint32_t indala_config_block_data = 0b00000000000010000001000001000000; - __disable_irq(); + FURI_CRITICAL_ENTER(); write_block(0, 0, false, indala_config_block_data); write_block(0, 1, false, card_data[0]); write_block(0, 2, false, card_data[1]); write_reset(); - __enable_irq(); + FURI_CRITICAL_EXIT(); } diff --git a/applications/loader/loader.c b/applications/loader/loader.c index d131de49833..95beab452b7 100644 --- a/applications/loader/loader.c +++ b/applications/loader/loader.c @@ -9,28 +9,46 @@ static Loader* loader_instance = NULL; -static void loader_menu_callback(void* _ctx, uint32_t index) { - const FlipperApplication* flipper_app = _ctx; +static bool + loader_start_application(const FlipperApplication* application, const char* arguments) { + loader_instance->application = application; - furi_assert(flipper_app->app); - furi_assert(flipper_app->name); + furi_assert(loader_instance->application_arguments == NULL); + if(arguments && strlen(arguments) > 0) { + loader_instance->application_arguments = strdup(arguments); + } - if(!loader_lock(loader_instance)) return; + FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); - if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) { - FURI_LOG_E(TAG, "Can't start app. %s is running", loader_instance->current_app->name); - return; + furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name); + furi_thread_set_stack_size( + loader_instance->application_thread, loader_instance->application->stack_size); + furi_thread_set_context( + loader_instance->application_thread, loader_instance->application_arguments); + furi_thread_set_callback( + loader_instance->application_thread, loader_instance->application->app); + + bool result = furi_thread_start(loader_instance->application_thread); + + if(!result) { + loader_instance->application = NULL; } - furi_hal_power_insomnia_enter(); - loader_instance->current_app = flipper_app; + return result; +} + +static void loader_menu_callback(void* _ctx, uint32_t index) { + const FlipperApplication* application = _ctx; + + furi_assert(application->app); + furi_assert(application->name); - FURI_LOG_I(TAG, "Starting: %s", loader_instance->current_app->name); - furi_thread_set_name(loader_instance->thread, flipper_app->name); - furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size); - furi_thread_set_context(loader_instance->thread, NULL); - furi_thread_set_callback(loader_instance->thread, flipper_app->app); - furi_thread_start(loader_instance->thread); + if(!loader_lock(loader_instance)) { + FURI_LOG_E(TAG, "Loader is locked"); + return; + } + + loader_start_application(application, NULL); } static void loader_submenu_callback(void* context, uint32_t index) { @@ -73,32 +91,36 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { } void loader_cli_open(Cli* cli, string_t args, Loader* instance) { - string_strim(args); + string_t application_name; + string_init(application_name); - if(string_size(args) == 0) { - printf("No application provided\r\n"); - return; - } + do { + if(!args_read_probably_quoted_string_and_trim(args, application_name)) { + printf("No application provided\r\n"); + break; + } - const FlipperApplication* application = loader_find_application_by_name(string_get_cstr(args)); - if(!application) { - printf("%s doesn't exists\r\n", string_get_cstr(args)); - return; - } + const FlipperApplication* application = + loader_find_application_by_name(string_get_cstr(application_name)); + if(!application) { + printf("%s doesn't exists\r\n", string_get_cstr(application_name)); + break; + } - if(furi_thread_get_state(instance->thread) != FuriThreadStateStopped) { - printf("Can't start, furi application is running"); - return; - } + string_strim(args); + if(!loader_start_application(application, string_get_cstr(args))) { + printf("Can't start, furi application is running"); + return; + } else { + // We must to increment lock counter to keep balance + // TODO: rewrite whole thing, it's complex as hell + FURI_CRITICAL_ENTER(); + instance->lock_count++; + FURI_CRITICAL_EXIT(); + } + } while(false); - instance->lock_semaphore++; - furi_hal_power_insomnia_enter(); - instance->current_app = application; - printf("Starting: %s\r\n", instance->current_app->name); - furi_thread_set_name(instance->thread, application->name); - furi_thread_set_stack_size(instance->thread, application->stack_size); - furi_thread_set_callback(instance->thread, application->app); - furi_thread_start(instance->thread); + string_clear(application_name); } void loader_cli_list(Cli* cli, string_t args, Loader* instance) { @@ -152,62 +174,44 @@ void loader_cli(Cli* cli, string_t args, void* _ctx) { LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { furi_assert(name); - const FlipperApplication* flipper_app = loader_find_application_by_name(name); + const FlipperApplication* application = loader_find_application_by_name(name); - if(!flipper_app) { + if(!application) { FURI_LOG_E(TAG, "Can't find application with name %s", name); return LoaderStatusErrorUnknownApp; } - bool locked = loader_lock(instance); - - if(!locked || (furi_thread_get_state(instance->thread) != FuriThreadStateStopped)) { - FURI_LOG_E(TAG, "Can't start app. %s is running", instance->current_app->name); - /* no need to call loader_unlock() - it is called as soon as application stops */ + if(!loader_lock(loader_instance)) { + FURI_LOG_E(TAG, "Loader is locked"); return LoaderStatusErrorAppStarted; } - instance->current_app = flipper_app; - void* thread_args = NULL; - if(args) { - string_set_str(instance->args, args); - string_strim(instance->args); - thread_args = (void*)string_get_cstr(instance->args); - FURI_LOG_I(TAG, "Start %s app with args: %s", name, args); - } else { - string_reset(instance->args); - FURI_LOG_I(TAG, "Start %s app with no args", name); + if(!loader_start_application(application, args)) { + return LoaderStatusErrorInternal; } - furi_thread_set_name(instance->thread, flipper_app->name); - furi_thread_set_stack_size(instance->thread, flipper_app->stack_size); - furi_thread_set_context(instance->thread, thread_args); - furi_thread_set_callback(instance->thread, flipper_app->app); - - bool thread_started = furi_thread_start(instance->thread); - return thread_started ? LoaderStatusOk : LoaderStatusErrorInternal; + return LoaderStatusOk; } bool loader_lock(Loader* instance) { - bool ret = false; - furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK); - if(instance->lock_semaphore == 0) { - instance->lock_semaphore++; - ret = true; + FURI_CRITICAL_ENTER(); + bool result = false; + if(instance->lock_count == 0) { + instance->lock_count++; + result = true; } - furi_check(osMutexRelease(instance->mutex) == osOK); - return ret; + FURI_CRITICAL_EXIT(); + return result; } void loader_unlock(Loader* instance) { - furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK); - furi_check(instance->lock_semaphore > 0); - instance->lock_semaphore--; - furi_check(osMutexRelease(instance->mutex) == osOK); + FURI_CRITICAL_ENTER(); + if(instance->lock_count > 0) instance->lock_count--; + FURI_CRITICAL_EXIT(); } bool loader_is_locked(Loader* instance) { - return (instance->lock_semaphore > 0); + return instance->lock_count > 0; } static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { @@ -219,6 +223,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con if(thread_state == FuriThreadStateRunning) { event.type = LoaderEventTypeApplicationStarted; furi_pubsub_publish(loader_instance->pubsub, &event); + furi_hal_power_insomnia_enter(); // Snapshot current memory usage instance->free_heap_size = memmgr_get_free_heap(); @@ -239,7 +244,13 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con TAG, "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.", heap_diff, - furi_thread_get_heap_size(instance->thread)); + furi_thread_get_heap_size(instance->application_thread)); + + if(loader_instance->application_arguments) { + free(loader_instance->application_arguments); + loader_instance->application_arguments = NULL; + } + furi_hal_power_insomnia_exit(); loader_unlock(instance); @@ -262,15 +273,12 @@ static uint32_t loader_back_to_primary_menu(void* context) { static Loader* loader_alloc() { Loader* instance = furi_alloc(sizeof(Loader)); - instance->thread = furi_thread_alloc(); - furi_thread_enable_heap_trace(instance->thread); - furi_thread_set_state_context(instance->thread, instance); - furi_thread_set_state_callback(instance->thread, loader_thread_state_callback); - - string_init(instance->args); + instance->application_thread = furi_thread_alloc(); + furi_thread_enable_heap_trace(instance->application_thread); + furi_thread_set_state_context(instance->application_thread, instance); + furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback); instance->pubsub = furi_pubsub_alloc(); - instance->mutex = osMutexNew(NULL); #ifdef SRV_CLI instance->cli = furi_record_open("cli"); @@ -327,13 +335,9 @@ static void loader_free(Loader* instance) { furi_record_close("cli"); } - osMutexDelete(instance->mutex); - furi_pubsub_free(instance->pubsub); - string_clear(instance->args); - - furi_thread_free(instance->thread); + furi_thread_free(instance->application_thread); menu_free(loader_instance->primary_menu); view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); diff --git a/applications/loader/loader_i.h b/applications/loader/loader_i.h index 3e6287699f4..a8b8bfc0918 100644 --- a/applications/loader/loader_i.h +++ b/applications/loader/loader_i.h @@ -16,9 +16,11 @@ struct Loader { osThreadId_t loader_thread; - FuriThread* thread; - const FlipperApplication* current_app; - string_t args; + + const FlipperApplication* application; + FuriThread* application_thread; + char* application_arguments; + Cli* cli; Gui* gui; @@ -29,8 +31,7 @@ struct Loader { Submenu* settings_menu; size_t free_heap_size; - osMutexId_t mutex; - volatile uint8_t lock_semaphore; + volatile uint8_t lock_count; FuriPubSub* pubsub; }; diff --git a/applications/power/power_cli.c b/applications/power/power_cli.c index 689a4e83ef4..7550a0065d5 100644 --- a/applications/power/power_cli.c +++ b/applications/power/power_cli.c @@ -1,28 +1,29 @@ #include "power_cli.h" -#include -#include #include +#include +#include +#include -void power_cli_poweroff(Cli* cli, string_t args, void* context) { +void power_cli_off(Cli* cli, string_t args) { Power* power = furi_record_open("power"); printf("It's now safe to disconnect USB from your flipper\r\n"); power_off(power); } -void power_cli_reboot(Cli* cli, string_t args, void* context) { +void power_cli_reboot(Cli* cli, string_t args) { power_reboot(PowerBootModeNormal); } -void power_cli_dfu(Cli* cli, string_t args, void* context) { +void power_cli_reboot2dfu(Cli* cli, string_t args) { power_reboot(PowerBootModeDfu); } -void power_cli_info(Cli* cli, string_t args, void* context) { +void power_cli_debug(Cli* cli, string_t args) { furi_hal_power_dump_state(); } -void power_cli_otg(Cli* cli, string_t args, void* context) { +void power_cli_5v(Cli* cli, string_t args) { if(!string_cmp(args, "0")) { furi_hal_power_disable_otg(); } else if(!string_cmp(args, "1")) { @@ -32,7 +33,7 @@ void power_cli_otg(Cli* cli, string_t args, void* context) { } } -void power_cli_ext(Cli* cli, string_t args, void* context) { +void power_cli_3v3(Cli* cli, string_t args) { if(!string_cmp(args, "0")) { furi_hal_power_disable_external_3_3v(); } else if(!string_cmp(args, "1")) { @@ -42,16 +43,70 @@ void power_cli_ext(Cli* cli, string_t args, void* context) { } } +static void power_cli_command_print_usage() { + printf("Usage:\r\n"); + printf("power \r\n"); + printf("Cmd list:\r\n"); + + printf("\toff\t - shutdown power\r\n"); + printf("\treboot\t - reboot\r\n"); + printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); + printf("\tdebug\t - show debug information\r\n"); + printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); + printf("\t3v3 <0 or 1>\t - enable or disable 3v3 ext\r\n"); +} + +void power_cli(Cli* cli, string_t args, void* context) { + string_t cmd; + string_init(cmd); + + do { + if(!args_read_string_and_trim(args, cmd)) { + power_cli_command_print_usage(); + break; + } + + if(string_cmp_str(cmd, "off") == 0) { + power_cli_off(cli, args); + break; + } + + if(string_cmp_str(cmd, "reboot") == 0) { + power_cli_reboot(cli, args); + break; + } + + if(string_cmp_str(cmd, "reboot2dfu") == 0) { + power_cli_reboot2dfu(cli, args); + break; + } + + if(string_cmp_str(cmd, "debug") == 0) { + power_cli_debug(cli, args); + break; + } + + if(string_cmp_str(cmd, "5v") == 0) { + power_cli_5v(cli, args); + break; + } + + if(string_cmp_str(cmd, "3v3") == 0) { + power_cli_3v3(cli, args); + break; + } + + power_cli_command_print_usage(); + } while(false); + + string_clear(cmd); +} + void power_on_system_start() { #ifdef SRV_CLI Cli* cli = furi_record_open("cli"); - cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, NULL); - cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, NULL); - cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, NULL); - cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, NULL); - cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, NULL); - cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, NULL); + cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL); furi_record_close("cli"); #endif diff --git a/applications/power/power_service/power_api.c b/applications/power/power_service/power_api.c index 32407032132..a92bfd002ca 100644 --- a/applications/power/power_service/power_api.c +++ b/applications/power/power_service/power_api.c @@ -10,7 +10,7 @@ void power_off(Power* power) { view_dispatcher_send_to_front(power->view_dispatcher); view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewPopup); osDelay(10); - furi_crash("Disconnect USB for safe shutdown"); + furi_halt("Disconnect USB for safe shutdown"); } void power_reboot(PowerBootMode mode) { diff --git a/bootloader/targets/f6/furi_hal/furi_hal_gpio.c b/bootloader/targets/f6/furi_hal/furi_hal_gpio.c index 9cc991f3549..9fd8f4f8b64 100644 --- a/bootloader/targets/f6/furi_hal/furi_hal_gpio.c +++ b/bootloader/targets/f6/furi_hal/furi_hal_gpio.c @@ -69,24 +69,36 @@ void hal_gpio_init_ex( // Configure gpio with interrupts disabled __disable_irq(); + // Set gpio speed - if(speed == GpioSpeedLow) { + switch(speed) { + case GpioSpeedLow: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); - } else if(speed == GpioSpeedMedium) { + break; + case GpioSpeedMedium: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); - } else if(speed == GpioSpeedHigh) { + break; + case GpioSpeedHigh: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); - } else { + break; + case GpioSpeedVeryHigh: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); + break; } + // Set gpio pull mode - if(pull == GpioPullNo) { + switch(pull) { + case GpioPullNo: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); - } else if(pull == GpioPullUp) { + break; + case GpioPullUp: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); - } else { + break; + case GpioPullDown: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); + break; } + // Set gpio mode if(mode >= GpioModeInterruptRise) { // Set pin in interrupt mode @@ -100,45 +112,59 @@ void hal_gpio_init_ex( LL_EXTI_EnableIT_0_31(exti_line); LL_EXTI_EnableFallingTrig_0_31(exti_line); } - if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { + if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { LL_EXTI_EnableEvent_0_31(exti_line); LL_EXTI_EnableRisingTrig_0_31(exti_line); } - if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { + if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { LL_EXTI_EnableEvent_0_31(exti_line); LL_EXTI_EnableFallingTrig_0_31(exti_line); } } else { - // Disable interrupt if it was set + // Disable interrupts if set if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && LL_EXTI_IsEnabledIT_0_31(exti_line)) { LL_EXTI_DisableIT_0_31(exti_line); LL_EXTI_DisableRisingTrig_0_31(exti_line); LL_EXTI_DisableFallingTrig_0_31(exti_line); } + + // Prepare alternative part if any + if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { + // set alternate function + if(hal_gpio_get_pin_num(gpio) < 8) { + LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); + } else { + LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); + } + } + // Set not interrupt pin modes - if(mode == GpioModeInput) { + switch(mode) { + case GpioModeInput: LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); - } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { + break; + case GpioModeOutputPushPull: + LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); + break; + case GpioModeAltFunctionPushPull: LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); - } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + break; + case GpioModeOutputOpenDrain: + LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); + break; + case GpioModeAltFunctionOpenDrain: LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); - } else if(mode == GpioModeAnalog) { + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + break; + case GpioModeAnalog: LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); - } - } - - if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { - // enable alternate mode - LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); - - // set alternate function - if(hal_gpio_get_pin_num(gpio) < 8) { - LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); - } else { - LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); + break; + default: + break; } } diff --git a/bootloader/targets/f7/furi_hal/furi_hal_gpio.c b/bootloader/targets/f7/furi_hal/furi_hal_gpio.c index 9cc991f3549..a7c5c074fb3 100644 --- a/bootloader/targets/f7/furi_hal/furi_hal_gpio.c +++ b/bootloader/targets/f7/furi_hal/furi_hal_gpio.c @@ -69,24 +69,36 @@ void hal_gpio_init_ex( // Configure gpio with interrupts disabled __disable_irq(); + // Set gpio speed - if(speed == GpioSpeedLow) { + switch(speed) { + case GpioSpeedLow: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); - } else if(speed == GpioSpeedMedium) { + break; + case GpioSpeedMedium: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); - } else if(speed == GpioSpeedHigh) { + break; + case GpioSpeedHigh: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); - } else { + break; + case GpioSpeedVeryHigh: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); + break; } + // Set gpio pull mode - if(pull == GpioPullNo) { + switch(pull) { + case GpioPullNo: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); - } else if(pull == GpioPullUp) { + break; + case GpioPullUp: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); - } else { + break; + case GpioPullDown: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); + break; } + // Set gpio mode if(mode >= GpioModeInterruptRise) { // Set pin in interrupt mode @@ -100,48 +112,61 @@ void hal_gpio_init_ex( LL_EXTI_EnableIT_0_31(exti_line); LL_EXTI_EnableFallingTrig_0_31(exti_line); } - if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { + if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { LL_EXTI_EnableEvent_0_31(exti_line); LL_EXTI_EnableRisingTrig_0_31(exti_line); } - if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { + if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { LL_EXTI_EnableEvent_0_31(exti_line); LL_EXTI_EnableFallingTrig_0_31(exti_line); } } else { - // Disable interrupt if it was set + // Disable interrupts if set if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && LL_EXTI_IsEnabledIT_0_31(exti_line)) { LL_EXTI_DisableIT_0_31(exti_line); LL_EXTI_DisableRisingTrig_0_31(exti_line); LL_EXTI_DisableFallingTrig_0_31(exti_line); } + + // Prepare alternative part if any + if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { + // set alternate function + if(hal_gpio_get_pin_num(gpio) < 8) { + LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); + } else { + LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); + } + } + // Set not interrupt pin modes - if(mode == GpioModeInput) { + switch(mode) { + case GpioModeInput: LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); - } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { + break; + case GpioModeOutputPushPull: + LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); + break; + case GpioModeAltFunctionPushPull: LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); - } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + break; + case GpioModeOutputOpenDrain: + LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); + break; + case GpioModeAltFunctionOpenDrain: LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); - } else if(mode == GpioModeAnalog) { + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + break; + case GpioModeAnalog: LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); + break; + default: + break; } } - - if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { - // enable alternate mode - LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); - - // set alternate function - if(hal_gpio_get_pin_num(gpio) < 8) { - LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); - } else { - LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); - } - } - __enable_irq(); } diff --git a/core/furi/check.c b/core/furi/check.c index e5f67f173dc..657e2905efa 100644 --- a/core/furi/check.c +++ b/core/furi/check.c @@ -4,7 +4,7 @@ #include #include -__attribute__((always_inline)) inline static void __furi_print_name() { +static void __furi_print_name() { if(task_is_isr_context()) { furi_hal_console_puts("[ISR] "); } else { @@ -19,9 +19,9 @@ __attribute__((always_inline)) inline static void __furi_print_name() { } } -__attribute__((always_inline)) inline static void __furi_halt() { - asm volatile("bkpt 0x00 \n" - "loop: \n" +static void __furi_halt() { + asm volatile("loop: \n" + "bkpt 0x00 \n" "wfi \n" "b loop \n" : @@ -50,3 +50,18 @@ void furi_crash(const char* message) { NVIC_SystemReset(); #endif } + +void furi_halt(const char* message) { + __disable_irq(); + + if(message == NULL) { + message = "System halt requested."; + } + + furi_hal_console_puts("\r\n\033[0;31m[HALT]"); + __furi_print_name(); + furi_hal_console_puts(message); + furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + __furi_halt(); +} diff --git a/core/furi/check.h b/core/furi/check.h index 8abd9bbbd3c..b4b668150f2 100644 --- a/core/furi/check.h +++ b/core/furi/check.h @@ -17,6 +17,9 @@ extern "C" { /** Crash system */ void furi_crash(const char* message); +/** Halt system */ +void furi_halt(const char* message); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f6/cube/startup_stm32wb55xx_cm4.s b/firmware/targets/f6/cube/startup_stm32wb55xx_cm4.s index c5c2b3fc3d8..053db01e424 100644 --- a/firmware/targets/f6/cube/startup_stm32wb55xx_cm4.s +++ b/firmware/targets/f6/cube/startup_stm32wb55xx_cm4.s @@ -1,48 +1,48 @@ /** - ****************************************************************************** - * @file startup_stm32wb55xx_cm4.s - * @author MCD Application Team - * @brief STM32WB55xx devices vector table GCC toolchain. - * This module performs: - * - Set the initial SP - * - Set the initial PC == Reset_Handler, - * - Set the vector table entries with the exceptions ISR address - * - Branches to main in the C library (which eventually - * calls main()). - * After Reset the Cortex-M4 processor is in Thread mode, - * priority is Privileged, and the Stack is set to Main. - ****************************************************************************** - * @attention - * - * Copyright (c) 2019-2021 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ - - .syntax unified - .cpu cortex-m4 - .fpu softvfp - .thumb - -.global g_pfnVectors -.global Default_Handler + ****************************************************************************** + * @file startup_stm32wb55xx_cm4.s + * @author MCD Application Team + * @brief STM32WB55xx devices vector table GCC toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M4 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + * Copyright (c) 2019-2021 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +.syntax unified +.cpu cortex-m4 +.fpu softvfp +.thumb + +.global g_pfnVectors +.global Default_Handler /* start address for the initialization values of the .data section. defined in linker script */ -.word _sidata +.word _sidata /* start address for the .data section. defined in linker script */ -.word _sdata +.word _sdata /* end address for the .data section. defined in linker script */ -.word _edata +.word _edata /* start address for the .bss section. defined in linker script */ -.word _sbss +.word _sbss /* end address for the .bss section. defined in linker script */ -.word _ebss +.word _ebss /* start address for the .MB_MEM2 section. defined in linker script */ .word _sMB_MEM2 /* end address for the .MB_MEM2 section. defined in linker script */ @@ -50,67 +50,69 @@ defined in linker script */ /* INIT_BSS macro is used to fill the specified region [start : end] with zeros */ .macro INIT_BSS start, end - ldr r0, =\start - ldr r1, =\end - movs r3, #0 - bl LoopFillZerobss + ldr r0, =\start + ldr r1, =\end + movs r3, #0 + bl LoopFillZerobss .endm /* INIT_DATA macro is used to copy data in the region [start : end] starting from 'src' */ .macro INIT_DATA start, end, src - ldr r0, =\start - ldr r1, =\end - ldr r2, =\src - movs r3, #0 - bl LoopCopyDataInit + ldr r0, =\start + ldr r1, =\end + ldr r2, =\src + movs r3, #0 + bl LoopCopyDataInit .endm .section .text.data_initializers CopyDataInit: - ldr r4, [r2, r3] - str r4, [r0, r3] - adds r3, r3, #4 + ldr r4, [r2, r3] + str r4, [r0, r3] + adds r3, r3, #4 LoopCopyDataInit: - adds r4, r0, r3 - cmp r4, r1 - bcc CopyDataInit - bx lr + adds r4, r0, r3 + cmp r4, r1 + bcc CopyDataInit + bx lr FillZerobss: - str r3, [r0] - adds r0, r0, #4 + str r3, [r0] + adds r0, r0, #4 LoopFillZerobss: - cmp r0, r1 - bcc FillZerobss - bx lr + cmp r0, r1 + bcc FillZerobss + bx lr + + +.section .text.Reset_Handler +.weak Reset_Handler +.type Reset_Handler, %function - .section .text.Reset_Handler - .weak Reset_Handler - .type Reset_Handler, %function Reset_Handler: - ldr r0, =_estack - mov sp, r0 /* set stack pointer */ -/* Call the clock system intitialization function.*/ - bl SystemInit + ldr r0, =_estack + mov sp, r0 /* set stack pointer */ + /* Call the clock system intitialization function.*/ + bl SystemInit /* Copy the data segment initializers from flash to SRAM */ - INIT_DATA _sdata, _edata, _sidata + INIT_DATA _sdata, _edata, _sidata /* Zero fill the bss segments. */ - INIT_BSS _sbss, _ebss - INIT_BSS _sMB_MEM2, _eMB_MEM2 + INIT_BSS _sbss, _ebss + INIT_BSS _sMB_MEM2, _eMB_MEM2 /* Call static constructors */ - bl __libc_init_array + bl __libc_init_array /* Call the application s entry point.*/ - bl main + bl main LoopForever: - b LoopForever - -.size Reset_Handler, .-Reset_Handler + b LoopForever + +.size Reset_Handler, .-Reset_Handler /** * @brief This is the code that gets called when the processor receives an @@ -120,11 +122,11 @@ LoopForever: * @param None * @retval None */ - .section .text.Default_Handler,"ax",%progbits + .section .text.Default_Handler,"ax",%progbits Default_Handler: Infinite_Loop: - b Infinite_Loop - .size Default_Handler, .-Default_Handler + b Infinite_Loop + .size Default_Handler, .-Default_Handler /****************************************************************************** * * The minimal vector table for a Cortex-M4. Note that the proper constructs @@ -132,91 +134,91 @@ Infinite_Loop: * 0x0000.0000. * ******************************************************************************/ - .section .isr_vector,"a",%progbits - .type g_pfnVectors, %object - .size g_pfnVectors, .-g_pfnVectors + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors g_pfnVectors: - .word _estack - .word Reset_Handler - .word NMI_Handler - .word HardFault_Handler - .word MemManage_Handler - .word BusFault_Handler - .word UsageFault_Handler - .word 0 - .word 0 - .word 0 - .word 0 - .word SVC_Handler - .word DebugMon_Handler - .word 0 - .word PendSV_Handler - .word SysTick_Handler - .word WWDG_IRQHandler - .word PVD_PVM_IRQHandler - .word TAMP_STAMP_LSECSS_IRQHandler - .word RTC_WKUP_IRQHandler - .word FLASH_IRQHandler - .word RCC_IRQHandler - .word EXTI0_IRQHandler - .word EXTI1_IRQHandler - .word EXTI2_IRQHandler - .word EXTI3_IRQHandler - .word EXTI4_IRQHandler - .word DMA1_Channel1_IRQHandler - .word DMA1_Channel2_IRQHandler - .word DMA1_Channel3_IRQHandler - .word DMA1_Channel4_IRQHandler - .word DMA1_Channel5_IRQHandler - .word DMA1_Channel6_IRQHandler - .word DMA1_Channel7_IRQHandler - .word ADC1_IRQHandler - .word USB_HP_IRQHandler - .word USB_LP_IRQHandler - .word C2SEV_PWR_C2H_IRQHandler - .word COMP_IRQHandler - .word EXTI9_5_IRQHandler - .word TIM1_BRK_IRQHandler - .word TIM1_UP_TIM16_IRQHandler - .word TIM1_TRG_COM_TIM17_IRQHandler - .word TIM1_CC_IRQHandler - .word TIM2_IRQHandler - .word PKA_IRQHandler - .word I2C1_EV_IRQHandler - .word I2C1_ER_IRQHandler - .word I2C3_EV_IRQHandler - .word I2C3_ER_IRQHandler - .word SPI1_IRQHandler - .word SPI2_IRQHandler - .word USART1_IRQHandler - .word LPUART1_IRQHandler - .word SAI1_IRQHandler - .word TSC_IRQHandler - .word EXTI15_10_IRQHandler - .word RTC_Alarm_IRQHandler - .word CRS_IRQHandler - .word PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler - .word IPCC_C1_RX_IRQHandler - .word IPCC_C1_TX_IRQHandler - .word HSEM_IRQHandler - .word LPTIM1_IRQHandler - .word LPTIM2_IRQHandler - .word LCD_IRQHandler - .word QUADSPI_IRQHandler - .word AES1_IRQHandler - .word AES2_IRQHandler - .word RNG_IRQHandler - .word FPU_IRQHandler - .word DMA2_Channel1_IRQHandler - .word DMA2_Channel2_IRQHandler - .word DMA2_Channel3_IRQHandler - .word DMA2_Channel4_IRQHandler - .word DMA2_Channel5_IRQHandler - .word DMA2_Channel6_IRQHandler - .word DMA2_Channel7_IRQHandler - .word DMAMUX1_OVR_IRQHandler + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_PVM_IRQHandler + .word TAMP_STAMP_LSECSS_IRQHandler + .word RTC_WKUP_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_IRQHandler + .word USB_HP_IRQHandler + .word USB_LP_IRQHandler + .word C2SEV_PWR_C2H_IRQHandler + .word COMP_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_TIM16_IRQHandler + .word TIM1_TRG_COM_TIM17_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word PKA_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C3_EV_IRQHandler + .word I2C3_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word LPUART1_IRQHandler + .word SAI1_IRQHandler + .word TSC_IRQHandler + .word EXTI15_10_IRQHandler + .word RTC_Alarm_IRQHandler + .word CRS_IRQHandler + .word PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler + .word IPCC_C1_RX_IRQHandler + .word IPCC_C1_TX_IRQHandler + .word HSEM_IRQHandler + .word LPTIM1_IRQHandler + .word LPTIM2_IRQHandler + .word LCD_IRQHandler + .word QUADSPI_IRQHandler + .word AES1_IRQHandler + .word AES2_IRQHandler + .word RNG_IRQHandler + .word FPU_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_IRQHandler + .word DMA2_Channel5_IRQHandler + .word DMA2_Channel6_IRQHandler + .word DMA2_Channel7_IRQHandler + .word DMAMUX1_OVR_IRQHandler /******************************************************************************* * @@ -225,220 +227,220 @@ g_pfnVectors: * this definition. * *******************************************************************************/ - .weak NMI_Handler - .thumb_set NMI_Handler,Default_Handler - - .weak HardFault_Handler - .thumb_set HardFault_Handler,Default_Handler + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler - .weak MemManage_Handler - .thumb_set MemManage_Handler,Default_Handler + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler - .weak BusFault_Handler - .thumb_set BusFault_Handler,Default_Handler + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler - .weak UsageFault_Handler - .thumb_set UsageFault_Handler,Default_Handler + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler - .weak SVC_Handler - .thumb_set SVC_Handler,Default_Handler + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler - .weak DebugMon_Handler - .thumb_set DebugMon_Handler,Default_Handler + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler - .weak PendSV_Handler - .thumb_set PendSV_Handler,Default_Handler + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler - .weak SysTick_Handler - .thumb_set SysTick_Handler,Default_Handler + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler - .weak WWDG_IRQHandler - .thumb_set WWDG_IRQHandler,Default_Handler + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler - .weak PVD_PVM_IRQHandler - .thumb_set PVD_PVM_IRQHandler,Default_Handler + .weak PVD_PVM_IRQHandler + .thumb_set PVD_PVM_IRQHandler,Default_Handler - .weak TAMP_STAMP_LSECSS_IRQHandler - .thumb_set TAMP_STAMP_LSECSS_IRQHandler,Default_Handler + .weak TAMP_STAMP_LSECSS_IRQHandler + .thumb_set TAMP_STAMP_LSECSS_IRQHandler,Default_Handler - .weak RTC_WKUP_IRQHandler - .thumb_set RTC_WKUP_IRQHandler,Default_Handler + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler - .weak FLASH_IRQHandler - .thumb_set FLASH_IRQHandler,Default_Handler + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler - .weak RCC_IRQHandler - .thumb_set RCC_IRQHandler,Default_Handler + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler - .weak EXTI0_IRQHandler - .thumb_set EXTI0_IRQHandler,Default_Handler + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler - .weak EXTI1_IRQHandler - .thumb_set EXTI1_IRQHandler,Default_Handler + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler - .weak EXTI2_IRQHandler - .thumb_set EXTI2_IRQHandler,Default_Handler + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler - .weak EXTI3_IRQHandler - .thumb_set EXTI3_IRQHandler,Default_Handler + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler - .weak EXTI4_IRQHandler - .thumb_set EXTI4_IRQHandler,Default_Handler + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler - .weak DMA1_Channel1_IRQHandler - .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler - .weak DMA1_Channel2_IRQHandler - .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler - .weak DMA1_Channel3_IRQHandler - .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler - .weak DMA1_Channel4_IRQHandler - .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler - .weak DMA1_Channel5_IRQHandler - .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler - .weak DMA1_Channel6_IRQHandler - .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler - .weak DMA1_Channel7_IRQHandler - .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler - .weak ADC1_IRQHandler - .thumb_set ADC1_IRQHandler,Default_Handler + .weak ADC1_IRQHandler + .thumb_set ADC1_IRQHandler,Default_Handler - .weak USB_HP_IRQHandler - .thumb_set USB_HP_IRQHandler,Default_Handler + .weak USB_HP_IRQHandler + .thumb_set USB_HP_IRQHandler,Default_Handler - .weak USB_LP_IRQHandler - .thumb_set USB_LP_IRQHandler,Default_Handler + .weak USB_LP_IRQHandler + .thumb_set USB_LP_IRQHandler,Default_Handler - .weak C2SEV_PWR_C2H_IRQHandler - .thumb_set C2SEV_PWR_C2H_IRQHandler,Default_Handler + .weak C2SEV_PWR_C2H_IRQHandler + .thumb_set C2SEV_PWR_C2H_IRQHandler,Default_Handler - .weak COMP_IRQHandler - .thumb_set COMP_IRQHandler,Default_Handler + .weak COMP_IRQHandler + .thumb_set COMP_IRQHandler,Default_Handler - .weak EXTI9_5_IRQHandler - .thumb_set EXTI9_5_IRQHandler,Default_Handler + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler - .weak TIM1_BRK_IRQHandler - .thumb_set TIM1_BRK_IRQHandler,Default_Handler + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler - .weak TIM1_UP_TIM16_IRQHandler - .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler - .weak TIM1_TRG_COM_TIM17_IRQHandler - .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler - .weak TIM1_CC_IRQHandler - .thumb_set TIM1_CC_IRQHandler,Default_Handler + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler - .weak TIM2_IRQHandler - .thumb_set TIM2_IRQHandler,Default_Handler + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler - .weak PKA_IRQHandler - .thumb_set PKA_IRQHandler,Default_Handler + .weak PKA_IRQHandler + .thumb_set PKA_IRQHandler,Default_Handler - .weak I2C1_EV_IRQHandler - .thumb_set I2C1_EV_IRQHandler,Default_Handler + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler - .weak I2C1_ER_IRQHandler - .thumb_set I2C1_ER_IRQHandler,Default_Handler + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler - .weak I2C3_EV_IRQHandler - .thumb_set I2C3_EV_IRQHandler,Default_Handler + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler - .weak I2C3_ER_IRQHandler - .thumb_set I2C3_ER_IRQHandler,Default_Handler + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler - .weak SPI1_IRQHandler - .thumb_set SPI1_IRQHandler,Default_Handler + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler - .weak SPI2_IRQHandler - .thumb_set SPI2_IRQHandler,Default_Handler + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler - .weak USART1_IRQHandler - .thumb_set USART1_IRQHandler,Default_Handler + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler - .weak LPUART1_IRQHandler - .thumb_set LPUART1_IRQHandler,Default_Handler + .weak LPUART1_IRQHandler + .thumb_set LPUART1_IRQHandler,Default_Handler - .weak SAI1_IRQHandler - .thumb_set SAI1_IRQHandler,Default_Handler + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler - .weak TSC_IRQHandler - .thumb_set TSC_IRQHandler,Default_Handler + .weak TSC_IRQHandler + .thumb_set TSC_IRQHandler,Default_Handler - .weak EXTI15_10_IRQHandler - .thumb_set EXTI15_10_IRQHandler,Default_Handler + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler - .weak RTC_Alarm_IRQHandler - .thumb_set RTC_Alarm_IRQHandler,Default_Handler + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler - .weak CRS_IRQHandler - .thumb_set CRS_IRQHandler,Default_Handler + .weak CRS_IRQHandler + .thumb_set CRS_IRQHandler,Default_Handler - .weak PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler - .thumb_set PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler,Default_Handler + .weak PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler + .thumb_set PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler,Default_Handler - .weak IPCC_C1_RX_IRQHandler - .thumb_set IPCC_C1_RX_IRQHandler,Default_Handler + .weak IPCC_C1_RX_IRQHandler + .thumb_set IPCC_C1_RX_IRQHandler,Default_Handler - .weak IPCC_C1_TX_IRQHandler - .thumb_set IPCC_C1_TX_IRQHandler,Default_Handler + .weak IPCC_C1_TX_IRQHandler + .thumb_set IPCC_C1_TX_IRQHandler,Default_Handler - .weak HSEM_IRQHandler - .thumb_set HSEM_IRQHandler,Default_Handler + .weak HSEM_IRQHandler + .thumb_set HSEM_IRQHandler,Default_Handler - .weak LPTIM1_IRQHandler - .thumb_set LPTIM1_IRQHandler,Default_Handler + .weak LPTIM1_IRQHandler + .thumb_set LPTIM1_IRQHandler,Default_Handler - .weak LPTIM2_IRQHandler - .thumb_set LPTIM2_IRQHandler,Default_Handler + .weak LPTIM2_IRQHandler + .thumb_set LPTIM2_IRQHandler,Default_Handler - .weak LCD_IRQHandler - .thumb_set LCD_IRQHandler,Default_Handler + .weak LCD_IRQHandler + .thumb_set LCD_IRQHandler,Default_Handler - .weak QUADSPI_IRQHandler - .thumb_set QUADSPI_IRQHandler,Default_Handler + .weak QUADSPI_IRQHandler + .thumb_set QUADSPI_IRQHandler,Default_Handler - .weak AES1_IRQHandler - .thumb_set AES1_IRQHandler,Default_Handler + .weak AES1_IRQHandler + .thumb_set AES1_IRQHandler,Default_Handler - .weak AES2_IRQHandler - .thumb_set AES2_IRQHandler,Default_Handler + .weak AES2_IRQHandler + .thumb_set AES2_IRQHandler,Default_Handler - .weak RNG_IRQHandler - .thumb_set RNG_IRQHandler,Default_Handler + .weak RNG_IRQHandler + .thumb_set RNG_IRQHandler,Default_Handler - .weak FPU_IRQHandler - .thumb_set FPU_IRQHandler,Default_Handler + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler - .weak DMA2_Channel1_IRQHandler - .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler - .weak DMA2_Channel2_IRQHandler - .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler - .weak DMA2_Channel3_IRQHandler - .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler - .weak DMA2_Channel4_IRQHandler - .thumb_set DMA2_Channel4_IRQHandler,Default_Handler + .weak DMA2_Channel4_IRQHandler + .thumb_set DMA2_Channel4_IRQHandler,Default_Handler - .weak DMA2_Channel5_IRQHandler - .thumb_set DMA2_Channel5_IRQHandler,Default_Handler + .weak DMA2_Channel5_IRQHandler + .thumb_set DMA2_Channel5_IRQHandler,Default_Handler - .weak DMA2_Channel6_IRQHandler - .thumb_set DMA2_Channel6_IRQHandler,Default_Handler + .weak DMA2_Channel6_IRQHandler + .thumb_set DMA2_Channel6_IRQHandler,Default_Handler - .weak DMA2_Channel7_IRQHandler - .thumb_set DMA2_Channel7_IRQHandler,Default_Handler + .weak DMA2_Channel7_IRQHandler + .thumb_set DMA2_Channel7_IRQHandler,Default_Handler - .weak DMAMUX1_OVR_IRQHandler - .thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler + .weak DMAMUX1_OVR_IRQHandler + .thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f6/furi_hal/furi_hal_gpio.c b/firmware/targets/f6/furi_hal/furi_hal_gpio.c index 8c2aefe2d5f..a6f25d10ece 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f6/furi_hal/furi_hal_gpio.c @@ -68,25 +68,37 @@ void hal_gpio_init_ex( uint32_t exti_line = GET_EXTI_LINE(gpio->pin); // Configure gpio with interrupts disabled - __disable_irq(); + FURI_CRITICAL_ENTER(); + // Set gpio speed - if(speed == GpioSpeedLow) { + switch(speed) { + case GpioSpeedLow: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); - } else if(speed == GpioSpeedMedium) { + break; + case GpioSpeedMedium: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); - } else if(speed == GpioSpeedHigh) { + break; + case GpioSpeedHigh: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); - } else { + break; + case GpioSpeedVeryHigh: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); + break; } + // Set gpio pull mode - if(pull == GpioPullNo) { + switch(pull) { + case GpioPullNo: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); - } else if(pull == GpioPullUp) { + break; + case GpioPullUp: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); - } else { + break; + case GpioPullDown: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); + break; } + // Set gpio mode if(mode >= GpioModeInterruptRise) { // Set pin in interrupt mode @@ -100,93 +112,106 @@ void hal_gpio_init_ex( LL_EXTI_EnableIT_0_31(exti_line); LL_EXTI_EnableFallingTrig_0_31(exti_line); } - if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { + if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { LL_EXTI_EnableEvent_0_31(exti_line); LL_EXTI_EnableRisingTrig_0_31(exti_line); } - if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { + if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { LL_EXTI_EnableEvent_0_31(exti_line); LL_EXTI_EnableFallingTrig_0_31(exti_line); } } else { - // Disable interrupt if it was set + // Disable interrupts if set if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && LL_EXTI_IsEnabledIT_0_31(exti_line)) { LL_EXTI_DisableIT_0_31(exti_line); LL_EXTI_DisableRisingTrig_0_31(exti_line); LL_EXTI_DisableFallingTrig_0_31(exti_line); } + + // Prepare alternative part if any + if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { + // set alternate function + if(hal_gpio_get_pin_num(gpio) < 8) { + LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); + } else { + LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); + } + } + // Set not interrupt pin modes - if(mode == GpioModeInput) { + switch(mode) { + case GpioModeInput: LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); - } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { + break; + case GpioModeOutputPushPull: + LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); + break; + case GpioModeAltFunctionPushPull: LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); - } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + break; + case GpioModeOutputOpenDrain: + LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); + break; + case GpioModeAltFunctionOpenDrain: LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); - } else if(mode == GpioModeAnalog) { + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + break; + case GpioModeAnalog: LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); + break; + default: + break; } } - - if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { - // enable alternate mode - LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); - - // set alternate function - if(hal_gpio_get_pin_num(gpio) < 8) { - LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); - } else { - LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); - } - } - - __enable_irq(); + FURI_CRITICAL_EXIT(); } void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) { furi_assert(gpio); furi_assert(cb); - __disable_irq(); + FURI_CRITICAL_ENTER(); uint8_t pin_num = hal_gpio_get_pin_num(gpio); furi_assert(gpio_interrupt[pin_num].callback == NULL); gpio_interrupt[pin_num].callback = cb; gpio_interrupt[pin_num].context = ctx; gpio_interrupt[pin_num].ready = true; - __enable_irq(); + FURI_CRITICAL_EXIT(); } void hal_gpio_enable_int_callback(const GpioPin* gpio) { furi_assert(gpio); - __disable_irq(); + FURI_CRITICAL_ENTER(); uint8_t pin_num = hal_gpio_get_pin_num(gpio); if(gpio_interrupt[pin_num].callback) { gpio_interrupt[pin_num].ready = true; } - __enable_irq(); + FURI_CRITICAL_EXIT(); } void hal_gpio_disable_int_callback(const GpioPin* gpio) { furi_assert(gpio); - __disable_irq(); + FURI_CRITICAL_ENTER(); uint8_t pin_num = hal_gpio_get_pin_num(gpio); gpio_interrupt[pin_num].ready = false; - __enable_irq(); + FURI_CRITICAL_EXIT(); } void hal_gpio_remove_int_callback(const GpioPin* gpio) { furi_assert(gpio); - __disable_irq(); + FURI_CRITICAL_ENTER(); uint8_t pin_num = hal_gpio_get_pin_num(gpio); gpio_interrupt[pin_num].callback = NULL; gpio_interrupt[pin_num].context = NULL; gpio_interrupt[pin_num].ready = false; - __enable_irq(); + FURI_CRITICAL_EXIT(); } static void hal_gpio_int_call(uint16_t pin_num) { diff --git a/firmware/targets/f6/furi_hal/furi_hal_irda.c b/firmware/targets/f6/furi_hal/furi_hal_irda.c index 070e3c16f26..cd9847414a9 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_irda.c +++ b/firmware/targets/f6/furi_hal/furi_hal_irda.c @@ -516,7 +516,7 @@ static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_ IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; furi_assert(buffer->polarity != NULL); - __disable_irq(); + FURI_CRITICAL_ENTER(); bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); if(channel_enabled) { LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); @@ -526,7 +526,7 @@ static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_ if(channel_enabled) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); } - __enable_irq(); + FURI_CRITICAL_EXIT(); } static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { @@ -536,7 +536,7 @@ static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { furi_assert(buffer->data != NULL); /* non-circular mode requires disabled channel before setup */ - __disable_irq(); + FURI_CRITICAL_ENTER(); bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); if(channel_enabled) { LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); @@ -546,7 +546,7 @@ static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { if(channel_enabled) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); } - __enable_irq(); + FURI_CRITICAL_EXIT(); } static void furi_hal_irda_async_tx_free_resources(void) { @@ -621,10 +621,10 @@ void furi_hal_irda_async_tx_start(uint32_t freq, float duty_cycle) { hal_gpio_init_ex( &gpio_irda_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); - __disable_irq(); + FURI_CRITICAL_ENTER(); LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ LL_TIM_EnableCounter(TIM1); - __enable_irq(); + FURI_CRITICAL_EXIT(); } void furi_hal_irda_async_tx_wait_termination(void) { @@ -642,9 +642,9 @@ void furi_hal_irda_async_tx_stop(void) { furi_assert(furi_hal_irda_state >= IrdaStateAsyncTx); furi_assert(furi_hal_irda_state < IrdaStateMAX); - __disable_irq(); + FURI_CRITICAL_ENTER(); if(furi_hal_irda_state == IrdaStateAsyncTx) furi_hal_irda_state = IrdaStateAsyncTxStopReq; - __enable_irq(); + FURI_CRITICAL_EXIT(); furi_hal_irda_async_tx_wait_termination(); } diff --git a/firmware/targets/f6/furi_hal/furi_hal_os.c b/firmware/targets/f6/furi_hal/furi_hal_os.c index 6ef5a9f4d17..3a6e5f38feb 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_os.c +++ b/firmware/targets/f6/furi_hal/furi_hal_os.c @@ -115,11 +115,11 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { } // Stop IRQ handling, no one should disturb us till we finish - __disable_irq(); + FURI_CRITICAL_ENTER(); // Confirm OS that sleep is still possible if(eTaskConfirmSleepModeStatus() == eAbortSleep) { - __enable_irq(); + FURI_CRITICAL_EXIT(); return; } @@ -136,7 +136,7 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { } // Reenable IRQ - __enable_irq(); + FURI_CRITICAL_EXIT(); } void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { diff --git a/firmware/targets/f6/furi_hal/furi_hal_uart.c b/firmware/targets/f6/furi_hal/furi_hal_uart.c index 1ebba5d3d8d..2ee0cf85dd0 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_uart.c +++ b/firmware/targets/f6/furi_hal/furi_hal_uart.c @@ -5,6 +5,7 @@ #include #include +#include static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); static void* irq_ctx[2]; @@ -33,13 +34,12 @@ static void furi_hal_usart_init(uint32_t baud) { USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; LL_USART_Init(USART1, &USART_InitStruct); - LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2); LL_USART_EnableFIFO(USART1); LL_USART_ConfigAsyncMode(USART1); LL_USART_Enable(USART1); - while(!LL_USART_IsActiveFlag_TEACK(USART1)) + while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) ; LL_USART_EnableIT_RXNE_RXFNE(USART1); @@ -70,13 +70,11 @@ static void furi_hal_lpuart_init(uint32_t baud) { LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; LL_LPUART_Init(LPUART1, &LPUART_InitStruct); - LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); - LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); LL_LPUART_EnableFIFO(LPUART1); LL_LPUART_Enable(LPUART1); - while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))) + while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) ; furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); diff --git a/firmware/targets/f6/target.mk b/firmware/targets/f6/target.mk index 0c9bd182e1b..767cbfa0a4d 100644 --- a/firmware/targets/f6/target.mk +++ b/firmware/targets/f6/target.mk @@ -14,7 +14,13 @@ FLASH_ADDRESS = 0x08000000 CFLAGS += -DNO_BOOTLOADER endif +DEBUG_RTOS_THREADS ?= 1 +ifeq ($(DEBUG_RTOS_THREADS), 1) OPENOCD_OPTS = -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init" +else +OPENOCD_OPTS = -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init" +endif + BOOT_CFLAGS = -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET) MCU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard diff --git a/firmware/targets/f7/cube/startup_stm32wb55xx_cm4.s b/firmware/targets/f7/cube/startup_stm32wb55xx_cm4.s index c5c2b3fc3d8..053db01e424 100644 --- a/firmware/targets/f7/cube/startup_stm32wb55xx_cm4.s +++ b/firmware/targets/f7/cube/startup_stm32wb55xx_cm4.s @@ -1,48 +1,48 @@ /** - ****************************************************************************** - * @file startup_stm32wb55xx_cm4.s - * @author MCD Application Team - * @brief STM32WB55xx devices vector table GCC toolchain. - * This module performs: - * - Set the initial SP - * - Set the initial PC == Reset_Handler, - * - Set the vector table entries with the exceptions ISR address - * - Branches to main in the C library (which eventually - * calls main()). - * After Reset the Cortex-M4 processor is in Thread mode, - * priority is Privileged, and the Stack is set to Main. - ****************************************************************************** - * @attention - * - * Copyright (c) 2019-2021 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ - - .syntax unified - .cpu cortex-m4 - .fpu softvfp - .thumb - -.global g_pfnVectors -.global Default_Handler + ****************************************************************************** + * @file startup_stm32wb55xx_cm4.s + * @author MCD Application Team + * @brief STM32WB55xx devices vector table GCC toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M4 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + * Copyright (c) 2019-2021 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +.syntax unified +.cpu cortex-m4 +.fpu softvfp +.thumb + +.global g_pfnVectors +.global Default_Handler /* start address for the initialization values of the .data section. defined in linker script */ -.word _sidata +.word _sidata /* start address for the .data section. defined in linker script */ -.word _sdata +.word _sdata /* end address for the .data section. defined in linker script */ -.word _edata +.word _edata /* start address for the .bss section. defined in linker script */ -.word _sbss +.word _sbss /* end address for the .bss section. defined in linker script */ -.word _ebss +.word _ebss /* start address for the .MB_MEM2 section. defined in linker script */ .word _sMB_MEM2 /* end address for the .MB_MEM2 section. defined in linker script */ @@ -50,67 +50,69 @@ defined in linker script */ /* INIT_BSS macro is used to fill the specified region [start : end] with zeros */ .macro INIT_BSS start, end - ldr r0, =\start - ldr r1, =\end - movs r3, #0 - bl LoopFillZerobss + ldr r0, =\start + ldr r1, =\end + movs r3, #0 + bl LoopFillZerobss .endm /* INIT_DATA macro is used to copy data in the region [start : end] starting from 'src' */ .macro INIT_DATA start, end, src - ldr r0, =\start - ldr r1, =\end - ldr r2, =\src - movs r3, #0 - bl LoopCopyDataInit + ldr r0, =\start + ldr r1, =\end + ldr r2, =\src + movs r3, #0 + bl LoopCopyDataInit .endm .section .text.data_initializers CopyDataInit: - ldr r4, [r2, r3] - str r4, [r0, r3] - adds r3, r3, #4 + ldr r4, [r2, r3] + str r4, [r0, r3] + adds r3, r3, #4 LoopCopyDataInit: - adds r4, r0, r3 - cmp r4, r1 - bcc CopyDataInit - bx lr + adds r4, r0, r3 + cmp r4, r1 + bcc CopyDataInit + bx lr FillZerobss: - str r3, [r0] - adds r0, r0, #4 + str r3, [r0] + adds r0, r0, #4 LoopFillZerobss: - cmp r0, r1 - bcc FillZerobss - bx lr + cmp r0, r1 + bcc FillZerobss + bx lr + + +.section .text.Reset_Handler +.weak Reset_Handler +.type Reset_Handler, %function - .section .text.Reset_Handler - .weak Reset_Handler - .type Reset_Handler, %function Reset_Handler: - ldr r0, =_estack - mov sp, r0 /* set stack pointer */ -/* Call the clock system intitialization function.*/ - bl SystemInit + ldr r0, =_estack + mov sp, r0 /* set stack pointer */ + /* Call the clock system intitialization function.*/ + bl SystemInit /* Copy the data segment initializers from flash to SRAM */ - INIT_DATA _sdata, _edata, _sidata + INIT_DATA _sdata, _edata, _sidata /* Zero fill the bss segments. */ - INIT_BSS _sbss, _ebss - INIT_BSS _sMB_MEM2, _eMB_MEM2 + INIT_BSS _sbss, _ebss + INIT_BSS _sMB_MEM2, _eMB_MEM2 /* Call static constructors */ - bl __libc_init_array + bl __libc_init_array /* Call the application s entry point.*/ - bl main + bl main LoopForever: - b LoopForever - -.size Reset_Handler, .-Reset_Handler + b LoopForever + +.size Reset_Handler, .-Reset_Handler /** * @brief This is the code that gets called when the processor receives an @@ -120,11 +122,11 @@ LoopForever: * @param None * @retval None */ - .section .text.Default_Handler,"ax",%progbits + .section .text.Default_Handler,"ax",%progbits Default_Handler: Infinite_Loop: - b Infinite_Loop - .size Default_Handler, .-Default_Handler + b Infinite_Loop + .size Default_Handler, .-Default_Handler /****************************************************************************** * * The minimal vector table for a Cortex-M4. Note that the proper constructs @@ -132,91 +134,91 @@ Infinite_Loop: * 0x0000.0000. * ******************************************************************************/ - .section .isr_vector,"a",%progbits - .type g_pfnVectors, %object - .size g_pfnVectors, .-g_pfnVectors + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors g_pfnVectors: - .word _estack - .word Reset_Handler - .word NMI_Handler - .word HardFault_Handler - .word MemManage_Handler - .word BusFault_Handler - .word UsageFault_Handler - .word 0 - .word 0 - .word 0 - .word 0 - .word SVC_Handler - .word DebugMon_Handler - .word 0 - .word PendSV_Handler - .word SysTick_Handler - .word WWDG_IRQHandler - .word PVD_PVM_IRQHandler - .word TAMP_STAMP_LSECSS_IRQHandler - .word RTC_WKUP_IRQHandler - .word FLASH_IRQHandler - .word RCC_IRQHandler - .word EXTI0_IRQHandler - .word EXTI1_IRQHandler - .word EXTI2_IRQHandler - .word EXTI3_IRQHandler - .word EXTI4_IRQHandler - .word DMA1_Channel1_IRQHandler - .word DMA1_Channel2_IRQHandler - .word DMA1_Channel3_IRQHandler - .word DMA1_Channel4_IRQHandler - .word DMA1_Channel5_IRQHandler - .word DMA1_Channel6_IRQHandler - .word DMA1_Channel7_IRQHandler - .word ADC1_IRQHandler - .word USB_HP_IRQHandler - .word USB_LP_IRQHandler - .word C2SEV_PWR_C2H_IRQHandler - .word COMP_IRQHandler - .word EXTI9_5_IRQHandler - .word TIM1_BRK_IRQHandler - .word TIM1_UP_TIM16_IRQHandler - .word TIM1_TRG_COM_TIM17_IRQHandler - .word TIM1_CC_IRQHandler - .word TIM2_IRQHandler - .word PKA_IRQHandler - .word I2C1_EV_IRQHandler - .word I2C1_ER_IRQHandler - .word I2C3_EV_IRQHandler - .word I2C3_ER_IRQHandler - .word SPI1_IRQHandler - .word SPI2_IRQHandler - .word USART1_IRQHandler - .word LPUART1_IRQHandler - .word SAI1_IRQHandler - .word TSC_IRQHandler - .word EXTI15_10_IRQHandler - .word RTC_Alarm_IRQHandler - .word CRS_IRQHandler - .word PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler - .word IPCC_C1_RX_IRQHandler - .word IPCC_C1_TX_IRQHandler - .word HSEM_IRQHandler - .word LPTIM1_IRQHandler - .word LPTIM2_IRQHandler - .word LCD_IRQHandler - .word QUADSPI_IRQHandler - .word AES1_IRQHandler - .word AES2_IRQHandler - .word RNG_IRQHandler - .word FPU_IRQHandler - .word DMA2_Channel1_IRQHandler - .word DMA2_Channel2_IRQHandler - .word DMA2_Channel3_IRQHandler - .word DMA2_Channel4_IRQHandler - .word DMA2_Channel5_IRQHandler - .word DMA2_Channel6_IRQHandler - .word DMA2_Channel7_IRQHandler - .word DMAMUX1_OVR_IRQHandler + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_PVM_IRQHandler + .word TAMP_STAMP_LSECSS_IRQHandler + .word RTC_WKUP_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_IRQHandler + .word USB_HP_IRQHandler + .word USB_LP_IRQHandler + .word C2SEV_PWR_C2H_IRQHandler + .word COMP_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_TIM16_IRQHandler + .word TIM1_TRG_COM_TIM17_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word PKA_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C3_EV_IRQHandler + .word I2C3_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word LPUART1_IRQHandler + .word SAI1_IRQHandler + .word TSC_IRQHandler + .word EXTI15_10_IRQHandler + .word RTC_Alarm_IRQHandler + .word CRS_IRQHandler + .word PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler + .word IPCC_C1_RX_IRQHandler + .word IPCC_C1_TX_IRQHandler + .word HSEM_IRQHandler + .word LPTIM1_IRQHandler + .word LPTIM2_IRQHandler + .word LCD_IRQHandler + .word QUADSPI_IRQHandler + .word AES1_IRQHandler + .word AES2_IRQHandler + .word RNG_IRQHandler + .word FPU_IRQHandler + .word DMA2_Channel1_IRQHandler + .word DMA2_Channel2_IRQHandler + .word DMA2_Channel3_IRQHandler + .word DMA2_Channel4_IRQHandler + .word DMA2_Channel5_IRQHandler + .word DMA2_Channel6_IRQHandler + .word DMA2_Channel7_IRQHandler + .word DMAMUX1_OVR_IRQHandler /******************************************************************************* * @@ -225,220 +227,220 @@ g_pfnVectors: * this definition. * *******************************************************************************/ - .weak NMI_Handler - .thumb_set NMI_Handler,Default_Handler - - .weak HardFault_Handler - .thumb_set HardFault_Handler,Default_Handler + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler - .weak MemManage_Handler - .thumb_set MemManage_Handler,Default_Handler + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler - .weak BusFault_Handler - .thumb_set BusFault_Handler,Default_Handler + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler - .weak UsageFault_Handler - .thumb_set UsageFault_Handler,Default_Handler + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler - .weak SVC_Handler - .thumb_set SVC_Handler,Default_Handler + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler - .weak DebugMon_Handler - .thumb_set DebugMon_Handler,Default_Handler + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler - .weak PendSV_Handler - .thumb_set PendSV_Handler,Default_Handler + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler - .weak SysTick_Handler - .thumb_set SysTick_Handler,Default_Handler + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler - .weak WWDG_IRQHandler - .thumb_set WWDG_IRQHandler,Default_Handler + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler - .weak PVD_PVM_IRQHandler - .thumb_set PVD_PVM_IRQHandler,Default_Handler + .weak PVD_PVM_IRQHandler + .thumb_set PVD_PVM_IRQHandler,Default_Handler - .weak TAMP_STAMP_LSECSS_IRQHandler - .thumb_set TAMP_STAMP_LSECSS_IRQHandler,Default_Handler + .weak TAMP_STAMP_LSECSS_IRQHandler + .thumb_set TAMP_STAMP_LSECSS_IRQHandler,Default_Handler - .weak RTC_WKUP_IRQHandler - .thumb_set RTC_WKUP_IRQHandler,Default_Handler + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler - .weak FLASH_IRQHandler - .thumb_set FLASH_IRQHandler,Default_Handler + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler - .weak RCC_IRQHandler - .thumb_set RCC_IRQHandler,Default_Handler + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler - .weak EXTI0_IRQHandler - .thumb_set EXTI0_IRQHandler,Default_Handler + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler - .weak EXTI1_IRQHandler - .thumb_set EXTI1_IRQHandler,Default_Handler + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler - .weak EXTI2_IRQHandler - .thumb_set EXTI2_IRQHandler,Default_Handler + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler - .weak EXTI3_IRQHandler - .thumb_set EXTI3_IRQHandler,Default_Handler + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler - .weak EXTI4_IRQHandler - .thumb_set EXTI4_IRQHandler,Default_Handler + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler - .weak DMA1_Channel1_IRQHandler - .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler - .weak DMA1_Channel2_IRQHandler - .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler - .weak DMA1_Channel3_IRQHandler - .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler - .weak DMA1_Channel4_IRQHandler - .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler - .weak DMA1_Channel5_IRQHandler - .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler - .weak DMA1_Channel6_IRQHandler - .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler - .weak DMA1_Channel7_IRQHandler - .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler - .weak ADC1_IRQHandler - .thumb_set ADC1_IRQHandler,Default_Handler + .weak ADC1_IRQHandler + .thumb_set ADC1_IRQHandler,Default_Handler - .weak USB_HP_IRQHandler - .thumb_set USB_HP_IRQHandler,Default_Handler + .weak USB_HP_IRQHandler + .thumb_set USB_HP_IRQHandler,Default_Handler - .weak USB_LP_IRQHandler - .thumb_set USB_LP_IRQHandler,Default_Handler + .weak USB_LP_IRQHandler + .thumb_set USB_LP_IRQHandler,Default_Handler - .weak C2SEV_PWR_C2H_IRQHandler - .thumb_set C2SEV_PWR_C2H_IRQHandler,Default_Handler + .weak C2SEV_PWR_C2H_IRQHandler + .thumb_set C2SEV_PWR_C2H_IRQHandler,Default_Handler - .weak COMP_IRQHandler - .thumb_set COMP_IRQHandler,Default_Handler + .weak COMP_IRQHandler + .thumb_set COMP_IRQHandler,Default_Handler - .weak EXTI9_5_IRQHandler - .thumb_set EXTI9_5_IRQHandler,Default_Handler + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler - .weak TIM1_BRK_IRQHandler - .thumb_set TIM1_BRK_IRQHandler,Default_Handler + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler - .weak TIM1_UP_TIM16_IRQHandler - .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler - .weak TIM1_TRG_COM_TIM17_IRQHandler - .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler - .weak TIM1_CC_IRQHandler - .thumb_set TIM1_CC_IRQHandler,Default_Handler + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler - .weak TIM2_IRQHandler - .thumb_set TIM2_IRQHandler,Default_Handler + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler - .weak PKA_IRQHandler - .thumb_set PKA_IRQHandler,Default_Handler + .weak PKA_IRQHandler + .thumb_set PKA_IRQHandler,Default_Handler - .weak I2C1_EV_IRQHandler - .thumb_set I2C1_EV_IRQHandler,Default_Handler + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler - .weak I2C1_ER_IRQHandler - .thumb_set I2C1_ER_IRQHandler,Default_Handler + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler - .weak I2C3_EV_IRQHandler - .thumb_set I2C3_EV_IRQHandler,Default_Handler + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler - .weak I2C3_ER_IRQHandler - .thumb_set I2C3_ER_IRQHandler,Default_Handler + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler - .weak SPI1_IRQHandler - .thumb_set SPI1_IRQHandler,Default_Handler + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler - .weak SPI2_IRQHandler - .thumb_set SPI2_IRQHandler,Default_Handler + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler - .weak USART1_IRQHandler - .thumb_set USART1_IRQHandler,Default_Handler + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler - .weak LPUART1_IRQHandler - .thumb_set LPUART1_IRQHandler,Default_Handler + .weak LPUART1_IRQHandler + .thumb_set LPUART1_IRQHandler,Default_Handler - .weak SAI1_IRQHandler - .thumb_set SAI1_IRQHandler,Default_Handler + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler - .weak TSC_IRQHandler - .thumb_set TSC_IRQHandler,Default_Handler + .weak TSC_IRQHandler + .thumb_set TSC_IRQHandler,Default_Handler - .weak EXTI15_10_IRQHandler - .thumb_set EXTI15_10_IRQHandler,Default_Handler + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler - .weak RTC_Alarm_IRQHandler - .thumb_set RTC_Alarm_IRQHandler,Default_Handler + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler - .weak CRS_IRQHandler - .thumb_set CRS_IRQHandler,Default_Handler + .weak CRS_IRQHandler + .thumb_set CRS_IRQHandler,Default_Handler - .weak PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler - .thumb_set PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler,Default_Handler + .weak PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler + .thumb_set PWR_SOTF_BLEACT_802ACT_RFPHASE_IRQHandler,Default_Handler - .weak IPCC_C1_RX_IRQHandler - .thumb_set IPCC_C1_RX_IRQHandler,Default_Handler + .weak IPCC_C1_RX_IRQHandler + .thumb_set IPCC_C1_RX_IRQHandler,Default_Handler - .weak IPCC_C1_TX_IRQHandler - .thumb_set IPCC_C1_TX_IRQHandler,Default_Handler + .weak IPCC_C1_TX_IRQHandler + .thumb_set IPCC_C1_TX_IRQHandler,Default_Handler - .weak HSEM_IRQHandler - .thumb_set HSEM_IRQHandler,Default_Handler + .weak HSEM_IRQHandler + .thumb_set HSEM_IRQHandler,Default_Handler - .weak LPTIM1_IRQHandler - .thumb_set LPTIM1_IRQHandler,Default_Handler + .weak LPTIM1_IRQHandler + .thumb_set LPTIM1_IRQHandler,Default_Handler - .weak LPTIM2_IRQHandler - .thumb_set LPTIM2_IRQHandler,Default_Handler + .weak LPTIM2_IRQHandler + .thumb_set LPTIM2_IRQHandler,Default_Handler - .weak LCD_IRQHandler - .thumb_set LCD_IRQHandler,Default_Handler + .weak LCD_IRQHandler + .thumb_set LCD_IRQHandler,Default_Handler - .weak QUADSPI_IRQHandler - .thumb_set QUADSPI_IRQHandler,Default_Handler + .weak QUADSPI_IRQHandler + .thumb_set QUADSPI_IRQHandler,Default_Handler - .weak AES1_IRQHandler - .thumb_set AES1_IRQHandler,Default_Handler + .weak AES1_IRQHandler + .thumb_set AES1_IRQHandler,Default_Handler - .weak AES2_IRQHandler - .thumb_set AES2_IRQHandler,Default_Handler + .weak AES2_IRQHandler + .thumb_set AES2_IRQHandler,Default_Handler - .weak RNG_IRQHandler - .thumb_set RNG_IRQHandler,Default_Handler + .weak RNG_IRQHandler + .thumb_set RNG_IRQHandler,Default_Handler - .weak FPU_IRQHandler - .thumb_set FPU_IRQHandler,Default_Handler + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler - .weak DMA2_Channel1_IRQHandler - .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler - .weak DMA2_Channel2_IRQHandler - .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler - .weak DMA2_Channel3_IRQHandler - .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler - .weak DMA2_Channel4_IRQHandler - .thumb_set DMA2_Channel4_IRQHandler,Default_Handler + .weak DMA2_Channel4_IRQHandler + .thumb_set DMA2_Channel4_IRQHandler,Default_Handler - .weak DMA2_Channel5_IRQHandler - .thumb_set DMA2_Channel5_IRQHandler,Default_Handler + .weak DMA2_Channel5_IRQHandler + .thumb_set DMA2_Channel5_IRQHandler,Default_Handler - .weak DMA2_Channel6_IRQHandler - .thumb_set DMA2_Channel6_IRQHandler,Default_Handler + .weak DMA2_Channel6_IRQHandler + .thumb_set DMA2_Channel6_IRQHandler,Default_Handler - .weak DMA2_Channel7_IRQHandler - .thumb_set DMA2_Channel7_IRQHandler,Default_Handler + .weak DMA2_Channel7_IRQHandler + .thumb_set DMA2_Channel7_IRQHandler,Default_Handler - .weak DMAMUX1_OVR_IRQHandler - .thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler + .weak DMAMUX1_OVR_IRQHandler + .thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/firmware/targets/f7/furi_hal/furi_hal_gpio.c index 8c2aefe2d5f..a6f25d10ece 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f7/furi_hal/furi_hal_gpio.c @@ -68,25 +68,37 @@ void hal_gpio_init_ex( uint32_t exti_line = GET_EXTI_LINE(gpio->pin); // Configure gpio with interrupts disabled - __disable_irq(); + FURI_CRITICAL_ENTER(); + // Set gpio speed - if(speed == GpioSpeedLow) { + switch(speed) { + case GpioSpeedLow: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); - } else if(speed == GpioSpeedMedium) { + break; + case GpioSpeedMedium: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); - } else if(speed == GpioSpeedHigh) { + break; + case GpioSpeedHigh: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); - } else { + break; + case GpioSpeedVeryHigh: LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); + break; } + // Set gpio pull mode - if(pull == GpioPullNo) { + switch(pull) { + case GpioPullNo: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); - } else if(pull == GpioPullUp) { + break; + case GpioPullUp: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); - } else { + break; + case GpioPullDown: LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); + break; } + // Set gpio mode if(mode >= GpioModeInterruptRise) { // Set pin in interrupt mode @@ -100,93 +112,106 @@ void hal_gpio_init_ex( LL_EXTI_EnableIT_0_31(exti_line); LL_EXTI_EnableFallingTrig_0_31(exti_line); } - if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { + if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { LL_EXTI_EnableEvent_0_31(exti_line); LL_EXTI_EnableRisingTrig_0_31(exti_line); } - if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { + if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { LL_EXTI_EnableEvent_0_31(exti_line); LL_EXTI_EnableFallingTrig_0_31(exti_line); } } else { - // Disable interrupt if it was set + // Disable interrupts if set if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && LL_EXTI_IsEnabledIT_0_31(exti_line)) { LL_EXTI_DisableIT_0_31(exti_line); LL_EXTI_DisableRisingTrig_0_31(exti_line); LL_EXTI_DisableFallingTrig_0_31(exti_line); } + + // Prepare alternative part if any + if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { + // set alternate function + if(hal_gpio_get_pin_num(gpio) < 8) { + LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); + } else { + LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); + } + } + // Set not interrupt pin modes - if(mode == GpioModeInput) { + switch(mode) { + case GpioModeInput: LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); - } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { + break; + case GpioModeOutputPushPull: + LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); + break; + case GpioModeAltFunctionPushPull: LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); - } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + break; + case GpioModeOutputOpenDrain: + LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); + break; + case GpioModeAltFunctionOpenDrain: LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); - } else if(mode == GpioModeAnalog) { + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + break; + case GpioModeAnalog: LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); + break; + default: + break; } } - - if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { - // enable alternate mode - LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); - - // set alternate function - if(hal_gpio_get_pin_num(gpio) < 8) { - LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); - } else { - LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); - } - } - - __enable_irq(); + FURI_CRITICAL_EXIT(); } void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) { furi_assert(gpio); furi_assert(cb); - __disable_irq(); + FURI_CRITICAL_ENTER(); uint8_t pin_num = hal_gpio_get_pin_num(gpio); furi_assert(gpio_interrupt[pin_num].callback == NULL); gpio_interrupt[pin_num].callback = cb; gpio_interrupt[pin_num].context = ctx; gpio_interrupt[pin_num].ready = true; - __enable_irq(); + FURI_CRITICAL_EXIT(); } void hal_gpio_enable_int_callback(const GpioPin* gpio) { furi_assert(gpio); - __disable_irq(); + FURI_CRITICAL_ENTER(); uint8_t pin_num = hal_gpio_get_pin_num(gpio); if(gpio_interrupt[pin_num].callback) { gpio_interrupt[pin_num].ready = true; } - __enable_irq(); + FURI_CRITICAL_EXIT(); } void hal_gpio_disable_int_callback(const GpioPin* gpio) { furi_assert(gpio); - __disable_irq(); + FURI_CRITICAL_ENTER(); uint8_t pin_num = hal_gpio_get_pin_num(gpio); gpio_interrupt[pin_num].ready = false; - __enable_irq(); + FURI_CRITICAL_EXIT(); } void hal_gpio_remove_int_callback(const GpioPin* gpio) { furi_assert(gpio); - __disable_irq(); + FURI_CRITICAL_ENTER(); uint8_t pin_num = hal_gpio_get_pin_num(gpio); gpio_interrupt[pin_num].callback = NULL; gpio_interrupt[pin_num].context = NULL; gpio_interrupt[pin_num].ready = false; - __enable_irq(); + FURI_CRITICAL_EXIT(); } static void hal_gpio_int_call(uint16_t pin_num) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_irda.c b/firmware/targets/f7/furi_hal/furi_hal_irda.c index 070e3c16f26..cd9847414a9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_irda.c +++ b/firmware/targets/f7/furi_hal/furi_hal_irda.c @@ -516,7 +516,7 @@ static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_ IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; furi_assert(buffer->polarity != NULL); - __disable_irq(); + FURI_CRITICAL_ENTER(); bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); if(channel_enabled) { LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); @@ -526,7 +526,7 @@ static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_ if(channel_enabled) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); } - __enable_irq(); + FURI_CRITICAL_EXIT(); } static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { @@ -536,7 +536,7 @@ static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { furi_assert(buffer->data != NULL); /* non-circular mode requires disabled channel before setup */ - __disable_irq(); + FURI_CRITICAL_ENTER(); bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); if(channel_enabled) { LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); @@ -546,7 +546,7 @@ static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { if(channel_enabled) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); } - __enable_irq(); + FURI_CRITICAL_EXIT(); } static void furi_hal_irda_async_tx_free_resources(void) { @@ -621,10 +621,10 @@ void furi_hal_irda_async_tx_start(uint32_t freq, float duty_cycle) { hal_gpio_init_ex( &gpio_irda_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); - __disable_irq(); + FURI_CRITICAL_ENTER(); LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ LL_TIM_EnableCounter(TIM1); - __enable_irq(); + FURI_CRITICAL_EXIT(); } void furi_hal_irda_async_tx_wait_termination(void) { @@ -642,9 +642,9 @@ void furi_hal_irda_async_tx_stop(void) { furi_assert(furi_hal_irda_state >= IrdaStateAsyncTx); furi_assert(furi_hal_irda_state < IrdaStateMAX); - __disable_irq(); + FURI_CRITICAL_ENTER(); if(furi_hal_irda_state == IrdaStateAsyncTx) furi_hal_irda_state = IrdaStateAsyncTxStopReq; - __enable_irq(); + FURI_CRITICAL_EXIT(); furi_hal_irda_async_tx_wait_termination(); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index 6ef5a9f4d17..3a6e5f38feb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -115,11 +115,11 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { } // Stop IRQ handling, no one should disturb us till we finish - __disable_irq(); + FURI_CRITICAL_ENTER(); // Confirm OS that sleep is still possible if(eTaskConfirmSleepModeStatus() == eAbortSleep) { - __enable_irq(); + FURI_CRITICAL_EXIT(); return; } @@ -136,7 +136,7 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { } // Reenable IRQ - __enable_irq(); + FURI_CRITICAL_EXIT(); } void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.c b/firmware/targets/f7/furi_hal/furi_hal_uart.c index 1ebba5d3d8d..2ee0cf85dd0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_uart.c +++ b/firmware/targets/f7/furi_hal/furi_hal_uart.c @@ -5,6 +5,7 @@ #include #include +#include static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); static void* irq_ctx[2]; @@ -33,13 +34,12 @@ static void furi_hal_usart_init(uint32_t baud) { USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; LL_USART_Init(USART1, &USART_InitStruct); - LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2); LL_USART_EnableFIFO(USART1); LL_USART_ConfigAsyncMode(USART1); LL_USART_Enable(USART1); - while(!LL_USART_IsActiveFlag_TEACK(USART1)) + while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) ; LL_USART_EnableIT_RXNE_RXFNE(USART1); @@ -70,13 +70,11 @@ static void furi_hal_lpuart_init(uint32_t baud) { LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; LL_LPUART_Init(LPUART1, &LPUART_InitStruct); - LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); - LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); LL_LPUART_EnableFIFO(LPUART1); LL_LPUART_Enable(LPUART1); - while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))) + while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) ; furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); diff --git a/firmware/targets/f7/target.mk b/firmware/targets/f7/target.mk index 0c9bd182e1b..767cbfa0a4d 100644 --- a/firmware/targets/f7/target.mk +++ b/firmware/targets/f7/target.mk @@ -14,7 +14,13 @@ FLASH_ADDRESS = 0x08000000 CFLAGS += -DNO_BOOTLOADER endif +DEBUG_RTOS_THREADS ?= 1 +ifeq ($(DEBUG_RTOS_THREADS), 1) OPENOCD_OPTS = -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init" +else +OPENOCD_OPTS = -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init" +endif + BOOT_CFLAGS = -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET) MCU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard diff --git a/lib/FreeRTOS-glue/cmsis_os2.c b/lib/FreeRTOS-glue/cmsis_os2.c index d1dcce86874..0f0c0f6ebdd 100644 --- a/lib/FreeRTOS-glue/cmsis_os2.c +++ b/lib/FreeRTOS-glue/cmsis_os2.c @@ -22,6 +22,8 @@ #include +#include + #include "cmsis_os2.h" // ::CMSIS:RTOS2 #include "cmsis_compiler.h" // Compiler agnostic definitions #include "os_tick.h" // OS Tick API @@ -455,11 +457,10 @@ uint32_t osKernelGetTickFreq (void) { Get the RTOS kernel system timer count. */ uint32_t osKernelGetSysTimerCount (void) { - uint32_t irqmask = IS_IRQ_MASKED(); TickType_t ticks; uint32_t val; - __disable_irq(); + FURI_CRITICAL_ENTER(); ticks = xTaskGetTickCount(); val = OS_Tick_GetCount(); @@ -471,9 +472,7 @@ uint32_t osKernelGetSysTimerCount (void) { } val += ticks * OS_Tick_GetInterval(); - if (irqmask == 0U) { - __enable_irq(); - } + FURI_CRITICAL_EXIT(); /* Return system timer count */ return (val); diff --git a/lib/cyfral/cyfral_emulator.h b/lib/cyfral/cyfral_emulator.h deleted file mode 100644 index 924198d4e31..00000000000 --- a/lib/cyfral/cyfral_emulator.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include - -class CyfralTiming { -public: - constexpr static const uint8_t ZERO_HIGH = 50; - constexpr static const uint8_t ZERO_LOW = 70; - constexpr static const uint8_t ONE_HIGH = 100; - constexpr static const uint8_t ONE_LOW = 70; -}; - -class CyfralEmulator { -private: - void send_nibble(uint8_t nibble); - void send_byte(uint8_t data); - inline void send_bit(bool bit); - const GpioPin* emulate_pin_record; - -public: - CyfralEmulator(const GpioPin* emulate_pin); - ~CyfralEmulator(); - void send(uint8_t* data, uint8_t count = 1, uint8_t repeat = 1); - void start(void); - void stop(void); -}; - -// 7 = 0 1 1 1 -// B = 1 0 1 1 -// D = 1 1 0 1 -// E = 1 1 1 0 - -void CyfralEmulator::send_nibble(uint8_t nibble) { - for(uint8_t i = 0; i < 4; i++) { - bool bit = nibble & (0b1000 >> i); - send_bit(bit); - } -} - -void CyfralEmulator::send_byte(uint8_t data) { - for(uint8_t i = 0; i < 8; i++) { - bool bit = data & (0b10000000 >> i); - send_bit(bit); - } -} - -void CyfralEmulator::send_bit(bool bit) { - if(!bit) { - hal_gpio_write(&ibutton_gpio, false); - delay_us(CyfralTiming::ZERO_LOW); - hal_gpio_write(&ibutton_gpio, true); - delay_us(CyfralTiming::ZERO_HIGH); - hal_gpio_write(&ibutton_gpio, false); - delay_us(CyfralTiming::ZERO_LOW); - } else { - hal_gpio_write(&ibutton_gpio, true); - delay_us(CyfralTiming::ONE_HIGH); - hal_gpio_write(&ibutton_gpio, false); - delay_us(CyfralTiming::ONE_LOW); - } -} - -CyfralEmulator::CyfralEmulator(const GpioPin* emulate_pin) { - emulate_pin_record = emulate_pin; -} - -CyfralEmulator::~CyfralEmulator() { -} - -void CyfralEmulator::send(uint8_t* data, uint8_t count, uint8_t repeat) { - osKernelLock(); - __disable_irq(); - - for(uint8_t i = 0; i < repeat; i++) { - // start sequence - send_nibble(0x01); - - // send data - for(uint8_t i = 0; i < count; i++) { - send_byte(data[i]); - } - } - - __enable_irq(); - osKernelUnlock(); -} - -void CyfralEmulator::start(void) { - hal_gpio_init(emulate_pin_record, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - hal_gpio_write(emulate_pin_record, false); -} - -void CyfralEmulator::stop(void) { - hal_gpio_init(emulate_pin_record, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} \ No newline at end of file diff --git a/lib/cyfral/cyfral_reader.h b/lib/cyfral/cyfral_reader.h deleted file mode 100644 index 498a4920007..00000000000 --- a/lib/cyfral/cyfral_reader.h +++ /dev/null @@ -1,272 +0,0 @@ -#pragma once -#include - -enum class CyfralReaderError : uint8_t { - NO_ERROR = 0, - UNABLE_TO_DETECT = 1, - RAW_DATA_SIZE_ERROR = 2, - UNKNOWN_NIBBLE_VALUE = 3, - NO_START_NIBBLE = 4, -}; - -class CyfralReader { -private: - ADC_HandleTypeDef adc_config; - ADC_TypeDef* adc_instance; - uint32_t adc_channel; - - void get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level); - void capture_data(bool* data, uint16_t capture_size, uint32_t line_min, uint32_t line_max); - bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count); - uint32_t search_array_in_array( - const bool* haystack, - const uint32_t haystack_size, - const bool* needle, - const uint32_t needle_size); - - // key is 9 nibbles - static const uint16_t bits_in_nibble = 4; - static const uint16_t key_length = 9; - static const uint32_t capture_size = key_length * bits_in_nibble * 2; - CyfralReaderError error; - -public: - CyfralReader(ADC_TypeDef* adc, uint32_t Channel); - ~CyfralReader(); - void start(void); - void stop(void); - bool read(uint8_t* data, uint8_t count); -}; - -void CyfralReader::get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level) { - uint32_t in = 0; - uint32_t min = UINT_MAX; - uint32_t max = 0; - - for(uint32_t i = 0; i < 256; i++) { - HAL_ADC_Start(&adc_config); - HAL_ADC_PollForConversion(&adc_config, 100); - in = HAL_ADC_GetValue(&adc_config); - if(in < min) min = in; - if(in > max) max = in; - } - - *min_level = min; - *max_level = max; -} - -void CyfralReader::capture_data( - bool* data, - uint16_t capture_size, - uint32_t line_min, - uint32_t line_max) { - uint32_t input_value = 0; - bool last_input_value = 0; - - uint32_t diff = line_max - line_min; - uint32_t mid = line_min + diff / 2; - - uint32_t low_threshold = mid - (diff / 4); - uint32_t high_threshold = mid - (diff / 4); - - uint16_t capture_position = 0; - uint32_t instructions_per_us = (SystemCoreClock / 1000000.0f); - uint32_t time_threshold = 75 * instructions_per_us; - uint32_t capture_max_time = 140 * (capture_size * 2) * instructions_per_us; - - uint32_t start = DWT->CYCCNT; - uint32_t end = DWT->CYCCNT; - - memset(data, 0, capture_size); - - osKernelLock(); - - uint32_t capture_start = DWT->CYCCNT; - while((capture_position < capture_size) && - ((DWT->CYCCNT - capture_start) < capture_max_time)) { - // read adc - HAL_ADC_Start(&adc_config); - HAL_ADC_PollForConversion(&adc_config, 100); - input_value = HAL_ADC_GetValue(&adc_config); - - // low to high transition - if((input_value > high_threshold) && last_input_value == 0) { - last_input_value = 1; - start = DWT->CYCCNT; - } - - // high to low transition - if((input_value < low_threshold) && last_input_value == 1) { - last_input_value = 0; - end = DWT->CYCCNT; - - // check transition time - if(end - start < time_threshold) { - data[capture_position] = 1; - capture_position++; - } else { - data[capture_position] = 0; - capture_position++; - } - } - } - - osKernelUnlock(); -} - -uint32_t CyfralReader::search_array_in_array( - const bool* haystack, - const uint32_t haystack_size, - const bool* needle, - const uint32_t needle_size) { - uint32_t haystack_index = 0, needle_index = 0; - - while(haystack_index < haystack_size && needle_index < needle_size) { - if(haystack[haystack_index] == needle[needle_index]) { - haystack_index++; - needle_index++; - if(needle_index == needle_size) { - return (haystack_index - needle_size); - }; - } else { - haystack_index = haystack_index - needle_index + 1; - needle_index = 0; - } - } - - return haystack_index; -} - -bool CyfralReader::parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count) { - const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0}; - uint32_t start_position = - search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble); - uint32_t end_position = 0; - - memset(data, 0, count); - - if(start_position < capture_size) { - start_position = start_position + bits_in_nibble; - end_position = start_position + count * 2 * bits_in_nibble; - - if(end_position >= capture_size) { - error = CyfralReaderError::RAW_DATA_SIZE_ERROR; - return false; - } - - bool first_nibble = true; - uint8_t data_position = 0; - uint8_t nibble_value = 0; - - while(data_position < count) { - nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 | - !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3]; - - switch(nibble_value) { - case(0x7): - case(0xB): - case(0xD): - case(0xE): - break; - default: - error = CyfralReaderError::UNKNOWN_NIBBLE_VALUE; - return false; - break; - } - - if(first_nibble) { - data[data_position] |= nibble_value << 4; - } else { - data[data_position] |= nibble_value; - } - - first_nibble = !first_nibble; - - if(first_nibble) { - data_position++; - } - - start_position = start_position + bits_in_nibble; - } - - error = CyfralReaderError::NO_ERROR; - return true; - } - - error = CyfralReaderError::NO_START_NIBBLE; - return false; -} - -CyfralReader::CyfralReader(ADC_TypeDef* adc, uint32_t channel) { - adc_instance = adc; - adc_channel = channel; -} - -CyfralReader::~CyfralReader() { -} - -void CyfralReader::start(void) { - ADC_ChannelConfTypeDef sConfig = {0}; - - // init ADC - adc_config.Instance = adc_instance; - adc_config.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; - adc_config.Init.Resolution = ADC_RESOLUTION_12B; - adc_config.Init.DataAlign = ADC_DATAALIGN_RIGHT; - adc_config.Init.ScanConvMode = ADC_SCAN_DISABLE; - adc_config.Init.EOCSelection = ADC_EOC_SINGLE_CONV; - adc_config.Init.LowPowerAutoWait = DISABLE; - adc_config.Init.ContinuousConvMode = DISABLE; - adc_config.Init.NbrOfConversion = 1; - adc_config.Init.DiscontinuousConvMode = DISABLE; - adc_config.Init.ExternalTrigConv = ADC_SOFTWARE_START; - adc_config.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; - adc_config.Init.DMAContinuousRequests = DISABLE; - adc_config.Init.Overrun = ADC_OVR_DATA_PRESERVED; - adc_config.Init.OversamplingMode = DISABLE; - if(HAL_ADC_Init(&adc_config) != HAL_OK) { - Error_Handler(); - } - - // init channel - sConfig.Channel = adc_channel; - sConfig.Rank = ADC_REGULAR_RANK_1; - sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; - sConfig.SingleDiff = ADC_SINGLE_ENDED; - sConfig.OffsetNumber = ADC_OFFSET_NONE; - sConfig.Offset = 0; - if(HAL_ADC_ConfigChannel(&adc_config, &sConfig) != HAL_OK) { - Error_Handler(); - } -} - -void CyfralReader::stop(void) { - HAL_ADC_DeInit(&adc_config); -} - -bool CyfralReader::read(uint8_t* data, uint8_t count) { - uint32_t line_level_min, line_level_max; - bool raw_data[capture_size]; - bool result = false; - error = CyfralReaderError::NO_ERROR; - - // calibrate - get_line_minmax(256, &line_level_min, &line_level_max); - - // TODO think about other detection method - // key not on line - if(line_level_max > 2000) { - error = CyfralReaderError::UNABLE_TO_DETECT; - return false; - } - - // capturing raw data consisting of bits - capture_data(raw_data, capture_size, line_level_min, line_level_max); - - // parse captured data - if(parse_data(raw_data, capture_size, data, count)) { - result = true; - } - - return result; -} \ No newline at end of file diff --git a/lib/cyfral/cyfral_reader_comp.h b/lib/cyfral/cyfral_reader_comp.h deleted file mode 100644 index 023fadbf50e..00000000000 --- a/lib/cyfral/cyfral_reader_comp.h +++ /dev/null @@ -1,283 +0,0 @@ -#pragma once -#include -#include "callback-connector.h" -#include - -enum class CyfralReaderCompError : uint8_t { - NO_ERROR = 0, - UNABLE_TO_DETECT = 1, - RAW_DATA_SIZE_ERROR = 2, - UNKNOWN_NIBBLE_VALUE = 3, - NO_START_NIBBLE = 4, - NOT_ENOUGH_DATA = 5, -}; - -extern COMP_HandleTypeDef hcomp1; - -typedef struct { - bool value; - uint32_t dwt_value; -} CompEvent; - -class CyfralReaderComp { -private: - bool capture_data(bool* data, uint16_t capture_size); - bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count); - uint32_t search_array_in_array( - const bool* haystack, - const uint32_t haystack_size, - const bool* needle, - const uint32_t needle_size); - - // key is 9 nibbles - static const uint16_t bits_in_nibble = 4; - static const uint16_t key_length = 9; - static const uint32_t capture_size = key_length * bits_in_nibble * 2; - CyfralReaderCompError error; - const GpioPin* pin_record; - - std::atomic ready_to_process; - void comparator_trigger_callback(void* hcomp, void* comp_ctx); - osMessageQueueId_t comp_event_queue; - -public: - CyfralReaderComp(const GpioPin* emulate_pin); - ~CyfralReaderComp(); - void start(void); - void stop(void); - bool read(uint8_t* data, uint8_t count); -}; - -bool CyfralReaderComp::capture_data(bool* data, uint16_t capture_size) { - uint32_t prev_timing = 0; - uint16_t data_index = 0; - CompEvent event_0, event_1; - osStatus_t status; - - // read first event to get initial timing - status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); - - if(status != osOK) { - return false; - } - - prev_timing = event_0.dwt_value; - - // read second event until we get 0 - while(1) { - status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); - if(status != osOK) { - return false; - } - prev_timing = event_0.dwt_value; - if(event_0.value == 0) break; - } - - while(1) { - // if event "zero" correct - if(status == osOK && event_0.value == 0) { - // get timing - event_0.dwt_value -= prev_timing; - prev_timing += event_0.dwt_value; - - // read next event - status = osMessageQueueGet(comp_event_queue, &event_1, NULL, 0); - - // if event "one" correct - if(status == osOK && event_1.value == 1) { - // get timing - event_1.dwt_value -= prev_timing; - prev_timing += event_1.dwt_value; - - // calculate percentage of event "one" to full timing - uint32_t full_timing = event_0.dwt_value + event_1.dwt_value; - uint32_t percentage_1 = 1000000 / full_timing * event_1.dwt_value; - - // write captured data - data[data_index] = percentage_1 > 500000 ? 0 : 1; - data_index++; - if(data_index >= capture_size) return true; - - status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); - } else { - return false; - } - } else { - return false; - } - } - - osMessageQueueReset(comp_event_queue); -} - -uint32_t CyfralReaderComp::search_array_in_array( - const bool* haystack, - const uint32_t haystack_size, - const bool* needle, - const uint32_t needle_size) { - uint32_t haystack_index = 0, needle_index = 0; - - while(haystack_index < haystack_size && needle_index < needle_size) { - if(haystack[haystack_index] == needle[needle_index]) { - haystack_index++; - needle_index++; - if(needle_index == needle_size) { - return (haystack_index - needle_size); - }; - } else { - haystack_index = haystack_index - needle_index + 1; - needle_index = 0; - } - } - - return haystack_index; -} - -void CyfralReaderComp::comparator_trigger_callback(void* hcomp, void* comp_ctx) { - CyfralReaderComp* _this = static_cast(comp_ctx); - COMP_HandleTypeDef* _hcomp = static_cast(hcomp); - - // check that hw is comparator 1 - if(_hcomp != &hcomp1) return; - - // if queue if not full - if(_this->ready_to_process == false) { - // send event to queue - CompEvent event; - // TOOD F4 and F5 differ - event.value = (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_LOW); - event.dwt_value = DWT->CYCCNT; - osStatus_t status = osMessageQueuePut(_this->comp_event_queue, &event, 0, 0); - - // queue is full, so we need to process data - if(status != osOK) { - _this->ready_to_process = true; - }; - } -} - -bool CyfralReaderComp::parse_data( - bool* raw_data, - uint16_t capture_size, - uint8_t* data, - uint8_t count) { - const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0}; - uint32_t start_position = - search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble); - uint32_t end_position = 0; - - memset(data, 0, count); - - if(start_position < capture_size) { - start_position = start_position + bits_in_nibble; - end_position = start_position + count * 2 * bits_in_nibble; - - if(end_position >= capture_size) { - error = CyfralReaderCompError::RAW_DATA_SIZE_ERROR; - return false; - } - - bool first_nibble = true; - uint8_t data_position = 0; - uint8_t nibble_value = 0; - - while(data_position < count) { - nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 | - !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3]; - - switch(nibble_value) { - case(0x7): - case(0xB): - case(0xD): - case(0xE): - break; - default: - error = CyfralReaderCompError::UNKNOWN_NIBBLE_VALUE; - return false; - break; - } - - if(first_nibble) { - data[data_position] |= nibble_value << 4; - } else { - data[data_position] |= nibble_value; - } - - first_nibble = !first_nibble; - - if(first_nibble) { - data_position++; - } - - start_position = start_position + bits_in_nibble; - } - - error = CyfralReaderCompError::NO_ERROR; - return true; - } - - error = CyfralReaderCompError::NO_START_NIBBLE; - return false; -} - -CyfralReaderComp::CyfralReaderComp(const GpioPin* gpio_pin) { - pin_record = gpio_pin; -} - -CyfralReaderComp::~CyfralReaderComp() { -} - -void CyfralReaderComp::start(void) { - // pulldown lf-rfid pins to prevent interference - // TODO open record - GpioPin rfid_pull_pin = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; - hal_gpio_init((GpioPin*)&rfid_pull_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - hal_gpio_write((GpioPin*)&rfid_pull_pin, false); - - // TODO open record - GpioPin rfid_out_pin = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; - hal_gpio_init((GpioPin*)&rfid_out_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - hal_gpio_write((GpioPin*)&rfid_out_pin, false); - - // connect comparator callback - void* comp_ctx = this; - comp_event_queue = osMessageQueueNew(capture_size * 2 + 2, sizeof(CompEvent), NULL); - ready_to_process = false; - - auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback); - api_interrupt_add(cmp_cb, InterruptTypeComparatorTrigger, comp_ctx); - - // start comaparator - HAL_COMP_Start(&hcomp1); -} - -void CyfralReaderComp::stop(void) { - // stop comaparator - HAL_COMP_Stop(&hcomp1); - - // disconnect comparator callback - auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback); - api_interrupt_remove(cmp_cb, InterruptTypeComparatorTrigger); - osMessageQueueDelete(comp_event_queue); -} - -bool CyfralReaderComp::read(uint8_t* data, uint8_t count) { - bool raw_data[capture_size]; - bool result = false; - error = CyfralReaderCompError::NO_ERROR; - - if(ready_to_process == false) { - error = CyfralReaderCompError::NOT_ENOUGH_DATA; - } else { - memset(raw_data, 0, sizeof(bool) * capture_size); - if(capture_data(raw_data, capture_size)) { - if(parse_data(raw_data, capture_size, data, count)) { - result = true; - } - } - - ready_to_process = false; - } - - return result; -} \ No newline at end of file diff --git a/lib/onewire/blanks_writer.cpp b/lib/onewire/blanks_writer.cpp deleted file mode 100644 index dc01cb34a0a..00000000000 --- a/lib/onewire/blanks_writer.cpp +++ /dev/null @@ -1,320 +0,0 @@ -#include "blanks_writer.h" - -class RW1990_1 { -public: - constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xD1; - constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0xB5; - constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; -}; - -class RW1990_2 { -public: - constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0x1D; - constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0x1E; - constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; -}; - -class TM2004 { -public: - constexpr static const uint8_t CMD_READ_STATUS = 0xAA; - constexpr static const uint8_t CMD_READ_MEMORY = 0xF0; - constexpr static const uint8_t CMD_WRITE_ROM = 0x3C; - constexpr static const uint8_t CMD_FINALIZATION = 0x35; - - constexpr static const uint8_t ANSWER_READ_MEMORY = 0xF5; -}; - -class TM01 { -public: - constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xC1; - constexpr static const uint8_t CMD_WRITE_ROM = 0xC5; - constexpr static const uint8_t CMD_SWITCH_TO_CYFRAL = 0xCA; - constexpr static const uint8_t CMD_SWITCH_TO_METAKOM = 0xCB; -}; - -class DS1990 { -public: - constexpr static const uint8_t CMD_READ_ROM = 0x33; -}; - -#include -#include -#include -#include - -void BlanksWriter::onewire_release(void) { - hal_gpio_write(gpio, true); -} - -void BlanksWriter::onewire_write_one_bit(bool value, uint32_t delay = 10000) { - onewire->write_bit(value); - delay_us(delay); - onewire_release(); -} - -BlanksWriter::BlanksWriter(const GpioPin* one_wire_gpio) { - gpio = one_wire_gpio; - onewire = new OneWireMaster(gpio); -} - -BlanksWriter::~BlanksWriter() { - free(onewire); -} - -WriterResult BlanksWriter::write(KeyType type, const uint8_t* key, uint8_t key_length) { - uint8_t write_result = -1; - WriterResult result = WR_ERROR; - - bool same_key = false; - - osKernelLock(); - bool presence = onewire->reset(); - osKernelUnlock(); - - if(presence) { - switch(type) { - case KeyType::KEY_DS1990: - same_key = compare_key_ds1990(key, key_length); - - if(!same_key) { - // currently we can write: - // RW1990, TM08v2, TM08vi-2 by write_1990_1() - // RW2004, RW2004 with EEPROM by write_TM2004(); - - if(write_result != 1) { - write_result = write_1990_1(key, key_length); - } - if(write_result != 1) { - write_result = write_1990_2(key, key_length); - } - if(write_result != 1) { - write_result = write_TM2004(key, key_length); - } - - if(write_result == 1) { - result = WR_OK; - } else if(write_result == 0) { - result = WR_ERROR; - } - } else { - write_result = 0; - result = WR_SAME_KEY; - } - break; - - default: - break; - } - } - - return result; -} - -bool BlanksWriter::write_TM2004(const uint8_t* key, uint8_t key_length) { - uint8_t answer; - bool result = true; - - osKernelLock(); - __disable_irq(); - - // write rom, addr is 0x0000 - onewire->reset(); - onewire->write(TM2004::CMD_WRITE_ROM); - onewire->write(0x00); - onewire->write(0x00); - - // write key - for(uint8_t i = 0; i < key_length; i++) { - // write key byte - onewire->write(key[i]); - answer = onewire->read(); - // TODO: check answer CRC - - // pulse indicating that data is correct - delay_us(600); - onewire_write_one_bit(1, 50000); - - // read writed key byte - answer = onewire->read(); - - // check that writed and readed are same - if(key[i] != answer) { - result = false; - break; - } - } - - onewire->reset(); - - __enable_irq(); - osKernelUnlock(); - - return result; -} - -bool BlanksWriter::write_1990_1(const uint8_t* key, uint8_t key_length) { - bool result = true; - - osKernelLock(); - __disable_irq(); - - // unlock - onewire->reset(); - onewire->write(RW1990_1::CMD_WRITE_RECORD_FLAG); - delay_us(10); - onewire_write_one_bit(0, 5000); - - // write key - onewire->reset(); - onewire->write(RW1990_1::CMD_WRITE_ROM); - for(uint8_t i = 0; i < key_length; i++) { - // inverted key for RW1990.1 - write_byte_ds1990(~key[i]); - delay_us(30000); - } - - // lock - onewire->write(RW1990_1::CMD_WRITE_RECORD_FLAG); - onewire_write_one_bit(1); - - __enable_irq(); - osKernelUnlock(); - - if(!compare_key_ds1990(key, key_length)) { - result = false; - } - - return result; -} - -bool BlanksWriter::write_1990_2(const uint8_t* key, uint8_t key_length) { - bool result = true; - - osKernelLock(); - __disable_irq(); - - // unlock - onewire->reset(); - onewire->write(RW1990_2::CMD_WRITE_RECORD_FLAG); - delay_us(10); - onewire_write_one_bit(1, 5000); - - // write key - onewire->reset(); - onewire->write(RW1990_2::CMD_WRITE_ROM); - for(uint8_t i = 0; i < key_length; i++) { - write_byte_ds1990(key[i]); - delay_us(30000); - } - - // lock - onewire->write(RW1990_2::CMD_WRITE_RECORD_FLAG); - onewire_write_one_bit(0); - - __enable_irq(); - osKernelUnlock(); - - if(!compare_key_ds1990(key, key_length)) { - result = false; - } - - return result; -} - -// TODO: untested -bool BlanksWriter::write_TM01(KeyType type, const uint8_t* key, uint8_t key_length) { - bool result = true; - - osKernelLock(); - __disable_irq(); - - // unlock - onewire->reset(); - onewire->write(TM01::CMD_WRITE_RECORD_FLAG); - onewire_write_one_bit(1, 10000); - - // write key - onewire->reset(); - onewire->write(TM01::CMD_WRITE_ROM); - - // TODO: key types - //if(type == KEY_METAKOM || type == KEY_CYFRAL) { - //} else { - for(uint8_t i = 0; i < key_length; i++) { - write_byte_ds1990(key[i]); - delay_us(10000); - } - //} - - // lock - onewire->write(TM01::CMD_WRITE_RECORD_FLAG); - onewire_write_one_bit(0, 10000); - - __enable_irq(); - osKernelUnlock(); - - if(!compare_key_ds1990(key, key_length)) { - result = false; - } - - osKernelLock(); - __disable_irq(); - - if(type == KEY_METAKOM || type == KEY_CYFRAL) { - onewire->reset(); - if(type == KEY_CYFRAL) - onewire->write(TM01::CMD_SWITCH_TO_CYFRAL); - else - onewire->write(TM01::CMD_SWITCH_TO_METAKOM); - onewire_write_one_bit(1); - } - - __enable_irq(); - osKernelUnlock(); - - return result; -} - -void BlanksWriter::write_byte_ds1990(uint8_t data) { - for(uint8_t n_bit = 0; n_bit < 8; n_bit++) { - onewire->write_bit(data & 1); - onewire_release(); - delay_us(5000); - data = data >> 1; - } -} - -bool BlanksWriter::compare_key_ds1990(const uint8_t* key, uint8_t key_length) { - uint8_t buff[key_length]; - bool result = false; - - osKernelLock(); - bool presence = onewire->reset(); - osKernelUnlock(); - - if(presence) { - osKernelLock(); - __disable_irq(); - onewire->write(DS1990::CMD_READ_ROM); - onewire->read_bytes(buff, key_length); - __enable_irq(); - osKernelUnlock(); - - result = true; - for(uint8_t i = 0; i < 8; i++) { - if(key[i] != buff[i]) { - result = false; - break; - } - } - } - return result; -} - -void BlanksWriter::start() { - onewire->start(); -} - -void BlanksWriter::stop() { - onewire->stop(); -} \ No newline at end of file diff --git a/lib/onewire/blanks_writer.h b/lib/onewire/blanks_writer.h deleted file mode 100644 index 8b8f84cd47b..00000000000 --- a/lib/onewire/blanks_writer.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include "one_wire_master.h" -#include "maxim_crc.h" - -typedef enum { - KEY_DS1990, /**< DS1990 */ - KEY_CYFRAL, /**< CYFRAL*/ - KEY_METAKOM, /**< METAKOM */ -} KeyType; - -typedef enum { - WR_OK, - WR_SAME_KEY, - WR_ERROR, -} WriterResult; - -class BlanksWriter { -private: - const GpioPin* gpio; - OneWireMaster* onewire; - - void onewire_release(void); - void onewire_write_one_bit(bool value, uint32_t delay); - - bool write_TM2004(const uint8_t* key, uint8_t key_length); - bool write_1990_1(const uint8_t* key, uint8_t key_length); - bool write_1990_2(const uint8_t* key, uint8_t key_length); - bool write_TM01(KeyType type, const uint8_t* key, uint8_t key_length); - - void write_byte_ds1990(uint8_t data); - bool compare_key_ds1990(const uint8_t* key, uint8_t key_length); - -public: - BlanksWriter(const GpioPin* one_wire_gpio); - ~BlanksWriter(); - - WriterResult write(KeyType type, const uint8_t* key, uint8_t key_length); - void start(); - void stop(); -}; diff --git a/lib/onewire/one_wire_slave.cpp b/lib/onewire/one_wire_slave.cpp index ac8abc62f50..c0f50caa6c3 100644 --- a/lib/onewire/one_wire_slave.cpp +++ b/lib/onewire/one_wire_slave.cpp @@ -258,7 +258,7 @@ bool OneWireSlave::bus_start(void) { if(device == nullptr) { result = false; } else { - __disable_irq(); + FURI_CRITICAL_ENTER(); pin_init_opendrain_in_isr_ctx(); error = OneWireSlaveError::NO_ERROR; @@ -274,7 +274,7 @@ bool OneWireSlave::bus_start(void) { } pin_init_interrupt_in_isr_ctx(); - __enable_irq(); + FURI_CRITICAL_EXIT(); } return result; @@ -305,4 +305,4 @@ void OneWireSlave::exti_callback(void* _ctx) { //FALL event pulse_start = DWT->CYCCNT; } -} \ No newline at end of file +} From 2a52d2d6201e46cceaa9f610792be20bbec9bb09 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Thu, 10 Feb 2022 16:01:49 +0300 Subject: [PATCH 7/9] [FL-2216, FL-2233] Archive fixes (#987) * archive: badusb, u2f and various fixes * archive: delete confirmation * badusb: removed empty string check * string pointer check * FuriHal: insomnia overflow assert, fix double insomnia exit in ble. BadUsb: fix uncommitted model. * view update fixes in gpio, badusb, u2f Co-authored-by: Aleksandr Kutuzov --- applications/archive/archive.c | 6 ++ applications/archive/archive_i.h | 3 + applications/archive/helpers/archive_apps.c | 74 ++++++++++++++++++ applications/archive/helpers/archive_apps.h | 21 +++++ .../archive/helpers/archive_browser.c | 65 +++++++++++---- .../archive/helpers/archive_browser.h | 44 +++++++---- .../archive/helpers/archive_favorites.c | 42 +++++++--- .../archive/helpers/archive_favorites.h | 2 +- applications/archive/helpers/archive_files.c | 72 ++++++++++++----- applications/archive/helpers/archive_files.h | 15 +++- .../archive/scenes/archive_scene_browser.c | 45 +++++------ .../archive/scenes/archive_scene_config.h | 1 + .../archive/scenes/archive_scene_delete.c | 70 +++++++++++++++++ .../archive/scenes/archive_scene_rename.c | 5 +- .../archive/views/archive_browser_view.c | 21 +++-- .../archive/views/archive_browser_view.h | 2 + applications/bad_usb/bad_usb_app.c | 26 ++++-- applications/bad_usb/views/bad_usb_view.c | 6 +- applications/gpio/views/gpio_usb_uart.c | 2 +- applications/u2f/views/u2f_view.c | 2 +- assets/compiled/assets_icons.c | 8 ++ assets/compiled/assets_icons.h | 2 + assets/icons/Archive/badusb_10px.png | Bin 0 -> 576 bytes assets/icons/Archive/u2f_10px.png | Bin 0 -> 583 bytes firmware/targets/f6/ble_glue/ble_glue.c | 4 +- firmware/targets/f6/furi_hal/furi_hal_power.c | 2 + firmware/targets/f7/ble_glue/ble_glue.c | 4 +- firmware/targets/f7/furi_hal/furi_hal_power.c | 2 + 28 files changed, 430 insertions(+), 116 deletions(-) create mode 100644 applications/archive/helpers/archive_apps.c create mode 100644 applications/archive/helpers/archive_apps.h create mode 100644 applications/archive/scenes/archive_scene_delete.c create mode 100644 assets/icons/Archive/badusb_10px.png create mode 100644 assets/icons/Archive/u2f_10px.png diff --git a/applications/archive/archive.c b/applications/archive/archive.c index 8b4918a5b73..c2fe4c6d4d8 100644 --- a/applications/archive/archive.c +++ b/applications/archive/archive.c @@ -39,6 +39,10 @@ ArchiveApp* archive_alloc() { view_dispatcher_add_view( archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); + archive->widget = widget_alloc(); + view_dispatcher_add_view( + archive->view_dispatcher, ArchiveViewWidget, widget_get_view(archive->widget)); + return archive; } @@ -47,6 +51,8 @@ void archive_free(ArchiveApp* archive) { view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewBrowser); view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); + view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewWidget); + widget_free(archive->widget); view_dispatcher_free(archive->view_dispatcher); scene_manager_free(archive->scene_manager); browser_free(archive->browser); diff --git a/applications/archive/archive_i.h b/applications/archive/archive_i.h index b450bb9cf34..c11a1803c4c 100644 --- a/applications/archive/archive_i.h +++ b/applications/archive/archive_i.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "views/archive_browser_view.h" @@ -15,6 +16,7 @@ typedef enum { ArchiveViewBrowser, ArchiveViewTextInput, + ArchiveViewWidget, ArchiveViewTotal, } ArchiveViewEnum; @@ -24,6 +26,7 @@ struct ArchiveApp { SceneManager* scene_manager; ArchiveBrowserView* browser; TextInput* text_input; + Widget* widget; char text_store[MAX_NAME_LEN]; char file_extension[MAX_EXT_LEN + 1]; }; diff --git a/applications/archive/helpers/archive_apps.c b/applications/archive/helpers/archive_apps.c new file mode 100644 index 00000000000..9293d225ca1 --- /dev/null +++ b/applications/archive/helpers/archive_apps.c @@ -0,0 +1,74 @@ +#include "archive_files.h" +#include "archive_apps.h" +#include "archive_browser.h" + +static const char* known_apps[] = { + [ArchiveAppTypeU2f] = "u2f", +}; + +ArchiveAppTypeEnum archive_get_app_type(const char* path) { + for(size_t i = 0; i < SIZEOF_ARRAY(known_apps); i++) { + if(strncmp(path, known_apps[i], strlen(known_apps[i])) != STRING_FAILURE) { + return i; + } + } + return ArchiveAppTypeUnknown; +} + +bool archive_app_is_available(void* context, const char* path) { + furi_assert(path); + + ArchiveAppTypeEnum app = archive_get_app_type(path); + + if(app == ArchiveAppTypeU2f) { + FileWorker* file_worker = file_worker_alloc(true); + bool file_exists = false; + file_worker_is_file_exist(file_worker, "/any/u2f/key.u2f", &file_exists); + if(file_exists) { + file_worker_is_file_exist(file_worker, "/any/u2f/cnt.u2f", &file_exists); + } + file_worker_free(file_worker); + return file_exists; + } else { + return false; + } +} + +bool archive_app_read_dir(void* context, const char* path) { + furi_assert(context); + furi_assert(path); + ArchiveBrowserView* browser = context; + + ArchiveAppTypeEnum app = archive_get_app_type(path); + + if(app == ArchiveAppTypeU2f) { + archive_add_app_item(browser, "/app:u2f/U2F Token"); + return true; + } else { + return false; + } +} + +void archive_app_delete_file(void* context, const char* path) { + furi_assert(context); + furi_assert(path); + ArchiveBrowserView* browser = context; + + ArchiveAppTypeEnum app = archive_get_app_type(path); + bool res = false; + + if(app == ArchiveAppTypeU2f) { + FileWorker* file_worker = file_worker_alloc(true); + res = file_worker_remove(file_worker, "/any/u2f/key.u2f"); + res |= file_worker_remove(file_worker, "/any/u2f/cnt.u2f"); + file_worker_free(file_worker); + + if(archive_is_favorite("/app:u2f/U2F Token")) { + archive_favorites_delete("/app:u2f/U2F Token"); + } + } + + if(res) { + archive_file_array_rm_selected(browser); + } +} diff --git a/applications/archive/helpers/archive_apps.h b/applications/archive/helpers/archive_apps.h new file mode 100644 index 00000000000..5abc27a5be5 --- /dev/null +++ b/applications/archive/helpers/archive_apps.h @@ -0,0 +1,21 @@ +#pragma once + +typedef enum { + ArchiveAppTypeU2f, + ArchiveAppTypeUnknown, + ArchiveAppsTotal, +} ArchiveAppTypeEnum; + +static const ArchiveFileTypeEnum app_file_types[] = { + [ArchiveAppTypeU2f] = ArchiveFileTypeU2f, + [ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown, +}; + +static inline const ArchiveFileTypeEnum archive_get_app_filetype(ArchiveAppTypeEnum app) { + return app_file_types[app]; +} + +ArchiveAppTypeEnum archive_get_app_type(const char* path); +bool archive_app_is_available(void* context, const char* path); +bool archive_app_read_dir(void* context, const char* path); +void archive_app_delete_file(void* context, const char* path); diff --git a/applications/archive/helpers/archive_browser.c b/applications/archive/helpers/archive_browser.c index eb316c1eee5..c1e9cacb808 100644 --- a/applications/archive/helpers/archive_browser.c +++ b/applications/archive/helpers/archive_browser.c @@ -1,5 +1,7 @@ +#include "archive_files.h" +#include "archive_apps.h" #include "archive_browser.h" -#include "math.h" +#include void archive_update_offset(ArchiveBrowserView* browser) { furi_assert(browser); @@ -177,24 +179,53 @@ void archive_set_last_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { }); } -void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name) { +void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { + furi_assert(browser); + furi_assert(name); + + ArchiveFile_t item; + + string_t full_name; + + string_init_set(full_name, browser->path); + string_cat_printf(full_name, "/%s", name); + + char* app_name = strchr(string_get_cstr(full_name), ':'); + if(app_name == NULL) { + string_clear(full_name); + return; + } + + ArchiveFile_t_init(&item); + string_init_set_str(item.name, name); + set_file_type(&item, NULL, app_name + 1, true); + + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_push_back(model->files, item); + return false; + }); + ArchiveFile_t_clear(&item); + string_clear(full_name); +} + +void archive_add_file_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name) { furi_assert(browser); furi_assert(file_info); furi_assert(name); ArchiveFile_t item; - if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(browser)), name)) { + if(filter_by_extension(file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { ArchiveFile_t_init(&item); string_init_set_str(item.name, name); - set_file_type(&item, file_info); + set_file_type(&item, file_info, archive_get_path(browser), false); with_view_model( browser->view, (ArchiveBrowserViewModel * model) { files_array_push_back(model->files, item); return false; }); - ArchiveFile_t_clear(&item); } } @@ -208,8 +239,7 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { if(show) { ArchiveFile_t* selected = files_array_get(model->files, model->idx); - selected->fav = archive_is_favorite( - "%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name)); + selected->fav = archive_is_favorite("%s", string_get_cstr(selected->name)); } return true; @@ -245,12 +275,18 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { archive_set_tab(browser, tab); - if((tab != ArchiveTabFavorites && - !archive_dir_empty(browser, archive_get_default_path(tab))) || - (tab == ArchiveTabFavorites && !archive_favorites_count(browser))) { - if(tab != ArchiveTabBrowser) { - archive_switch_tab(browser, key); - } + const char* path = archive_get_default_path(tab); + bool tab_empty = true; + if(tab == ArchiveTabFavorites) { + if(archive_favorites_count(browser) > 0) tab_empty = false; + } else if(strncmp(path, "/app:", 5) == 0) { + if(archive_app_is_available(browser, path)) tab_empty = false; + } else { + if(archive_dir_not_empty(browser, archive_get_default_path(tab))) tab_empty = false; + } + + if((tab_empty) && (tab != ArchiveTabBrowser)) { + archive_switch_tab(browser, key); } else { with_view_model( browser->view, (ArchiveBrowserViewModel * model) { @@ -277,8 +313,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { return false; }); - string_cat(browser->path, "/"); - string_cat(browser->path, name); + string_set(browser->path, name); archive_switch_dir(browser, string_get_cstr(browser->path)); } diff --git a/applications/archive/helpers/archive_browser.h b/applications/archive/helpers/archive_browser.h index 44bce9140c6..6486967faaa 100644 --- a/applications/archive/helpers/archive_browser.h +++ b/applications/archive/helpers/archive_browser.h @@ -11,6 +11,8 @@ static const char* tab_default_paths[] = { [ArchiveTabSubGhz] = "/any/subghz", [ArchiveTabLFRFID] = "/any/lfrfid", [ArchiveTabIrda] = "/any/irda", + [ArchiveTabBadUsb] = "/any/badusb", + [ArchiveTabU2f] = "/app:u2f", [ArchiveTabBrowser] = "/any", }; @@ -20,30 +22,37 @@ static const char* known_ext[] = { [ArchiveFileTypeSubGhz] = ".sub", [ArchiveFileTypeLFRFID] = ".rfid", [ArchiveFileTypeIrda] = ".ir", + [ArchiveFileTypeBadUsb] = ".txt", + [ArchiveFileTypeU2f] = "?", + [ArchiveFileTypeFolder] = "?", + [ArchiveFileTypeUnknown] = "*", }; -static inline const char* get_tab_ext(ArchiveTabEnum tab) { - switch(tab) { - case ArchiveTabIButton: - return known_ext[ArchiveFileTypeIButton]; - case ArchiveTabNFC: - return known_ext[ArchiveFileTypeNFC]; - case ArchiveTabSubGhz: - return known_ext[ArchiveFileTypeSubGhz]; - case ArchiveTabLFRFID: - return known_ext[ArchiveFileTypeLFRFID]; - case ArchiveTabIrda: - return known_ext[ArchiveFileTypeIrda]; - default: - return "*"; - } +static const ArchiveFileTypeEnum known_type[] = { + [ArchiveTabFavorites] = ArchiveFileTypeUnknown, + [ArchiveTabIButton] = ArchiveFileTypeIButton, + [ArchiveTabNFC] = ArchiveFileTypeNFC, + [ArchiveTabSubGhz] = ArchiveFileTypeSubGhz, + [ArchiveTabLFRFID] = ArchiveFileTypeLFRFID, + [ArchiveTabIrda] = ArchiveFileTypeIrda, + [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, + [ArchiveTabU2f] = ArchiveFileTypeU2f, + [ArchiveTabBrowser] = ArchiveFileTypeUnknown, +}; + +static inline const ArchiveFileTypeEnum archive_get_tab_filetype(ArchiveTabEnum tab) { + return known_type[tab]; +} + +static inline const char* archive_get_tab_ext(ArchiveTabEnum tab) { + return known_ext[archive_get_tab_filetype(tab)]; } static inline const char* archive_get_default_path(ArchiveTabEnum tab) { return tab_default_paths[tab]; } -inline bool is_known_app(ArchiveFileTypeEnum type) { +inline bool archive_is_known_app(ArchiveFileTypeEnum type) { return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); } @@ -62,7 +71,8 @@ uint8_t archive_get_depth(ArchiveBrowserView* browser); const char* archive_get_path(ArchiveBrowserView* browser); const char* archive_get_name(ArchiveBrowserView* browser); -void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name); +void archive_add_app_item(ArchiveBrowserView* browser, const char* name); +void archive_add_file_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name); void archive_show_file_menu(ArchiveBrowserView* browser, bool show); void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active); diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c index b622e76fae4..97bf9149885 100644 --- a/applications/archive/helpers/archive_favorites.c +++ b/applications/archive/helpers/archive_favorites.c @@ -1,5 +1,7 @@ #include "archive_favorites.h" +#include "archive_files.h" +#include "archive_apps.h" #include "archive_browser.h" uint16_t archive_favorites_count(void* context) { @@ -46,10 +48,16 @@ static bool archive_favourites_rescan() { break; } - bool file_exists = false; - file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); - if(file_exists) { - archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); + if(string_search(buffer, "/app:") == 0) { + if(archive_app_is_available(NULL, string_get_cstr(buffer))) { + archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); + } + } else { + bool file_exists = false; + file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); + if(file_exists) { + archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); + } } } } @@ -88,13 +96,22 @@ bool archive_favorites_read(void* context) { break; } - bool file_exists = false; - file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); + if(string_search(buffer, "/app:") == 0) { + if(archive_app_is_available(browser, string_get_cstr(buffer))) { + archive_add_app_item(browser, string_get_cstr(buffer)); + } else { + need_refresh = true; + } + } else { + bool file_exists = false; + file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); + + if(file_exists) + archive_add_file_item(browser, &file_info, string_get_cstr(buffer)); + else + need_refresh = true; + } - if(file_exists) - archive_add_item(browser, &file_info, string_get_cstr(buffer)); - else - need_refresh = true; string_reset(buffer); } } @@ -185,8 +202,7 @@ bool archive_is_favorite(const char* format, ...) { return found; } -bool archive_favorites_rename(const char* file_path, const char* src, const char* dst) { - furi_assert(file_path); +bool archive_favorites_rename(const char* src, const char* dst) { furi_assert(src); furi_assert(dst); @@ -198,7 +214,7 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char string_init(buffer); string_init(path); - string_printf(path, "%s/%s", file_path, src); + string_printf(path, "%s", src); bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { diff --git a/applications/archive/helpers/archive_favorites.h b/applications/archive/helpers/archive_favorites.h index c028bd2b43f..3a517f4bc95 100644 --- a/applications/archive/helpers/archive_favorites.h +++ b/applications/archive/helpers/archive_favorites.h @@ -8,6 +8,6 @@ uint16_t archive_favorites_count(void* context); bool archive_favorites_read(void* context); bool archive_favorites_delete(const char* format, ...); bool archive_is_favorite(const char* format, ...); -bool archive_favorites_rename(const char* file_path, const char* src, const char* dst); +bool archive_favorites_rename(const char* src, const char* dst); void archive_add_to_favorites(const char* file_path); void archive_favorites_save(void* context); diff --git a/applications/archive/helpers/archive_files.c b/applications/archive/helpers/archive_files.c index a5bd2eb91ba..08542bfed87 100644 --- a/applications/archive/helpers/archive_files.c +++ b/applications/archive/helpers/archive_files.c @@ -1,8 +1,11 @@ #include "archive_files.h" +#include "archive_apps.h" #include "archive_browser.h" #define TAG "Archive" +#define ASSETS_DIR "assets" + bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { furi_assert(file_info); furi_assert(tab_ext); @@ -15,7 +18,11 @@ bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* n } else if(strstr(name, tab_ext) != NULL) { result = true; } else if(file_info->flags & FSF_DIRECTORY) { - result = true; + if(strstr(name, ASSETS_DIR) != NULL) { + result = false; // Skip assets folder in all tabs except browser + } else { + result = true; + } } return result; @@ -38,21 +45,36 @@ void archive_get_file_extension(char* name, char* ext) { strncpy(ext, dot, MAX_EXT_LEN); } -void set_file_type(ArchiveFile_t* file, FileInfo* file_info) { +void set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app) { furi_assert(file); - furi_assert(file_info); - for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) { - if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) { - file->type = i; - return; + file->is_app = is_app; + if(is_app) { + file->type = archive_get_app_filetype(archive_get_app_type(path)); + } else { + furi_assert(file_info); + + for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) { + if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; + if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) { + if(i == ArchiveFileTypeBadUsb) { + if(string_search_str(file->name, archive_get_default_path(ArchiveTabBadUsb)) == + 0) { + file->type = i; + return; // *.txt file is a BadUSB script only if it is in BadUSB folder + } + } else { + file->type = i; + return; + } + } } - } - if(file_info->flags & FSF_DIRECTORY) { - file->type = ArchiveFileTypeFolder; - } else { - file->type = ArchiveFileTypeUnknown; + if(file_info->flags & FSF_DIRECTORY) { + file->type = ArchiveFileTypeFolder; + } else { + file->type = ArchiveFileTypeUnknown; + } } } @@ -63,17 +85,21 @@ bool archive_get_filenames(void* context, const char* path) { ArchiveBrowserView* browser = context; archive_file_array_rm_all(browser); - if(archive_get_tab(browser) != ArchiveTabFavorites) { - res = archive_read_dir(browser, path); - } else { + if(archive_get_tab(browser) == ArchiveTabFavorites) { res = archive_favorites_read(browser); + } else if(strncmp(path, "/app:", 5) == 0) { + res = archive_app_read_dir(browser, path); + } else { + res = archive_read_dir(browser, path); } return res; } -bool archive_dir_empty(void* context, const char* path) { // can be simpler? +bool archive_dir_not_empty(void* context, const char* path) { // can be simpler? furi_assert(context); + ArchiveBrowserView* browser = context; + FileInfo file_info; Storage* fs_api = furi_record_open("storage"); File* directory = storage_file_alloc(fs_api); @@ -92,8 +118,11 @@ bool archive_dir_empty(void* context, const char* path) { // can be simpler? } if(files_found) { break; - } else if(storage_file_get_error(directory) == FSE_OK) { - files_found = name[0]; + } else if((storage_file_get_error(directory) == FSE_OK) && (name[0])) { + if(filter_by_extension( + &file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { + files_found = true; + } } else { return false; } @@ -114,6 +143,8 @@ bool archive_read_dir(void* context, const char* path) { Storage* fs_api = furi_record_open("storage"); File* directory = storage_file_alloc(fs_api); char name[MAX_NAME_LEN]; + snprintf(name, MAX_NAME_LEN, "%s/", path); + size_t path_len = strlen(name); size_t files_cnt = 0; if(!storage_dir_open(directory, path)) { @@ -123,13 +154,14 @@ bool archive_read_dir(void* context, const char* path) { } while(1) { - if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { + if(!storage_dir_read(directory, &file_info, &name[path_len], MAX_NAME_LEN - path_len)) { break; } + if(files_cnt > MAX_FILES) { break; } else if(storage_file_get_error(directory) == FSE_OK) { - archive_add_item(browser, &file_info, name); + archive_add_file_item(browser, &file_info, name); ++files_cnt; } else { storage_dir_close(directory); diff --git a/applications/archive/helpers/archive_files.h b/applications/archive/helpers/archive_files.h index 12fde2fb3fb..ff751d9b7de 100644 --- a/applications/archive/helpers/archive_files.h +++ b/applications/archive/helpers/archive_files.h @@ -9,29 +9,38 @@ typedef enum { ArchiveFileTypeSubGhz, ArchiveFileTypeLFRFID, ArchiveFileTypeIrda, + ArchiveFileTypeBadUsb, + ArchiveFileTypeU2f, ArchiveFileTypeFolder, ArchiveFileTypeUnknown, - AppIdTotal, + ArchiveFileTypesTotal, } ArchiveFileTypeEnum; typedef struct { string_t name; ArchiveFileTypeEnum type; bool fav; + bool is_app; } ArchiveFile_t; static void ArchiveFile_t_init(ArchiveFile_t* obj) { obj->type = ArchiveFileTypeUnknown; + obj->is_app = false; + obj->fav = false; string_init(obj->name); } static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { obj->type = src->type; + obj->is_app = src->is_app; + obj->fav = src->fav; string_init_set(obj->name, src->name); } static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { obj->type = src->type; + obj->is_app = src->is_app; + obj->fav = src->fav; string_set(obj->name, src->name); } @@ -48,11 +57,11 @@ ARRAY_DEF( CLEAR(API_2(ArchiveFile_t_clear)))) bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name); -void set_file_type(ArchiveFile_t* file, FileInfo* file_info); +void set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app); void archive_trim_file_path(char* name, bool ext); void archive_get_file_extension(char* name, char* ext); bool archive_get_filenames(void* context, const char* path); -bool archive_dir_empty(void* context, const char* path); +bool archive_dir_not_empty(void* context, const char* path); bool archive_read_dir(void* context, const char* path); void archive_file_append(const char* path, const char* format, ...); void archive_delete_file(void* context, const char* format, ...); \ No newline at end of file diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/archive/scenes/archive_scene_browser.c index 88a058bbc67..228bb305a89 100644 --- a/applications/archive/scenes/archive_scene_browser.c +++ b/applications/archive/scenes/archive_scene_browser.c @@ -1,5 +1,6 @@ #include "../archive_i.h" #include "../helpers/archive_files.h" +#include "../helpers/archive_apps.h" #include "../helpers/archive_favorites.h" #include "../helpers/archive_browser.h" #include "../views/archive_browser_view.h" @@ -12,29 +13,29 @@ static const char* flipper_app_name[] = { [ArchiveFileTypeSubGhz] = "Sub-GHz", [ArchiveFileTypeLFRFID] = "125 kHz RFID", [ArchiveFileTypeIrda] = "Infrared", + [ArchiveFileTypeBadUsb] = "Bad USB", + [ArchiveFileTypeU2f] = "U2F", }; -static void archive_run_in_app( - ArchiveBrowserView* browser, - ArchiveFile_t* selected, - bool full_path_provided) { +static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) { Loader* loader = furi_record_open("loader"); - string_t full_path; - if(!full_path_provided) { - string_init_printf( - full_path, "%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name)); + LoaderStatus status; + if(selected->is_app) { + char* param = strrchr(string_get_cstr(selected->name), '/'); + if(param != NULL) { + param++; + } + status = loader_start(loader, flipper_app_name[selected->type], param); } else { - string_init_set(full_path, selected->name); + status = loader_start( + loader, flipper_app_name[selected->type], string_get_cstr(selected->name)); } - LoaderStatus status = - loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); if(status != LoaderStatusOk) { FURI_LOG_E(TAG, "loader_start failed: %d", status); } - string_clear(full_path); furi_record_close("loader"); } @@ -57,9 +58,8 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { ArchiveBrowserView* browser = archive->browser; ArchiveFile_t* selected = archive_get_current_file(browser); - const char* path = archive_get_path(browser); const char* name = archive_get_name(browser); - bool known_app = is_known_app(selected->type); + bool known_app = archive_is_known_app(selected->type); bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; bool consumed = false; @@ -75,7 +75,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { break; case ArchiveBrowserEventFileMenuRun: if(known_app) { - archive_run_in_app(browser, selected, favorites); + archive_run_in_app(browser, selected); } consumed = true; break; @@ -85,10 +85,10 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_file_array_rm_selected(browser); archive_show_file_menu(browser, false); } else if(known_app) { - if(archive_is_favorite("%s/%s", path, name)) { - archive_favorites_delete("%s/%s", path, name); + if(archive_is_favorite("%s", name)) { + archive_favorites_delete("%s", name); } else { - archive_file_append(ARCHIVE_FAV_PATH, "%s/%s\n", path, name); + archive_file_append(ARCHIVE_FAV_PATH, "%s\n", name); } archive_show_file_menu(browser, false); } @@ -98,18 +98,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { case ArchiveBrowserEventFileMenuAction: if(favorites) { browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); - } else if(known_app) { + } else if((known_app) && (selected->is_app == false)) { scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); } consumed = true; break; case ArchiveBrowserEventFileMenuDelete: - if(favorites) { - archive_delete_file(browser, "%s", name); - } else { - archive_delete_file(browser, "%s/%s", path, name); - } - archive_show_file_menu(browser, false); + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); consumed = true; break; case ArchiveBrowserEventEnterDir: diff --git a/applications/archive/scenes/archive_scene_config.h b/applications/archive/scenes/archive_scene_config.h index 2ed4e00c2de..4145f43dab7 100644 --- a/applications/archive/scenes/archive_scene_config.h +++ b/applications/archive/scenes/archive_scene_config.h @@ -1,2 +1,3 @@ ADD_SCENE(archive, browser, Browser) ADD_SCENE(archive, rename, Rename) +ADD_SCENE(archive, delete, Delete) diff --git a/applications/archive/scenes/archive_scene_delete.c b/applications/archive/scenes/archive_scene_delete.c new file mode 100644 index 00000000000..04b375107f8 --- /dev/null +++ b/applications/archive/scenes/archive_scene_delete.c @@ -0,0 +1,70 @@ +#include "../archive_i.h" +#include "../helpers/archive_favorites.h" +#include "../helpers/archive_files.h" +#include "../helpers/archive_apps.h" +#include "../helpers/archive_browser.h" + +#define SCENE_DELETE_CUSTOM_EVENT (0UL) +#define MAX_TEXT_INPUT_LEN 22 + +void archive_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void archive_scene_delete_on_enter(void* context) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", archive_scene_delete_widget_callback, app); + widget_add_button_element( + app->widget, GuiButtonTypeRight, "Delete", archive_scene_delete_widget_callback, app); + + ArchiveFile_t* current = archive_get_current_file(app->browser); + strlcpy(app->text_store, string_get_cstr(current->name), MAX_NAME_LEN); + char* name = strrchr(app->text_store, '/'); + if(name != NULL) { + name++; + } + + char delete_str[64]; + snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", name); + widget_add_text_box_element(app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget); +} + +bool archive_scene_delete_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + + ArchiveBrowserView* browser = app->browser; + ArchiveFile_t* selected = archive_get_current_file(browser); + const char* name = archive_get_name(browser); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + if(selected->is_app) { + archive_app_delete_file(browser, name); + } else { + archive_delete_file(browser, "%s", name); + } + archive_show_file_menu(browser, false); + return scene_manager_previous_scene(app->scene_manager); + } else if(event.event == GuiButtonTypeLeft) { + return scene_manager_previous_scene(app->scene_manager); + } + } + return false; +} + +void archive_scene_delete_on_exit(void* context) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + + widget_reset(app->widget); +} diff --git a/applications/archive/scenes/archive_scene_rename.c b/applications/archive/scenes/archive_scene_rename.c index 8f32eb8a851..9e5738017db 100644 --- a/applications/archive/scenes/archive_scene_rename.c +++ b/applications/archive/scenes/archive_scene_rename.c @@ -52,7 +52,8 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { const char* path = archive_get_path(archive->browser); const char* name = archive_get_name(archive->browser); - string_init_printf(buffer_src, "%s/%s", path, name); + string_init_printf(buffer_src, "%s", name); + //TODO: take path from src name string_init_printf(buffer_dst, "%s/%s", path, archive->text_store); // append extension @@ -64,7 +65,7 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { furi_record_close("storage"); if(file->fav) { - archive_favorites_rename(path, name, string_get_cstr(buffer_dst)); + archive_favorites_rename(name, string_get_cstr(buffer_dst)); } string_clear(buffer_src); diff --git a/applications/archive/views/archive_browser_view.c b/applications/archive/views/archive_browser_view.c index 525fbbcbb47..5e079436446 100644 --- a/applications/archive/views/archive_browser_view.c +++ b/applications/archive/views/archive_browser_view.c @@ -10,6 +10,8 @@ static const char* ArchiveTabNames[] = { [ArchiveTabSubGhz] = "Sub-GHz", [ArchiveTabLFRFID] = "RFID LF", [ArchiveTabIrda] = "Infrared", + [ArchiveTabBadUsb] = "Bad USB", + [ArchiveTabU2f] = "U2F", [ArchiveTabBrowser] = "Browser"}; static const Icon* ArchiveItemIcons[] = { @@ -18,6 +20,8 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeSubGhz] = &I_sub1_10px, [ArchiveFileTypeLFRFID] = &I_125_10px, [ArchiveFileTypeIrda] = &I_ir_10px, + [ArchiveFileTypeBadUsb] = &I_badusb_10px, + [ArchiveFileTypeU2f] = &I_u2f_10px, [ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px, }; @@ -47,15 +51,20 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveFile_t* selected = files_array_get(model->files, model->idx); - if(!is_known_app(selected->type)) { + if(!archive_is_known_app(selected->type)) { string_set_str(menu[0], "---"); string_set_str(menu[1], "---"); string_set_str(menu[2], "---"); - } else if(selected->fav) { - string_set_str(menu[1], "Unpin"); - } else if(model->tab_idx == ArchiveTabFavorites) { + } else { + if(model->tab_idx == ArchiveTabFavorites) { + string_set_str(menu[2], "Move"); + } else if(selected->is_app) { + string_set_str(menu[2], "---"); + } + } + + if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) { string_set_str(menu[1], "Unpin"); - string_set_str(menu[2], "Move"); } for(size_t i = 0; i < MENU_ITEMS; i++) { @@ -102,7 +111,7 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); - archive_trim_file_path(cstr_buff, is_known_app(file->type)); + archive_trim_file_path(cstr_buff, archive_is_known_app(file->type)); string_init_set_str(str_buff, cstr_buff); elements_string_fit_width( canvas, str_buff, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); diff --git a/applications/archive/views/archive_browser_view.h b/applications/archive/views/archive_browser_view.h index 634dec441cd..af4ed85ecc2 100644 --- a/applications/archive/views/archive_browser_view.h +++ b/applications/archive/views/archive_browser_view.h @@ -24,6 +24,8 @@ typedef enum { ArchiveTabNFC, ArchiveTabIrda, ArchiveTabIButton, + ArchiveTabBadUsb, + ArchiveTabU2f, ArchiveTabBrowser, ArchiveTabTotal, } ArchiveTabEnum; diff --git a/applications/bad_usb/bad_usb_app.c b/applications/bad_usb/bad_usb_app.c index c4a3c9301d8..498095624f4 100644 --- a/applications/bad_usb/bad_usb_app.c +++ b/applications/bad_usb/bad_usb_app.c @@ -2,6 +2,7 @@ #include #include #include +#include static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -39,27 +40,34 @@ static bool bad_usb_check_assets() { return ret; } -BadUsbApp* bad_usb_app_alloc() { +BadUsbApp* bad_usb_app_alloc(char* arg) { BadUsbApp* app = furi_alloc(sizeof(BadUsbApp)); + if(arg != NULL) { + string_t filename; + string_init(filename); + path_extract_filename_no_ext(arg, filename); + strncpy(app->file_name, string_get_cstr(filename), BAD_USB_FILE_NAME_LEN); + string_clear(filename); + } + app->gui = furi_record_open("gui"); app->notifications = furi_record_open("notification"); app->dialogs = furi_record_open("dialogs"); app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); view_dispatcher_set_tick_event_callback( app->view_dispatcher, bad_usb_app_tick_event_callback, 500); - view_dispatcher_set_custom_event_callback( app->view_dispatcher, bad_usb_app_custom_event_callback); view_dispatcher_set_navigation_event_callback( app->view_dispatcher, bad_usb_app_back_event_callback); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - // Custom Widget app->widget = widget_alloc(); view_dispatcher_add_view( @@ -69,7 +77,11 @@ BadUsbApp* bad_usb_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view)); - if(bad_usb_check_assets()) { + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + if(*app->file_name != '\0') { + scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); + } else if(bad_usb_check_assets()) { scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); } else { scene_manager_next_scene(app->scene_manager, BadUsbSceneError); @@ -106,7 +118,7 @@ int32_t bad_usb_app(void* p) { FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_set_config(&usb_hid); - BadUsbApp* bad_usb_app = bad_usb_app_alloc(); + BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p); view_dispatcher_run(bad_usb_app->view_dispatcher); diff --git a/applications/bad_usb/views/bad_usb_view.c b/applications/bad_usb/views/bad_usb_view.c index fecd2f7a71d..6d226ca7166 100644 --- a/applications/bad_usb/views/bad_usb_view.c +++ b/applications/bad_usb/views/bad_usb_view.c @@ -144,7 +144,7 @@ void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* c bad_usb->view, (BadUsbModel * model) { bad_usb->callback = callback; bad_usb->context = context; - return false; + return true; }); } @@ -153,7 +153,7 @@ void bad_usb_set_file_name(BadUsb* bad_usb, char* name) { with_view_model( bad_usb->view, (BadUsbModel * model) { model->file_name = name; - return false; + return true; }); } @@ -163,6 +163,6 @@ void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { bad_usb->view, (BadUsbModel * model) { memcpy(&(model->state), st, sizeof(BadUsbState)); model->anim_frame ^= 1; - return false; + return true; }); } diff --git a/applications/gpio/views/gpio_usb_uart.c b/applications/gpio/views/gpio_usb_uart.c index 3b2d1f38572..be80f019a67 100644 --- a/applications/gpio/views/gpio_usb_uart.c +++ b/applications/gpio/views/gpio_usb_uart.c @@ -151,6 +151,6 @@ void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUa model->rx_active = (model->rx_cnt != st->rx_cnt); model->tx_cnt = st->tx_cnt; model->rx_cnt = st->rx_cnt; - return false; + return true; }); } diff --git a/applications/u2f/views/u2f_view.c b/applications/u2f/views/u2f_view.c index c810d141087..8a6f441a661 100644 --- a/applications/u2f/views/u2f_view.c +++ b/applications/u2f/views/u2f_view.c @@ -96,6 +96,6 @@ void u2f_view_set_state(U2fView* u2f, U2fViewMsg msg) { with_view_model( u2f->view, (U2fModel * model) { model->display_msg = msg; - return false; + return true; }); } diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index 1d395b96e4b..4a779743efb 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -40,6 +40,9 @@ const uint8_t *_I_125_10px[] = {_I_125_10px_0}; const uint8_t _I_Nfc_10px_0[] = {0x00,0x80,0x00,0x00,0x01,0x22,0x02,0x43,0x02,0x45,0x02,0x49,0x02,0x31,0x02,0x22,0x02,0x00,0x01,0x80,0x00,}; const uint8_t *_I_Nfc_10px[] = {_I_Nfc_10px_0}; +const uint8_t _I_badusb_10px_0[] = {0x01,0x00,0x11,0x00,0x00,0x0f,0xe2,0x01,0xfc,0x80,0xdd,0x20,0x32,0x48,0x08,0x14,0x40,0x23,0xa8,0x08,0xa0,}; +const uint8_t *_I_badusb_10px[] = {_I_badusb_10px_0}; + const uint8_t _I_ble_10px_0[] = {0x00,0x04,0x00,0x8C,0x00,0x15,0x01,0x56,0x02,0x8C,0x02,0x8C,0x02,0x56,0x02,0x15,0x01,0x8C,0x00,0x04,0x00,}; const uint8_t *_I_ble_10px[] = {_I_ble_10px_0}; @@ -55,6 +58,9 @@ const uint8_t *_I_ir_10px[] = {_I_ir_10px_0}; const uint8_t _I_sub1_10px_0[] = {0x01,0x00,0x12,0x00,0x81,0x40,0x69,0x30,0x2c,0x2c,0x0b,0x6a,0x01,0x28,0x0c,0x0a,0x65,0x01,0x98,0x40,0x00,0x26,}; const uint8_t *_I_sub1_10px[] = {_I_sub1_10px_0}; +const uint8_t _I_u2f_10px_0[] = {0x00,0x00,0x00,0xFE,0x01,0x01,0x02,0x0C,0x00,0xF2,0x03,0x92,0x02,0x0C,0x00,0x01,0x02,0xFE,0x01,0x00,0x00,}; +const uint8_t *_I_u2f_10px[] = {_I_u2f_10px_0}; + const uint8_t _I_unknown_10px_0[] = {0x01,0x00,0x12,0x00,0xbc,0x40,0x39,0x90,0x0c,0x24,0x03,0x81,0x00,0xb0,0x40,0x26,0x00,0x12,0x00,0x08,0x14,0xc0,}; const uint8_t *_I_unknown_10px[] = {_I_unknown_10px_0}; @@ -624,11 +630,13 @@ const Icon A_Levelup1_128x64 = {.width=128,.height=64,.frame_count=11,.frame_rat const Icon A_Levelup2_128x64 = {.width=128,.height=64,.frame_count=11,.frame_rate=2,.frames=_A_Levelup2_128x64}; const Icon I_125_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_125_10px}; const Icon I_Nfc_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_Nfc_10px}; +const Icon I_badusb_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_badusb_10px}; const Icon I_ble_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_ble_10px}; const Icon I_dir_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_dir_10px}; const Icon I_ibutt_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_ibutt_10px}; const Icon I_ir_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_ir_10px}; const Icon I_sub1_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_sub1_10px}; +const Icon I_u2f_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_u2f_10px}; const Icon I_unknown_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_unknown_10px}; const Icon I_BLE_Pairing_128x64 = {.width=128,.height=64,.frame_count=1,.frame_rate=0,.frames=_I_BLE_Pairing_128x64}; const Icon I_Ble_connected_38x34 = {.width=38,.height=34,.frame_count=1,.frame_rate=0,.frames=_I_Ble_connected_38x34}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index 8fa6d6c6c76..8f53bc36d23 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -7,11 +7,13 @@ extern const Icon A_Levelup1_128x64; extern const Icon A_Levelup2_128x64; extern const Icon I_125_10px; extern const Icon I_Nfc_10px; +extern const Icon I_badusb_10px; extern const Icon I_ble_10px; extern const Icon I_dir_10px; extern const Icon I_ibutt_10px; extern const Icon I_ir_10px; extern const Icon I_sub1_10px; +extern const Icon I_u2f_10px; extern const Icon I_unknown_10px; extern const Icon I_BLE_Pairing_128x64; extern const Icon I_Ble_connected_38x34; diff --git a/assets/icons/Archive/badusb_10px.png b/assets/icons/Archive/badusb_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..037474aa3bc9c2e1aca79a68483e69980432bcf5 GIT binary patch literal 576 zcmV-G0>AxEX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu`HWXkp{t5s906j@WK~xyijZi@f05Awj>HlAL zr$MwDdI>{Qf+U53tOUR#xOeyy)jcQo#JNRv)7r6DVVK|+*(cmT+R+EbO(O#X#REG4 O0000EX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv00000008+zyMF)x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu{1uVUoNs|Bo07OYdK~xyijgYww05Avx?TGzX zz7!EC4G1?heldU+2uZR%k^uSL+0?e2taR-}6`h2x#_2kxune}*>oEbW-V;;Yj| 0) { if(ble_glue->status == BleGlueStatusFusStarted) { @@ -125,13 +126,14 @@ bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info) { countdown--; osDelay(1); } + if(ble_glue->status == BleGlueStatusFusStarted) { SHCI_GetWirelessFwInfo(info); } else { FURI_LOG_E(TAG, "Failed to start FUS"); ble_glue->status = BleGlueStatusBroken; } - furi_hal_power_insomnia_exit(); + return ret; } diff --git a/firmware/targets/f6/furi_hal/furi_hal_power.c b/firmware/targets/f6/furi_hal/furi_hal_power.c index 1dbdb3ddc80..a211b8c587c 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_power.c +++ b/firmware/targets/f6/furi_hal/furi_hal_power.c @@ -90,12 +90,14 @@ uint16_t furi_hal_power_insomnia_level() { void furi_hal_power_insomnia_enter() { vTaskSuspendAll(); + furi_assert(furi_hal_power.insomnia < UINT8_MAX); furi_hal_power.insomnia++; xTaskResumeAll(); } void furi_hal_power_insomnia_exit() { vTaskSuspendAll(); + furi_assert(furi_hal_power.insomnia > 0); furi_hal_power.insomnia--; xTaskResumeAll(); } diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index c38c0731015..6e55b3680f8 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -116,6 +116,7 @@ void ble_glue_init() { bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info) { bool ret = false; + size_t countdown = 1000; while(countdown > 0) { if(ble_glue->status == BleGlueStatusFusStarted) { @@ -125,13 +126,14 @@ bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info) { countdown--; osDelay(1); } + if(ble_glue->status == BleGlueStatusFusStarted) { SHCI_GetWirelessFwInfo(info); } else { FURI_LOG_E(TAG, "Failed to start FUS"); ble_glue->status = BleGlueStatusBroken; } - furi_hal_power_insomnia_exit(); + return ret; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 1dbdb3ddc80..a211b8c587c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -90,12 +90,14 @@ uint16_t furi_hal_power_insomnia_level() { void furi_hal_power_insomnia_enter() { vTaskSuspendAll(); + furi_assert(furi_hal_power.insomnia < UINT8_MAX); furi_hal_power.insomnia++; xTaskResumeAll(); } void furi_hal_power_insomnia_exit() { vTaskSuspendAll(); + furi_assert(furi_hal_power.insomnia > 0); furi_hal_power.insomnia--; xTaskResumeAll(); } From 21ac37a6f696da8ec26407d2f025c24825fcfec8 Mon Sep 17 00:00:00 2001 From: Albert Kharisov Date: Thu, 10 Feb 2022 22:17:41 +0400 Subject: [PATCH 8/9] [FL-2152] New PIN lock (#989) * [Fl-2152] New PIN Lock, part 1 * Fix errors & leaks, renaming * Add support to f6 * Fix error, remove duplicate code * Fix drawing corners of Lock Popup * FuriHal: insomnia if usb connected * Applications: cleanup timers use Co-authored-by: Aleksandr Kutuzov --- .../views/one_shot_animation_view.c | 2 +- applications/desktop/desktop.c | 104 ++-- applications/desktop/desktop_helpers.c | 82 +++ applications/desktop/desktop_helpers.h | 9 + applications/desktop/desktop_i.h | 39 +- .../desktop_settings/desktop_settings.h | 22 +- .../desktop_settings/desktop_settings_app.c | 45 +- .../desktop_settings/desktop_settings_app.h | 24 +- .../scenes/desktop_settings_scene_config.h | 11 +- .../scenes/desktop_settings_scene_i.h | 7 + .../scenes/desktop_settings_scene_pin_auth.c | 95 ++++ .../desktop_settings_scene_pin_disable.c | 56 ++ .../scenes/desktop_settings_scene_pin_error.c | 76 +++ ...nu.c => desktop_settings_scene_pin_menu.c} | 51 +- .../scenes/desktop_settings_scene_pin_setup.c | 107 ++++ .../desktop_settings_scene_pin_setup_done.c | 77 +++ .../desktop_settings_scene_pin_setup_howto.c | 44 ++ .../desktop_settings_scene_pin_setup_howto2.c | 67 +++ .../desktop_settings_scene_pincode_input.c | 64 --- .../scenes/desktop_settings_scene_start.c | 19 +- .../desktop_settings_view_pin_setup_howto.c | 78 +++ .../desktop_settings_view_pin_setup_howto.h | 15 + .../desktop_settings_view_pin_setup_howto2.c | 101 ++++ .../desktop_settings_view_pin_setup_howto2.h | 20 + .../desktop/scenes/desktop_scene_config.h | 4 +- .../desktop/scenes/desktop_scene_debug.c | 4 +- .../desktop/scenes/desktop_scene_fault.c | 2 +- .../scenes/desktop_scene_first_start.c | 4 +- .../scenes/desktop_scene_hw_mismatch.c | 4 +- applications/desktop/scenes/desktop_scene_i.h | 7 +- .../desktop/scenes/desktop_scene_lock_menu.c | 47 +- .../desktop/scenes/desktop_scene_locked.c | 109 ++++ .../desktop/scenes/desktop_scene_main.c | 55 +- .../desktop/scenes/desktop_scene_pin_input.c | 162 ++++++ .../scenes/desktop_scene_pin_timeout.c | 46 ++ .../desktop/scenes/desktop_scene_pinsetup.c | 50 -- applications/desktop/views/desktop_events.h | 27 +- applications/desktop/views/desktop_locked.c | 247 --------- applications/desktop/views/desktop_locked.h | 36 -- .../{desktop_debug.c => desktop_view_debug.c} | 6 +- .../{desktop_debug.h => desktop_view_debug.h} | 0 ...rst_start.c => desktop_view_first_start.c} | 3 +- ...rst_start.h => desktop_view_first_start.h} | 0 ...p_lock_menu.c => desktop_view_lock_menu.c} | 2 +- ...p_lock_menu.h => desktop_view_lock_menu.h} | 0 .../desktop/views/desktop_view_locked.c | 233 +++++++++ .../desktop/views/desktop_view_locked.h | 21 + .../{desktop_main.c => desktop_view_main.c} | 2 +- .../{desktop_main.h => desktop_view_main.h} | 0 .../desktop/views/desktop_view_pin_input.c | 340 +++++++++++++ .../desktop/views/desktop_view_pin_input.h | 40 ++ .../views/desktop_view_pin_setup_done.c | 80 +++ .../views/desktop_view_pin_setup_done.h | 15 + .../desktop/views/desktop_view_pin_timeout.c | 109 ++++ .../desktop/views/desktop_view_pin_timeout.h | 16 + applications/dolphin/dolphin.c | 10 +- applications/gui/elements.c | 44 ++ applications/gui/elements.h | 13 + applications/gui/modules/code_input.c | 478 ------------------ applications/gui/modules/code_input.h | 91 ---- applications/loader/loader.c | 7 + assets/compiled/assets_icons.c | 44 +- assets/compiled/assets_icons.h | 11 +- assets/icons/Interface/LockPopup_100x49.png | Bin 577 -> 0 bytes assets/icons/PIN/Pin_arrow_down_7x9.png | Bin 0 -> 3607 bytes assets/icons/PIN/Pin_arrow_left_9x7.png | Bin 0 -> 3603 bytes assets/icons/PIN/Pin_arrow_right_9x7.png | Bin 0 -> 3602 bytes assets/icons/PIN/Pin_arrow_up7x9.png | Bin 0 -> 3603 bytes assets/icons/PIN/Pin_attention_dpad_29x29.png | Bin 0 -> 3688 bytes assets/icons/PIN/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes assets/icons/PIN/Pin_back_full_40x8.png | Bin 0 -> 3641 bytes assets/icons/PIN/Pin_cell_13x13.png | Bin 0 -> 3593 bytes assets/icons/PIN/Pin_pointer_5x3.png | Bin 0 -> 3592 bytes assets/icons/PIN/Pin_star_7x7.png | Bin 0 -> 3600 bytes firmware/targets/f6/furi_hal/furi_hal_power.c | 8 +- firmware/targets/f6/furi_hal/furi_hal_rtc.c | 8 + firmware/targets/f6/furi_hal/furi_hal_usb.c | 5 + firmware/targets/f7/furi_hal/furi_hal_power.c | 8 +- firmware/targets/f7/furi_hal/furi_hal_rtc.c | 8 + firmware/targets/f7/furi_hal/furi_hal_usb.c | 5 + .../targets/furi_hal_include/furi_hal_rtc.h | 5 + 81 files changed, 2458 insertions(+), 1173 deletions(-) create mode 100644 applications/desktop/desktop_helpers.c create mode 100644 applications/desktop/desktop_helpers.h create mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_i.h create mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c create mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_disable.c create mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c rename applications/desktop/desktop_settings/scenes/{desktop_settings_scene_pincode_menu.c => desktop_settings_scene_pin_menu.c} (54%) create mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c create mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c create mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c create mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c delete mode 100644 applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_input.c create mode 100644 applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.c create mode 100644 applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.h create mode 100644 applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c create mode 100644 applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h create mode 100644 applications/desktop/scenes/desktop_scene_locked.c create mode 100644 applications/desktop/scenes/desktop_scene_pin_input.c create mode 100644 applications/desktop/scenes/desktop_scene_pin_timeout.c delete mode 100644 applications/desktop/scenes/desktop_scene_pinsetup.c delete mode 100644 applications/desktop/views/desktop_locked.c delete mode 100644 applications/desktop/views/desktop_locked.h rename applications/desktop/views/{desktop_debug.c => desktop_view_debug.c} (98%) rename applications/desktop/views/{desktop_debug.h => desktop_view_debug.h} (100%) rename applications/desktop/views/{desktop_first_start.c => desktop_view_first_start.c} (99%) rename applications/desktop/views/{desktop_first_start.h => desktop_view_first_start.h} (100%) rename applications/desktop/views/{desktop_lock_menu.c => desktop_view_lock_menu.c} (99%) rename applications/desktop/views/{desktop_lock_menu.h => desktop_view_lock_menu.h} (100%) create mode 100644 applications/desktop/views/desktop_view_locked.c create mode 100644 applications/desktop/views/desktop_view_locked.h rename applications/desktop/views/{desktop_main.c => desktop_view_main.c} (98%) rename applications/desktop/views/{desktop_main.h => desktop_view_main.h} (100%) create mode 100644 applications/desktop/views/desktop_view_pin_input.c create mode 100644 applications/desktop/views/desktop_view_pin_input.h create mode 100644 applications/desktop/views/desktop_view_pin_setup_done.c create mode 100644 applications/desktop/views/desktop_view_pin_setup_done.h create mode 100644 applications/desktop/views/desktop_view_pin_timeout.c create mode 100644 applications/desktop/views/desktop_view_pin_timeout.h delete mode 100644 applications/gui/modules/code_input.c delete mode 100644 applications/gui/modules/code_input.h delete mode 100644 assets/icons/Interface/LockPopup_100x49.png create mode 100644 assets/icons/PIN/Pin_arrow_down_7x9.png create mode 100644 assets/icons/PIN/Pin_arrow_left_9x7.png create mode 100644 assets/icons/PIN/Pin_arrow_right_9x7.png create mode 100644 assets/icons/PIN/Pin_arrow_up7x9.png create mode 100644 assets/icons/PIN/Pin_attention_dpad_29x29.png create mode 100644 assets/icons/PIN/Pin_back_arrow_10x8.png create mode 100644 assets/icons/PIN/Pin_back_full_40x8.png create mode 100644 assets/icons/PIN/Pin_cell_13x13.png create mode 100644 assets/icons/PIN/Pin_pointer_5x3.png create mode 100644 assets/icons/PIN/Pin_star_7x7.png diff --git a/applications/desktop/animations/views/one_shot_animation_view.c b/applications/desktop/animations/views/one_shot_animation_view.c index 106cef551ff..d7e9915a7f3 100644 --- a/applications/desktop/animations/views/one_shot_animation_view.c +++ b/applications/desktop/animations/views/one_shot_animation_view.c @@ -81,7 +81,7 @@ OneShotView* one_shot_view_alloc(void) { OneShotView* view = furi_alloc(sizeof(OneShotView)); view->view = view_alloc(); view->update_timer = - xTimerCreate("Update timer", 1000, pdTRUE, view, one_shot_view_update_timer_callback); + xTimerCreate(NULL, 1000, pdTRUE, view, one_shot_view_update_timer_callback); view_allocate_model(view->view, ViewModelTypeLocking, sizeof(OneShotViewModel)); view_set_context(view->view, view); diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c index 5d9b7b80a59..29e17a88e64 100644 --- a/applications/desktop/desktop.c +++ b/applications/desktop/desktop.c @@ -1,9 +1,3 @@ -#include "animations/animation_manager.h" -#include "desktop/scenes/desktop_scene.h" -#include "desktop/scenes/desktop_scene_i.h" -#include "desktop/views/desktop_locked.h" -#include "desktop_i.h" - #include #include #include @@ -12,23 +6,38 @@ #include #include +#include "animations/animation_manager.h" +#include "desktop/scenes/desktop_scene.h" +#include "desktop/scenes/desktop_scene_i.h" +#include "desktop/views/desktop_view_locked.h" +#include "desktop/views/desktop_view_pin_input.h" +#include "desktop/views/desktop_view_pin_timeout.h" +#include "desktop_i.h" +#include "desktop_helpers.h" + static void desktop_lock_icon_callback(Canvas* canvas, void* context) { furi_assert(canvas); canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8); } -bool desktop_custom_event_callback(void* context, uint32_t event) { +static bool desktop_custom_event_callback(void* context, uint32_t event) { furi_assert(context); Desktop* desktop = (Desktop*)context; return scene_manager_handle_custom_event(desktop->scene_manager, event); } -bool desktop_back_event_callback(void* context) { +static bool desktop_back_event_callback(void* context) { furi_assert(context); Desktop* desktop = (Desktop*)context; return scene_manager_handle_back_event(desktop->scene_manager); } +static void desktop_tick_event_callback(void* context) { + furi_assert(context); + Desktop* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + Desktop* desktop_alloc() { Desktop* desktop = furi_alloc(sizeof(Desktop)); @@ -42,6 +51,8 @@ Desktop* desktop_alloc() { view_dispatcher_enable_queue(desktop->view_dispatcher); view_dispatcher_attach_to_gui( desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeDesktop); + view_dispatcher_set_tick_event_callback( + desktop->view_dispatcher, desktop_tick_event_callback, 500); view_dispatcher_set_event_callback_context(desktop->view_dispatcher, desktop); view_dispatcher_set_custom_event_callback( @@ -49,37 +60,60 @@ Desktop* desktop_alloc() { view_dispatcher_set_navigation_event_callback( desktop->view_dispatcher, desktop_back_event_callback); - desktop->locked_view = desktop_locked_alloc(); desktop->lock_menu = desktop_lock_menu_alloc(); desktop->debug_view = desktop_debug_alloc(); desktop->first_start_view = desktop_first_start_alloc(); desktop->hw_mismatch_popup = popup_alloc(); - desktop->code_input = code_input_alloc(); + desktop->locked_view = desktop_view_locked_alloc(); + desktop->pin_input_view = desktop_view_pin_input_alloc(); + desktop->pin_timeout_view = desktop_view_pin_timeout_alloc(); + desktop->main_view_stack = view_stack_alloc(); desktop->main_view = desktop_main_alloc(); View* dolphin_view = animation_manager_get_animation_view(desktop->animation_manager); view_stack_add_view(desktop->main_view_stack, desktop_main_get_view(desktop->main_view)); view_stack_add_view(desktop->main_view_stack, dolphin_view); - view_stack_add_view(desktop->main_view_stack, desktop_locked_get_view(desktop->locked_view)); + view_stack_add_view( + desktop->main_view_stack, desktop_view_locked_get_view(desktop->locked_view)); + /* locked view (as animation view) attends in 2 scenes: main & locked, + * because it has to draw "Unlocked" label on main scene */ + desktop->locked_view_stack = view_stack_alloc(); + view_stack_add_view(desktop->locked_view_stack, dolphin_view); + view_stack_add_view( + desktop->locked_view_stack, desktop_view_locked_get_view(desktop->locked_view)); + + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdMain, + view_stack_get_view(desktop->main_view_stack)); view_dispatcher_add_view( - desktop->view_dispatcher, DesktopViewMain, view_stack_get_view(desktop->main_view_stack)); + desktop->view_dispatcher, + DesktopViewIdLocked, + view_stack_get_view(desktop->locked_view_stack)); view_dispatcher_add_view( desktop->view_dispatcher, - DesktopViewLockMenu, + DesktopViewIdLockMenu, desktop_lock_menu_get_view(desktop->lock_menu)); view_dispatcher_add_view( - desktop->view_dispatcher, DesktopViewDebug, desktop_debug_get_view(desktop->debug_view)); + desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view)); view_dispatcher_add_view( desktop->view_dispatcher, - DesktopViewFirstStart, + DesktopViewIdFirstStart, desktop_first_start_get_view(desktop->first_start_view)); view_dispatcher_add_view( desktop->view_dispatcher, - DesktopViewHwMismatch, + DesktopViewIdHwMismatch, popup_get_view(desktop->hw_mismatch_popup)); view_dispatcher_add_view( - desktop->view_dispatcher, DesktopViewPinSetup, code_input_get_view(desktop->code_input)); + desktop->view_dispatcher, + DesktopViewIdPinTimeout, + desktop_view_pin_timeout_get_view(desktop->pin_timeout_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdPinInput, + desktop_view_pin_input_get_view(desktop->pin_input_view)); + // Lock icon desktop->lock_viewport = view_port_alloc(); view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8)); @@ -93,27 +127,29 @@ Desktop* desktop_alloc() { void desktop_free(Desktop* desktop) { furi_assert(desktop); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewMain); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLockMenu); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLocked); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewPinSetup); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdFirstStart); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout); view_dispatcher_free(desktop->view_dispatcher); scene_manager_free(desktop->scene_manager); animation_manager_free(desktop->animation_manager); view_stack_free(desktop->main_view_stack); - view_stack_free(desktop->locked_view_stack); desktop_main_free(desktop->main_view); + view_stack_free(desktop->locked_view_stack); + desktop_view_locked_free(desktop->locked_view); desktop_lock_menu_free(desktop->lock_menu); - desktop_locked_free(desktop->locked_view); + desktop_view_locked_free(desktop->locked_view); desktop_debug_free(desktop->debug_view); desktop_first_start_free(desktop->first_start_view); popup_free(desktop->hw_mismatch_popup); - code_input_free(desktop->code_input); + desktop_view_pin_timeout_free(desktop->pin_timeout_view); osSemaphoreDelete(desktop->unload_animation_semaphore); @@ -145,14 +181,18 @@ int32_t desktop_srv(void* p) { SAVE_DESKTOP_SETTINGS(&desktop->settings); } + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { - furi_hal_usb_disable(); - scene_manager_set_scene_state( - desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedWithPin); + if(desktop->settings.pin_code.length > 0) { + scene_manager_set_scene_state( + desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + } } - scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); - if(desktop_is_first_start()) { scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart); } diff --git a/applications/desktop/desktop_helpers.c b/applications/desktop/desktop_helpers.c new file mode 100644 index 00000000000..c3025ae10cf --- /dev/null +++ b/applications/desktop/desktop_helpers.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include + +#include "desktop_helpers.h" +#include "desktop_i.h" + +static const NotificationSequence sequence_pin_fail = { + &message_display_on, + + &message_red_255, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + &message_red_0, + + &message_delay_250, + + &message_red_255, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + &message_red_0, + NULL, +}; + +static const uint8_t desktop_helpers_fails_timeout[] = { + 0, + 0, + 0, + 0, + 30, + 60, + 90, + 120, + 150, + 180, + /* +60 for every next fail */ +}; + +void desktop_helpers_emit_error_notification() { + NotificationApp* notification = furi_record_open("notification"); + notification_message(notification, &sequence_pin_fail); + furi_record_close("notification"); +} + +void desktop_helpers_lock_system(Desktop* desktop, bool hard_lock) { + view_port_enabled_set(desktop->lock_viewport, true); + if(hard_lock) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLock); + furi_hal_usb_disable(); + } + + Gui* gui = furi_record_open("gui"); + gui_set_lockdown(gui, true); + furi_record_close("gui"); +} + +void desktop_helpers_unlock_system(Desktop* desktop) { + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + furi_hal_usb_enable(); + view_port_enabled_set(desktop->lock_viewport, false); + + Gui* gui = furi_record_open("gui"); + gui_set_lockdown(gui, false); + furi_record_close("gui"); +} + +uint32_t desktop_helpers_get_pin_fail_timeout(uint32_t pin_fails) { + uint32_t pin_timeout = 0; + uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; + if(pin_fails <= max_index) { + pin_timeout = desktop_helpers_fails_timeout[pin_fails]; + } else { + pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; + } + + return pin_timeout; +} diff --git a/applications/desktop/desktop_helpers.h b/applications/desktop/desktop_helpers.h new file mode 100644 index 00000000000..f8393df8d38 --- /dev/null +++ b/applications/desktop/desktop_helpers.h @@ -0,0 +1,9 @@ +#pragma once +#include +#include +#include "desktop.h" + +void desktop_helpers_emit_error_notification(); +void desktop_helpers_lock_system(Desktop* desktop, bool hard_lock); +void desktop_helpers_unlock_system(Desktop* desktop); +uint32_t desktop_helpers_get_pin_fail_timeout(uint32_t pin_fails); diff --git a/applications/desktop/desktop_i.h b/applications/desktop/desktop_i.h index b6d0d9233df..fb15dbd7469 100644 --- a/applications/desktop/desktop_i.h +++ b/applications/desktop/desktop_i.h @@ -2,11 +2,13 @@ #include "desktop.h" #include "animations/animation_manager.h" -#include "views/desktop_main.h" -#include "views/desktop_first_start.h" -#include "views/desktop_lock_menu.h" -#include "views/desktop_locked.h" -#include "views/desktop_debug.h" +#include "views/desktop_view_pin_timeout.h" +#include "views/desktop_view_pin_input.h" +#include "views/desktop_view_locked.h" +#include "views/desktop_view_main.h" +#include "views/desktop_view_first_start.h" +#include "views/desktop_view_lock_menu.h" +#include "views/desktop_view_debug.h" #include "desktop/desktop_settings/desktop_settings.h" #include @@ -14,21 +16,21 @@ #include #include #include -#include #include #define STATUS_BAR_Y_SHIFT 13 typedef enum { - DesktopViewMain, - DesktopViewLockMenu, - DesktopViewLocked, - DesktopViewDebug, - DesktopViewFirstStart, - DesktopViewHwMismatch, - DesktopViewPinSetup, - DesktopViewTotal, -} DesktopViewEnum; + DesktopViewIdMain, + DesktopViewIdLockMenu, + DesktopViewIdLocked, + DesktopViewIdDebug, + DesktopViewIdFirstStart, + DesktopViewIdHwMismatch, + DesktopViewIdPinInput, + DesktopViewIdPinTimeout, + DesktopViewIdTotal, +} DesktopViewId; struct Desktop { // Scene @@ -42,16 +44,15 @@ struct Desktop { Popup* hw_mismatch_popup; DesktopLockMenuView* lock_menu; DesktopDebugView* debug_view; - CodeInput* code_input; - + DesktopViewLocked* locked_view; DesktopMainView* main_view; - DesktopLockedView* locked_view; + DesktopViewPinTimeout* pin_timeout_view; ViewStack* main_view_stack; ViewStack* locked_view_stack; DesktopSettings settings; - PinCode pincode_buffer; + DesktopViewPinInput* pin_input_view; ViewPort* lock_viewport; diff --git a/applications/desktop/desktop_settings/desktop_settings.h b/applications/desktop/desktop_settings/desktop_settings.h index 27ded7156d5..24165fe26be 100644 --- a/applications/desktop/desktop_settings/desktop_settings.h +++ b/applications/desktop/desktop_settings/desktop_settings.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -9,6 +10,8 @@ #define DESKTOP_SETTINGS_MAGIC (0x17) #define PIN_MAX_LENGTH 12 +#define DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG "run_pin_setup" + #define SAVE_DESKTOP_SETTINGS(x) \ saved_struct_save( \ DESKTOP_SETTINGS_PATH, \ @@ -25,12 +28,27 @@ DESKTOP_SETTINGS_MAGIC, \ DESKTOP_SETTINGS_VER) +#define MAX_PIN_SIZE 10 +#define MIN_PIN_SIZE 4 + typedef struct { + InputKey data[MAX_PIN_SIZE]; uint8_t length; - uint8_t data[PIN_MAX_LENGTH]; } PinCode; typedef struct { uint16_t favorite; - PinCode pincode; + PinCode pin_code; } DesktopSettings; + +static inline bool pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { + furi_assert(pin_code1); + furi_assert(pin_code2); + bool result = false; + + if(pin_code1->length == pin_code2->length) { + result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); + } + + return result; +} diff --git a/applications/desktop/desktop_settings/desktop_settings_app.c b/applications/desktop/desktop_settings/desktop_settings_app.c index 1c65f58baac..ca078ccd834 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.c +++ b/applications/desktop/desktop_settings/desktop_settings_app.c @@ -1,6 +1,10 @@ -#include "desktop_settings_app.h" #include +#include +#include + +#include "desktop_settings_app.h" #include "scenes/desktop_settings_scene.h" +#include "../views/desktop_view_pin_input.h" static bool desktop_settings_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -30,17 +34,28 @@ DesktopSettingsApp* desktop_settings_app_alloc() { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + app->popup = popup_alloc(); app->submenu = submenu_alloc(); + app->pin_input_view = desktop_view_pin_input_alloc(); + app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc(); + app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc(); + view_dispatcher_add_view( app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); - - app->code_input = code_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup)); view_dispatcher_add_view( app->view_dispatcher, - DesktopSettingsAppViewPincodeInput, - code_input_get_view(app->code_input)); - - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); + DesktopSettingsAppViewIdPinInput, + desktop_view_pin_input_get_view(app->pin_input_view)); + view_dispatcher_add_view( + app->view_dispatcher, + DesktopSettingsAppViewIdPinSetupHowto, + desktop_settings_view_pin_setup_howto_get_view(app->pin_setup_howto_view)); + view_dispatcher_add_view( + app->view_dispatcher, + DesktopSettingsAppViewIdPinSetupHowto2, + desktop_settings_view_pin_setup_howto2_get_view(app->pin_setup_howto2_view)); return app; } @@ -48,9 +63,15 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { furi_assert(app); // Variable item list view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu); + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto); + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2); submenu_free(app->submenu); - view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput); - code_input_free(app->code_input); + popup_free(app->popup); + desktop_view_pin_input_free(app->pin_input_view); + desktop_settings_view_pin_setup_howto_free(app->pin_setup_howto_view); + desktop_settings_view_pin_setup_howto2_free(app->pin_setup_howto2_view); // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); @@ -62,6 +83,12 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { extern int32_t desktop_settings_app(void* p) { DesktopSettingsApp* app = desktop_settings_app_alloc(); LOAD_DESKTOP_SETTINGS(&app->settings); + if(!strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG)) { + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); + } else { + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); + } + view_dispatcher_run(app->view_dispatcher); desktop_settings_app_free(app); return 0; diff --git a/applications/desktop/desktop_settings/desktop_settings_app.h b/applications/desktop/desktop_settings/desktop_settings_app.h index 9c37a1549d1..6297e42db5f 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.h +++ b/applications/desktop/desktop_settings/desktop_settings_app.h @@ -1,22 +1,22 @@ #pragma once #include +#include #include #include #include -#include #include "desktop_settings.h" - -typedef enum { - CodeEventsSetPin, - CodeEventsChangePin, - CodeEventsDisablePin, -} CodeEventsEnum; +#include "desktop/views/desktop_view_pin_input.h" +#include "views/desktop_settings_view_pin_setup_howto.h" +#include "views/desktop_settings_view_pin_setup_howto2.h" typedef enum { DesktopSettingsAppViewMenu, - DesktopSettingsAppViewPincodeInput, + DesktopSettingsAppViewIdPopup, + DesktopSettingsAppViewIdPinInput, + DesktopSettingsAppViewIdPinSetupHowto, + DesktopSettingsAppViewIdPinSetupHowto2, } DesktopSettingsAppView; typedef struct { @@ -26,7 +26,13 @@ typedef struct { SceneManager* scene_manager; ViewDispatcher* view_dispatcher; Submenu* submenu; - CodeInput* code_input; + Popup* popup; + DesktopViewPinInput* pin_input_view; + DesktopSettingsViewPinSetupHowto* pin_setup_howto_view; + DesktopSettingsViewPinSetupHowto2* pin_setup_howto2_view; + + PinCode pincode_buffer; + bool pincode_buffer_filled; uint8_t menu_idx; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h index 126873dbe15..5bc52172b9e 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h @@ -1,4 +1,11 @@ ADD_SCENE(desktop_settings, start, Start) ADD_SCENE(desktop_settings, favorite, Favorite) -ADD_SCENE(desktop_settings, pincode_menu, PinCodeMenu) -ADD_SCENE(desktop_settings, pincode_input, PinCodeInput) +ADD_SCENE(desktop_settings, pin_menu, PinMenu) + +ADD_SCENE(desktop_settings, pin_auth, PinAuth) +ADD_SCENE(desktop_settings, pin_error, PinError) +ADD_SCENE(desktop_settings, pin_disable, PinDisable) +ADD_SCENE(desktop_settings, pin_setup, PinSetup) +ADD_SCENE(desktop_settings, pin_setup_howto, PinSetupHowto) +ADD_SCENE(desktop_settings, pin_setup_howto2, PinSetupHowto2) +ADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone) diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_i.h b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_i.h new file mode 100644 index 00000000000..230fec873da --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_i.h @@ -0,0 +1,7 @@ +#pragma once + +#define SCENE_STATE_PIN_AUTH_DISABLE (0) +#define SCENE_STATE_PIN_AUTH_CHANGE_PIN (1) + +#define SCENE_STATE_PIN_ERROR_MISMATCH (0) +#define SCENE_STATE_PIN_ERROR_WRONG (1) diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c new file mode 100644 index 00000000000..c57506f9e7d --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#include "../desktop_settings_app.h" +#include "desktop/desktop_settings/desktop_settings.h" +#include "desktop/views/desktop_view_pin_input.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" + +#define SCENE_EVENT_EXIT (0U) +#define SCENE_EVENT_PINS_EQUAL (1U) +#define SCENE_EVENT_PINS_DIFFERENT (2U) + +static void pin_auth_done_callback(const PinCode* pin_code, void* context) { + furi_assert(pin_code); + furi_assert(context); + DesktopSettingsApp* app = context; + + app->pincode_buffer = *pin_code; + if(pins_are_equal(&app->settings.pin_code, pin_code)) { + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); + } else { + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); + } +} + +static void pin_auth_back_callback(void* context) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); +} + +void desktop_settings_scene_pin_auth_on_enter(void* context) { + DesktopSettingsApp* app = context; + + LOAD_DESKTOP_SETTINGS(&app->settings); + furi_assert(app->settings.pin_code.length > 0); + + desktop_view_pin_input_set_context(app->pin_input_view, app); + desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback); + desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_auth_done_callback); + desktop_view_pin_input_set_label_button(app->pin_input_view, "OK"); + desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL); + desktop_view_pin_input_set_label_secondary( + app->pin_input_view, 0, 8, "Enter your current PIN:"); + desktop_view_pin_input_reset_pin(app->pin_input_view); + desktop_view_pin_input_unlock_input(app->pin_input_view); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); +} + +bool desktop_settings_scene_pin_auth_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EVENT_PINS_DIFFERENT: + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppScenePinError, SCENE_STATE_PIN_ERROR_WRONG); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError); + consumed = true; + break; + case SCENE_EVENT_PINS_EQUAL: { + uint32_t state = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinAuth); + if(state == SCENE_STATE_PIN_AUTH_CHANGE_PIN) { + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); + } else if(state == SCENE_STATE_PIN_AUTH_DISABLE) { + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinDisable); + } else { + furi_assert(0); + } + consumed = true; + break; + } + case SCENE_EVENT_EXIT: + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppScenePinMenu); + consumed = true; + break; + + default: + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_pin_auth_on_exit(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); + desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL); +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_disable.c new file mode 100644 index 00000000000..7486d4aa3c2 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +#include "../desktop_settings_app.h" +#include "../desktop_settings.h" +#include "desktop/desktop_settings/desktop_settings.h" +#include "desktop_settings_scene.h" + +#define SCENE_EVENT_EXIT (0U) + +static void pin_disable_back_callback(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); +} + +void desktop_settings_scene_pin_disable_on_enter(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + app->settings.pin_code.length = 0; + memset(app->settings.pin_code.data, '0', sizeof(app->settings.pin_code.data)); + SAVE_DESKTOP_SETTINGS(&app->settings); + + popup_set_context(app->popup, app); + popup_set_callback(app->popup, pin_disable_back_callback); + popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(app->popup, "PIN\ndeleted!", 95, 9, AlignCenter, AlignCenter); + popup_set_timeout(app->popup, 1500); + popup_enable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); +} + +bool desktop_settings_scene_pin_disable_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EVENT_EXIT: + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppScenePinMenu); + consumed = true; + break; + + default: + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_pin_disable_on_exit(void* context) { +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c new file mode 100644 index 00000000000..07bba4a87aa --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c @@ -0,0 +1,76 @@ +#include +#include +#include + +#include "desktop/desktop_settings/desktop_settings.h" +#include "desktop/views/desktop_view_pin_input.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" +#include "../../desktop_helpers.h" +#include "../desktop_settings_app.h" + +#define SCENE_EVENT_EXIT (0U) + +static void pin_error_back_callback(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); +} + +static void pin_error_done_callback(const PinCode* pin_code, void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); +} + +void desktop_settings_scene_pin_error_on_enter(void* context) { + DesktopSettingsApp* app = context; + desktop_helpers_emit_error_notification(); + + desktop_view_pin_input_set_context(app->pin_input_view, app); + desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_error_back_callback); + desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_error_done_callback); + + uint32_t state = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinError); + if(state == SCENE_STATE_PIN_ERROR_MISMATCH) { + desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN mismatch!"); + } else if(state == SCENE_STATE_PIN_ERROR_WRONG) { + desktop_view_pin_input_set_label_primary(app->pin_input_view, 35, 8, "Wrong PIN!"); + } else { + furi_assert(0); + } + desktop_view_pin_input_set_label_secondary(app->pin_input_view, 0, 8, NULL); + desktop_view_pin_input_set_label_button(app->pin_input_view, "Retry"); + desktop_view_pin_input_lock_input(app->pin_input_view); + desktop_view_pin_input_set_pin(app->pin_input_view, &app->pincode_buffer); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); +} + +bool desktop_settings_scene_pin_error_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EVENT_EXIT: + scene_manager_previous_scene(app->scene_manager); + consumed = true; + break; + + default: + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_pin_error_on_exit(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + desktop_view_pin_input_unlock_input(app->pin_input_view); + desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); + desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL); +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_menu.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_menu.c similarity index 54% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_menu.c rename to applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index 9e72f52ae78..d226181d7be 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_menu.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -1,38 +1,45 @@ +#include +#include + #include "../desktop_settings_app.h" -#include "applications.h" #include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" + +#define SCENE_EVENT_SET_PIN 0 +#define SCENE_EVENT_CHANGE_PIN 1 +#define SCENE_EVENT_DISABLE_PIN 2 -static void desktop_settings_scene_pincode_menu_submenu_callback(void* context, uint32_t index) { +static void desktop_settings_scene_pin_menu_submenu_callback(void* context, uint32_t index) { DesktopSettingsApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -void desktop_settings_scene_pincode_menu_on_enter(void* context) { +void desktop_settings_scene_pin_menu_on_enter(void* context) { DesktopSettingsApp* app = context; Submenu* submenu = app->submenu; submenu_reset(submenu); - if(!app->settings.pincode.length) { + if(!app->settings.pin_code.length) { submenu_add_item( submenu, "Set Pin", - CodeEventsSetPin, - desktop_settings_scene_pincode_menu_submenu_callback, + SCENE_EVENT_SET_PIN, + desktop_settings_scene_pin_menu_submenu_callback, app); } else { submenu_add_item( submenu, "Change Pin", - CodeEventsChangePin, - desktop_settings_scene_pincode_menu_submenu_callback, + SCENE_EVENT_CHANGE_PIN, + desktop_settings_scene_pin_menu_submenu_callback, app); submenu_add_item( submenu, "Disable", - CodeEventsDisablePin, - desktop_settings_scene_pincode_menu_submenu_callback, + SCENE_EVENT_DISABLE_PIN, + desktop_settings_scene_pin_menu_submenu_callback, app); } @@ -41,28 +48,28 @@ void desktop_settings_scene_pincode_menu_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); } -bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEvent event) { +bool desktop_settings_scene_pin_menu_on_event(void* context, SceneManagerEvent event) { DesktopSettingsApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case CodeEventsSetPin: - scene_manager_set_scene_state( - app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); + case SCENE_EVENT_SET_PIN: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); consumed = true; break; - case CodeEventsChangePin: + case SCENE_EVENT_CHANGE_PIN: scene_manager_set_scene_state( - app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); + app->scene_manager, + DesktopSettingsAppScenePinAuth, + SCENE_STATE_PIN_AUTH_CHANGE_PIN); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth); consumed = true; break; - case CodeEventsDisablePin: + case SCENE_EVENT_DISABLE_PIN: scene_manager_set_scene_state( - app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); + app->scene_manager, DesktopSettingsAppScenePinAuth, SCENE_STATE_PIN_AUTH_DISABLE); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth); consumed = true; break; default: @@ -73,7 +80,7 @@ bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEve return consumed; } -void desktop_settings_scene_pincode_menu_on_exit(void* context) { +void desktop_settings_scene_pin_menu_on_exit(void* context) { DesktopSettingsApp* app = context; submenu_reset(app->submenu); } diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c new file mode 100644 index 00000000000..5659684f985 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c @@ -0,0 +1,107 @@ +#include +#include +#include + +#include "../desktop_settings_app.h" +#include "desktop/desktop_settings/desktop_settings.h" +#include "desktop/views/desktop_view_pin_input.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" + +#define SCENE_EVENT_EXIT (0U) +#define SCENE_EVENT_1ST_PIN_ENTERED (1U) +#define SCENE_EVENT_PINS_EQUAL (2U) +#define SCENE_EVENT_PINS_DIFFERENT (3U) + +static void pin_setup_done_callback(const PinCode* pin_code, void* context) { + furi_assert(pin_code); + furi_assert(context); + DesktopSettingsApp* app = context; + + if(!app->pincode_buffer_filled) { + app->pincode_buffer = *pin_code; + app->pincode_buffer_filled = true; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED); + } else { + app->pincode_buffer_filled = false; + if(pins_are_equal(&app->pincode_buffer, pin_code)) { + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); + } else { + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); + } + } +} + +static void pin_setup_back_callback(void* context) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); +} + +void desktop_settings_scene_pin_setup_on_enter(void* context) { + DesktopSettingsApp* app = context; + + app->pincode_buffer_filled = false; + desktop_view_pin_input_set_context(app->pin_input_view, app); + desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_setup_back_callback); + desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback); + desktop_view_pin_input_set_label_button(app->pin_input_view, "OK"); + desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL); + desktop_view_pin_input_set_label_secondary( + app->pin_input_view, 0, 8, "Enter from 4 to 10 arrows:"); + desktop_view_pin_input_reset_pin(app->pin_input_view); + desktop_view_pin_input_unlock_input(app->pin_input_view); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); +} + +bool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EVENT_1ST_PIN_ENTERED: + desktop_view_pin_input_set_label_button(app->pin_input_view, "OK"); + desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL); + desktop_view_pin_input_set_label_secondary( + app->pin_input_view, 0, 8, "Confirm your PIN:"); + desktop_view_pin_input_reset_pin(app->pin_input_view); + desktop_view_pin_input_unlock_input(app->pin_input_view); + consumed = true; + break; + case SCENE_EVENT_PINS_DIFFERENT: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppScenePinError, + SCENE_STATE_PIN_ERROR_MISMATCH); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError); + consumed = true; + break; + case SCENE_EVENT_PINS_EQUAL: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto2); + consumed = true; + break; + case SCENE_EVENT_EXIT: { + uint32_t scene_found; + scene_found = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppScenePinMenu); + if(!scene_found) { + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + + default: + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_pin_setup_on_exit(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); + desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL); +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c new file mode 100644 index 00000000000..7be0e51c341 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include + +#include "../desktop_settings_app.h" +#include "desktop/desktop_settings/desktop_settings.h" +#include "desktop/views/desktop_view_pin_input.h" +#include "desktop_settings_scene.h" + +#define SCENE_EVENT_DONE (0U) + +static void pin_setup_done_callback(const PinCode* pin_code, void* context) { + furi_assert(pin_code); + furi_assert(context); + DesktopSettingsApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_DONE); +} + +void desktop_settings_scene_pin_setup_done_on_enter(void* context) { + DesktopSettingsApp* app = context; + + app->settings.pin_code = app->pincode_buffer; + SAVE_DESKTOP_SETTINGS(&app->settings); + NotificationApp* notification = furi_record_open("notification"); + notification_message(notification, &sequence_single_vibro); + furi_record_close("notification"); + + desktop_view_pin_input_set_context(app->pin_input_view, app); + desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); + desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback); + desktop_view_pin_input_set_pin(app->pin_input_view, &app->settings.pin_code); + desktop_view_pin_input_set_label_button(app->pin_input_view, "Done"); + desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN activated!"); + desktop_view_pin_input_set_label_secondary( + app->pin_input_view, 7, 45, "Remember or write it down"); + desktop_view_pin_input_lock_input(app->pin_input_view); + desktop_view_pin_input_set_pin_position(app->pin_input_view, 64, 24); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); +} + +bool desktop_settings_scene_pin_setup_done_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EVENT_DONE: { + bool scene_found = false; + scene_found = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppScenePinMenu); + if(!scene_found) { + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + default: + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + return consumed; +} + +void desktop_settings_scene_pin_setup_done_on_exit(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + desktop_view_pin_input_set_pin_position(app->pin_input_view, 64, 32); + desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); + desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL); +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c new file mode 100644 index 00000000000..22727a7a096 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c @@ -0,0 +1,44 @@ +#include +#include +#include + +#include "desktop_settings_scene.h" +#include "../desktop_settings_app.h" +#include "../views/desktop_settings_view_pin_setup_howto.h" + +#define SCENE_EXIT_EVENT (0U) + +static void desktop_settings_scene_pin_lock_done_callback(void* context) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); +} + +void desktop_settings_scene_pin_setup_howto_on_enter(void* context) { + DesktopSettingsApp* app = context; + + desktop_settings_view_pin_setup_howto_set_callback( + app->pin_setup_howto_view, desktop_settings_scene_pin_lock_done_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto); +} + +bool desktop_settings_scene_pin_setup_howto_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EXIT_EVENT: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetup); + consumed = true; + break; + default: + furi_assert(0); + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_pin_setup_howto_on_exit(void* context) { +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c new file mode 100644 index 00000000000..477d1f27a73 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c @@ -0,0 +1,67 @@ +#include +#include +#include + +#include "desktop_settings_scene.h" +#include "../desktop_settings_app.h" +#include "../views/desktop_settings_view_pin_setup_howto2.h" + +#define SCENE_EXIT_EVENT (0U) +#define SCENE_DONE_EVENT (1U) + +static void desktop_settings_scene_pin_setup_howto2_done_callback(void* context) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_DONE_EVENT); +} + +static void desktop_settings_scene_pin_setup_howto2_exit_callback(void* context) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); +} + +void desktop_settings_scene_pin_setup_howto2_on_enter(void* context) { + DesktopSettingsApp* app = context; + + desktop_settings_view_pin_setup_howto2_set_context(app->pin_setup_howto2_view, app); + desktop_settings_view_pin_setup_howto2_set_ok_callback( + app->pin_setup_howto2_view, desktop_settings_scene_pin_setup_howto2_done_callback); + desktop_settings_view_pin_setup_howto2_set_cancel_callback( + app->pin_setup_howto2_view, desktop_settings_scene_pin_setup_howto2_exit_callback); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2); +} + +bool desktop_settings_scene_pin_setup_howto2_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_DONE_EVENT: { + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupDone); + consumed = true; + break; + } + case SCENE_EXIT_EVENT: { + bool scene_found = false; + scene_found = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppScenePinMenu); + if(!scene_found) { + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + default: + furi_assert(0); + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_pin_setup_howto2_on_exit(void* context) { + DesktopSettingsApp* app = context; + desktop_settings_view_pin_setup_howto2_set_ok_callback(app->pin_setup_howto2_view, NULL); + desktop_settings_view_pin_setup_howto2_set_cancel_callback(app->pin_setup_howto2_view, NULL); +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_input.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_input.c deleted file mode 100644 index a24551c4d7d..00000000000 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_input.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "../desktop_settings_app.h" -#include "desktop_settings_scene.h" - -#define SCENE_EXIT_EVENT (0U) - -void desktop_settings_scene_ok_callback(void* context) { - DesktopSettingsApp* app = context; - uint32_t state = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput); - - if(state == CodeEventsDisablePin) { - memset(app->settings.pincode.data, 0, app->settings.pincode.length * sizeof(uint8_t)); - app->settings.pincode.length = 0; - } - - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); -} - -void desktop_settings_scene_pincode_input_on_enter(void* context) { - DesktopSettingsApp* app = context; - CodeInput* code_input = app->code_input; - - uint32_t state = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput); - bool update = state != CodeEventsDisablePin; - - code_input_set_header_text(code_input, "PIN Code Setup"); - code_input_set_result_callback( - code_input, - desktop_settings_scene_ok_callback, - NULL, - app, - app->settings.pincode.data, - &app->settings.pincode.length, - update); - - view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput); -} - -bool desktop_settings_scene_pincode_input_on_event(void* context, SceneManagerEvent event) { - DesktopSettingsApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case SCENE_EXIT_EVENT: - scene_manager_previous_scene(app->scene_manager); - consumed = true; - break; - - default: - consumed = true; - break; - } - } - return consumed; -} - -void desktop_settings_scene_pincode_input_on_exit(void* context) { - DesktopSettingsApp* app = context; - SAVE_DESKTOP_SETTINGS(&app->settings); - code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0); - code_input_set_header_text(app->code_input, ""); -} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c index 4ea7e0ccece..8f856b6aef2 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c @@ -1,11 +1,10 @@ +#include + #include "../desktop_settings_app.h" -#include "applications.h" #include "desktop_settings_scene.h" -enum DesktopSettingsStartSubmenuIndex { - DesktopSettingsStartSubmenuIndexFavorite, - DesktopSettingsStartSubmenuIndexPinSetup, -}; +#define SCENE_EVENT_SELECT_FAVORITE 0 +#define SCENE_EVENT_SELECT_PIN_SETUP 1 static void desktop_settings_scene_start_submenu_callback(void* context, uint32_t index) { DesktopSettingsApp* app = context; @@ -19,14 +18,14 @@ void desktop_settings_scene_start_on_enter(void* context) { submenu_add_item( submenu, "Favorite App", - DesktopSettingsStartSubmenuIndexFavorite, + SCENE_EVENT_SELECT_FAVORITE, desktop_settings_scene_start_submenu_callback, app); submenu_add_item( submenu, "PIN Setup", - DesktopSettingsStartSubmenuIndexPinSetup, + SCENE_EVENT_SELECT_PIN_SETUP, desktop_settings_scene_start_submenu_callback, app); @@ -39,12 +38,12 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case DesktopSettingsStartSubmenuIndexFavorite: + case SCENE_EVENT_SELECT_FAVORITE: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); consumed = true; break; - case DesktopSettingsStartSubmenuIndexPinSetup: - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeMenu); + case SCENE_EVENT_SELECT_PIN_SETUP: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); consumed = true; break; } diff --git a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.c b/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.c new file mode 100644 index 00000000000..c87de756ac7 --- /dev/null +++ b/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "desktop_settings_view_pin_setup_howto.h" + +struct DesktopSettingsViewPinSetupHowto { + View* view; + DesktopSettingsViewPinSetupHowtoDoneCallback callback; + void* context; +}; + +static void desktop_settings_view_pin_setup_howto_draw(Canvas* canvas, void* model) { + furi_assert(canvas); + furi_assert(model); + + canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29); + elements_button_right(canvas, "Next"); + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Setting up PIN"); + + canvas_set_font(canvas, FontSecondary); + elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols"); +} + +static bool desktop_settings_view_pin_setup_howto_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopSettingsViewPinSetupHowto* instance = context; + bool consumed = false; + + if((event->key == InputKeyRight) && (event->type == InputTypeShort)) { + instance->callback(instance->context); + consumed = true; + } + + return consumed; +} + +void desktop_settings_view_pin_setup_howto_set_callback( + DesktopSettingsViewPinSetupHowto* instance, + DesktopSettingsViewPinSetupHowtoDoneCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +DesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc() { + DesktopSettingsViewPinSetupHowto* view = furi_alloc(sizeof(DesktopSettingsViewPinSetupHowto)); + view->view = view_alloc(); + view_allocate_model(view->view, ViewModelTypeLockFree, 1); + view_set_context(view->view, view); + view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto_draw); + view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto_input); + + return view; +} + +void desktop_settings_view_pin_setup_howto_free(DesktopSettingsViewPinSetupHowto* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* desktop_settings_view_pin_setup_howto_get_view(DesktopSettingsViewPinSetupHowto* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.h b/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.h new file mode 100644 index 00000000000..0f62cb433a3 --- /dev/null +++ b/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +typedef struct DesktopSettingsViewPinSetupHowto DesktopSettingsViewPinSetupHowto; + +typedef void (*DesktopSettingsViewPinSetupHowtoDoneCallback)(void*); + +void desktop_settings_view_pin_setup_howto_set_callback( + DesktopSettingsViewPinSetupHowto* instance, + DesktopSettingsViewPinSetupHowtoDoneCallback callback, + void* context); +DesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc(); +void desktop_settings_view_pin_setup_howto_free(DesktopSettingsViewPinSetupHowto* instance); +View* desktop_settings_view_pin_setup_howto_get_view(DesktopSettingsViewPinSetupHowto* instance); diff --git a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c b/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c new file mode 100644 index 00000000000..3ef22b461e5 --- /dev/null +++ b/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "desktop_settings_view_pin_setup_howto2.h" + +struct DesktopSettingsViewPinSetupHowto2 { + View* view; + DesktopSettingsViewPinSetupHowto2Callback cancel_callback; + DesktopSettingsViewPinSetupHowto2Callback ok_callback; + void* context; +}; + +static void desktop_settings_view_pin_setup_howto2_draw(Canvas* canvas, void* model) { + furi_assert(canvas); + furi_assert(model); + + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, + 64, + 24, + AlignCenter, + AlignCenter, + "Forgotten PIN can only be\n" + "reset with entire device.\n" + "Read docs How to reset PIN."); + + elements_button_right(canvas, "OK"); + elements_button_left(canvas, "Cancel"); +} + +static bool desktop_settings_view_pin_setup_howto2_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopSettingsViewPinSetupHowto2* instance = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + instance->ok_callback(instance->context); + consumed = true; + } else if(event->key == InputKeyLeft) { + instance->cancel_callback(instance->context); + consumed = true; + } + } + + return consumed; +} + +void desktop_settings_view_pin_setup_howto2_set_context( + DesktopSettingsViewPinSetupHowto2* instance, + void* context) { + furi_assert(instance); + instance->context = context; +} + +void desktop_settings_view_pin_setup_howto2_set_cancel_callback( + DesktopSettingsViewPinSetupHowto2* instance, + DesktopSettingsViewPinSetupHowto2Callback callback) { + furi_assert(instance); + instance->cancel_callback = callback; +} + +void desktop_settings_view_pin_setup_howto2_set_ok_callback( + DesktopSettingsViewPinSetupHowto2* instance, + DesktopSettingsViewPinSetupHowto2Callback callback) { + furi_assert(instance); + instance->ok_callback = callback; +} + +DesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc() { + DesktopSettingsViewPinSetupHowto2* view = + furi_alloc(sizeof(DesktopSettingsViewPinSetupHowto2)); + view->view = view_alloc(); + view_allocate_model(view->view, ViewModelTypeLockFree, 1); + view_set_context(view->view, view); + view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto2_draw); + view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto2_input); + + return view; +} + +void desktop_settings_view_pin_setup_howto2_free(DesktopSettingsViewPinSetupHowto2* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* desktop_settings_view_pin_setup_howto2_get_view(DesktopSettingsViewPinSetupHowto2* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h b/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h new file mode 100644 index 00000000000..8e3018d9809 --- /dev/null +++ b/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +typedef struct DesktopSettingsViewPinSetupHowto2 DesktopSettingsViewPinSetupHowto2; + +typedef void (*DesktopSettingsViewPinSetupHowto2Callback)(void*); + +DesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc(); +void desktop_settings_view_pin_setup_howto2_free(DesktopSettingsViewPinSetupHowto2* instance); +View* desktop_settings_view_pin_setup_howto2_get_view(DesktopSettingsViewPinSetupHowto2* instance); +void desktop_settings_view_pin_setup_howto2_set_context( + DesktopSettingsViewPinSetupHowto2* instance, + void* context); +void desktop_settings_view_pin_setup_howto2_set_cancel_callback( + DesktopSettingsViewPinSetupHowto2* instance, + DesktopSettingsViewPinSetupHowto2Callback callback); +void desktop_settings_view_pin_setup_howto2_set_ok_callback( + DesktopSettingsViewPinSetupHowto2* instance, + DesktopSettingsViewPinSetupHowto2Callback callback); diff --git a/applications/desktop/scenes/desktop_scene_config.h b/applications/desktop/scenes/desktop_scene_config.h index b4e39dd3540..c84d6ff89f7 100644 --- a/applications/desktop/scenes/desktop_scene_config.h +++ b/applications/desktop/scenes/desktop_scene_config.h @@ -3,5 +3,7 @@ ADD_SCENE(desktop, lock_menu, LockMenu) ADD_SCENE(desktop, debug, Debug) ADD_SCENE(desktop, first_start, FirstStart) ADD_SCENE(desktop, hw_mismatch, HwMismatch) -ADD_SCENE(desktop, pinsetup, PinSetup) ADD_SCENE(desktop, fault, Fault) +ADD_SCENE(desktop, locked, Locked) +ADD_SCENE(desktop, pin_input, PinInput) +ADD_SCENE(desktop, pin_timeout, PinTimeout) diff --git a/applications/desktop/scenes/desktop_scene_debug.c b/applications/desktop/scenes/desktop_scene_debug.c index bf8607f8888..99fcad87ef1 100644 --- a/applications/desktop/scenes/desktop_scene_debug.c +++ b/applications/desktop/scenes/desktop_scene_debug.c @@ -3,7 +3,7 @@ #include #include "../desktop_i.h" -#include "../views/desktop_debug.h" +#include "../views/desktop_view_debug.h" #include "desktop_scene.h" void desktop_scene_debug_callback(DesktopEvent event, void* context) { @@ -17,7 +17,7 @@ void desktop_scene_debug_on_enter(void* context) { desktop_debug_get_dolphin_data(desktop->debug_view); desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop); - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewDebug); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdDebug); } bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/desktop/scenes/desktop_scene_fault.c b/applications/desktop/scenes/desktop_scene_fault.c index a2555e64c1b..b75b32aa63b 100644 --- a/applications/desktop/scenes/desktop_scene_fault.c +++ b/applications/desktop/scenes/desktop_scene_fault.c @@ -25,7 +25,7 @@ void desktop_scene_fault_on_enter(void* context) { char* message = (char*)furi_hal_rtc_get_fault_data(); popup_set_text(popup, message, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter); popup_set_callback(popup, desktop_scene_fault_callback); - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewHwMismatch); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); } bool desktop_scene_fault_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/desktop/scenes/desktop_scene_first_start.c b/applications/desktop/scenes/desktop_scene_first_start.c index 8dfc96dcf64..9563094cf0b 100644 --- a/applications/desktop/scenes/desktop_scene_first_start.c +++ b/applications/desktop/scenes/desktop_scene_first_start.c @@ -2,7 +2,7 @@ #include #include "../desktop_i.h" -#include "../views/desktop_first_start.h" +#include "../views/desktop_view_first_start.h" #include "../views/desktop_events.h" void desktop_scene_first_start_callback(DesktopEvent event, void* context) { @@ -17,7 +17,7 @@ void desktop_scene_first_start_on_enter(void* context) { desktop_first_start_set_callback( first_start_view, desktop_scene_first_start_callback, desktop); - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewFirstStart); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdFirstStart); } bool desktop_scene_first_start_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/desktop/scenes/desktop_scene_hw_mismatch.c b/applications/desktop/scenes/desktop_scene_hw_mismatch.c index 7ca090b9620..05a6fd687ee 100644 --- a/applications/desktop/scenes/desktop_scene_hw_mismatch.c +++ b/applications/desktop/scenes/desktop_scene_hw_mismatch.c @@ -1,5 +1,5 @@ #include -#include +#include #include "desktop_scene.h" #include "../desktop_i.h" @@ -31,7 +31,7 @@ void desktop_scene_hw_mismatch_on_enter(void* context) { popup, "!!!! HW Mismatch !!!!", 60, 14 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter); popup_set_text(popup, text_buffer, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter); popup_set_callback(popup, desktop_scene_hw_mismatch_callback); - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewHwMismatch); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); } bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/desktop/scenes/desktop_scene_i.h b/applications/desktop/scenes/desktop_scene_i.h index f459eb4abe1..953f8c83601 100644 --- a/applications/desktop/scenes/desktop_scene_i.h +++ b/applications/desktop/scenes/desktop_scene_i.h @@ -1,7 +1,4 @@ #pragma once -typedef enum { - DesktopMainSceneStateUnlocked, - DesktopMainSceneStateLockedWithPin, - DesktopMainSceneStateLockedNoPin, -} DesktopMainSceneState; +#define SCENE_LOCKED_FIRST_ENTER 0 +#define SCENE_LOCKED_REPEAT_ENTER 1 diff --git a/applications/desktop/scenes/desktop_scene_lock_menu.c b/applications/desktop/scenes/desktop_scene_lock_menu.c index 7a3d51bba5e..c329af594b3 100644 --- a/applications/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/desktop/scenes/desktop_scene_lock_menu.c @@ -1,8 +1,13 @@ +#include +#include +#include #include #include +#include #include "../desktop_i.h" -#include "../views/desktop_lock_menu.h" +#include "../desktop_settings/desktop_settings.h" +#include "../views/desktop_view_lock_menu.h" #include "desktop_scene_i.h" #include "desktop_scene.h" @@ -15,36 +20,50 @@ void desktop_scene_lock_menu_on_enter(void* context) { Desktop* desktop = (Desktop*)context; LOAD_DESKTOP_SETTINGS(&desktop->settings); - + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); - desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pincode.length > 0); + desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pin_code.length > 0); + desktop_lock_menu_set_idx(desktop->lock_menu, 0); - uint8_t idx = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); - desktop_lock_menu_set_idx(desktop->lock_menu, idx); - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLockMenu); } bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { Desktop* desktop = (Desktop*)context; bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { + if(event.type == SceneManagerEventTypeTick) { + bool check_pin_changed = + scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); + if(check_pin_changed) { + LOAD_DESKTOP_SETTINGS(&desktop->settings); + if(desktop->settings.pin_code.length > 0) { + desktop_lock_menu_pin_set(desktop->lock_menu, 1); + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); + } + } + } else if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopLockMenuEventLock: - scene_manager_set_scene_state( - desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedNoPin); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); - scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + scene_manager_set_scene_state( + desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); consumed = true; break; case DesktopLockMenuEventPinLock: - if(desktop->settings.pincode.length > 0) { + if(desktop->settings.pin_code.length > 0) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLock); scene_manager_set_scene_state( - desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedWithPin); - scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); } else { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); - scene_manager_next_scene(desktop->scene_manager, DesktopScenePinSetup); + Loader* loader = furi_record_open("loader"); + LoaderStatus status = + loader_start(loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); + furi_check(status == LoaderStatusOk); + furi_record_close("loader"); } consumed = true; diff --git a/applications/desktop/scenes/desktop_scene_locked.c b/applications/desktop/scenes/desktop_scene_locked.c new file mode 100644 index 00000000000..98d5fa0a36d --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_locked.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include + +#include "../desktop.h" +#include "../desktop_i.h" +#include "../desktop_helpers.h" +#include "../animations/animation_manager.h" +#include "../views/desktop_events.h" +#include "../views/desktop_view_pin_input.h" +#include "../views/desktop_view_locked.h" +#include "desktop_scene.h" +#include "desktop_scene_i.h" + +#define WRONG_PIN_HEADER_TIMEOUT 3000 +#define INPUT_PIN_VIEW_TIMEOUT 15000 + +static void desktop_scene_locked_callback(DesktopEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +static void desktop_scene_locked_new_idle_animation_callback(void* context) { + furi_assert(context); + Desktop* desktop = context; + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopAnimationEventNewIdleAnimation); +} + +void desktop_scene_locked_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + + // callbacks for 1-st layer + animation_manager_set_new_idle_callback( + desktop->animation_manager, desktop_scene_locked_new_idle_animation_callback); + animation_manager_set_check_callback(desktop->animation_manager, NULL); + animation_manager_set_interact_callback(desktop->animation_manager, NULL); + + // callbacks for 2-nd layer + desktop_view_locked_set_callback(desktop->locked_view, desktop_scene_locked_callback, desktop); + + bool switch_to_timeout_scene = false; + uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); + if(state == SCENE_LOCKED_FIRST_ENTER) { + bool pin_locked = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); + desktop_helpers_lock_system(desktop, pin_locked); + if(pin_locked) { + LOAD_DESKTOP_SETTINGS(&desktop->settings); + desktop_view_locked_lock(desktop->locked_view, true); + uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails); + if(pin_timeout) { + scene_manager_set_scene_state( + desktop->scene_manager, DesktopScenePinTimeout, pin_timeout); + switch_to_timeout_scene = true; + } else { + desktop_view_locked_close_doors(desktop->locked_view); + } + } else { + desktop_view_locked_lock(desktop->locked_view, false); + desktop_view_locked_close_doors(desktop->locked_view); + } + scene_manager_set_scene_state( + desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_REPEAT_ENTER); + } + + if(switch_to_timeout_scene) { + scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout); + } else { + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLocked); + } +} + +bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopLockedEventUnlocked: + furi_hal_rtc_set_pin_fails(0); + desktop_helpers_unlock_system(desktop); + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneMain); + consumed = true; + break; + case DesktopLockedEventUpdate: + desktop_view_locked_update(desktop->locked_view); + consumed = true; + break; + case DesktopLockedEventShowPinInput: + scene_manager_next_scene(desktop->scene_manager, DesktopScenePinInput); + consumed = true; + break; + case DesktopAnimationEventNewIdleAnimation: + animation_manager_new_idle_process(desktop->animation_manager); + consumed = true; + break; + } + } + + return consumed; +} + +void desktop_scene_locked_on_exit(void* context) { +} diff --git a/applications/desktop/scenes/desktop_scene_main.c b/applications/desktop/scenes/desktop_scene_main.c index 0db7508dcab..8131d0c324b 100644 --- a/applications/desktop/scenes/desktop_scene_main.c +++ b/applications/desktop/scenes/desktop_scene_main.c @@ -4,15 +4,14 @@ #include #include -#include "desktop/desktop_i.h" -#include "desktop/views/desktop_main.h" +#include "../desktop_i.h" +#include "../views/desktop_events.h" +#include "../views/desktop_view_main.h" #include "desktop_scene.h" #include "desktop_scene_i.h" #define TAG "DesktopSrv" -#define MAIN_VIEW_DEFAULT (0UL) - static void desktop_scene_main_app_started_callback(const void* message, void* context) { furi_assert(context); Desktop* desktop = context; @@ -31,19 +30,22 @@ static void desktop_scene_main_app_started_callback(const void* message, void* c static void desktop_scene_main_new_idle_animation_callback(void* context) { furi_assert(context); Desktop* desktop = context; - view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventNewIdleAnimation); + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopAnimationEventNewIdleAnimation); } static void desktop_scene_main_check_animation_callback(void* context) { furi_assert(context); Desktop* desktop = context; - view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventCheckAnimation); + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopAnimationEventCheckAnimation); } static void desktop_scene_main_interact_animation_callback(void* context) { furi_assert(context); Desktop* desktop = context; - view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventInteractAnimation); + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopAnimationEventInteractAnimation); } static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { @@ -80,7 +82,6 @@ void desktop_scene_main_on_enter(void* context) { desktop->animation_manager, desktop_scene_main_check_animation_callback); animation_manager_set_interact_callback( desktop->animation_manager, desktop_scene_main_interact_animation_callback); - desktop_locked_set_callback(desktop->locked_view, desktop_scene_main_callback, desktop); furi_assert(osSemaphoreGetCount(desktop->unload_animation_semaphore) == 0); Loader* loader = furi_record_open("loader"); @@ -90,24 +91,7 @@ void desktop_scene_main_on_enter(void* context) { desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); - DesktopMainSceneState state = - scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain); - if(state == DesktopMainSceneStateLockedNoPin) { - desktop_locked_lock(desktop->locked_view); - view_port_enabled_set(desktop->lock_viewport, true); - } else if(state == DesktopMainSceneStateLockedWithPin) { - LOAD_DESKTOP_SETTINGS(&desktop->settings); - furi_assert(desktop->settings.pincode.length > 0); - desktop_locked_lock_pincode(desktop->locked_view, desktop->settings.pincode); - view_port_enabled_set(desktop->lock_viewport, true); - furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - furi_hal_usb_disable(); - } else { - furi_assert(state == DesktopMainSceneStateUnlocked); - view_port_enabled_set(desktop->lock_viewport, false); - } - - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdMain); } bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { @@ -154,15 +138,15 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; break; - case DesktopMainEventCheckAnimation: + case DesktopAnimationEventCheckAnimation: animation_manager_check_blocking_process(desktop->animation_manager); consumed = true; break; - case DesktopMainEventNewIdleAnimation: + case DesktopAnimationEventNewIdleAnimation: animation_manager_new_idle_process(desktop->animation_manager); consumed = true; break; - case DesktopMainEventInteractAnimation: + case DesktopAnimationEventInteractAnimation: animation_manager_interact_process(desktop->animation_manager); consumed = true; break; @@ -175,16 +159,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { animation_manager_load_and_continue_animation(desktop->animation_manager); consumed = true; break; - case DesktopMainEventUnlocked: - consumed = true; - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); - furi_hal_usb_enable(); - view_port_enabled_set(desktop->lock_viewport, false); - scene_manager_set_scene_state( - desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateUnlocked); - break; - case DesktopMainEventUpdate: - desktop_locked_update(desktop->locked_view); + case DesktopLockedEventUpdate: + desktop_view_locked_update(desktop->locked_view); consumed = true; break; @@ -213,5 +189,4 @@ void desktop_scene_main_on_exit(void* context) { animation_manager_set_check_callback(desktop->animation_manager, NULL); animation_manager_set_interact_callback(desktop->animation_manager, NULL); animation_manager_set_context(desktop->animation_manager, desktop); - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneMain, MAIN_VIEW_DEFAULT); } diff --git a/applications/desktop/scenes/desktop_scene_pin_input.c b/applications/desktop/scenes/desktop_scene_pin_input.c new file mode 100644 index 00000000000..8d714ff9e48 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_pin_input.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../desktop.h" +#include "../desktop_i.h" +#include "../animations/animation_manager.h" +#include "../views/desktop_events.h" +#include "../views/desktop_view_pin_input.h" +#include "../desktop_helpers.h" +#include "desktop_scene.h" +#include "desktop_scene_i.h" + +#define WRONG_PIN_HEADER_TIMEOUT 3000 +#define INPUT_PIN_VIEW_TIMEOUT 15000 + +typedef struct { + TimerHandle_t timer; +} DesktopScenePinInputState; + +static void desktop_scene_locked_light_red(bool value) { + NotificationApp* app = furi_record_open("notification"); + if(value) { + notification_message(app, &sequence_set_only_red_255); + } else { + notification_message(app, &sequence_reset_red); + } + furi_record_close("notification"); +} + +static void + desktop_scene_pin_input_set_timer(Desktop* desktop, bool enable, TickType_t new_period) { + furi_assert(desktop); + + DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state( + desktop->scene_manager, DesktopScenePinInput); + furi_assert(state); + if(enable) { + xTimerChangePeriod(state->timer, new_period, portMAX_DELAY); + } else { + xTimerStop(state->timer, portMAX_DELAY); + } +} + +static void desktop_scene_pin_input_back_callback(void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventBack); +} + +static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { + Desktop* desktop = (Desktop*)context; + if(pins_are_equal(&desktop->settings.pin_code, pin_code)) { + view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); + } else { + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopPinInputEventUnlockFailed); + } +} + +static void desktop_scene_pin_input_timer_callback(TimerHandle_t timer) { + Desktop* desktop = pvTimerGetTimerID(timer); + + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopPinInputEventResetWrongPinLabel); +} + +void desktop_scene_pin_input_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + + desktop_view_pin_input_set_context(desktop->pin_input_view, desktop); + desktop_view_pin_input_set_back_callback( + desktop->pin_input_view, desktop_scene_pin_input_back_callback); + desktop_view_pin_input_set_timeout_callback( + desktop->pin_input_view, desktop_scene_pin_input_back_callback); + desktop_view_pin_input_set_done_callback( + desktop->pin_input_view, desktop_scene_pin_input_done_callback); + + DesktopScenePinInputState* state = furi_alloc(sizeof(DesktopScenePinInputState)); + state->timer = + xTimerCreate(NULL, 10000, pdFALSE, desktop, desktop_scene_pin_input_timer_callback); + scene_manager_set_scene_state(desktop->scene_manager, DesktopScenePinInput, (uint32_t)state); + + desktop_view_pin_input_hide_pin(desktop->pin_input_view, true); + desktop_view_pin_input_set_label_button(desktop->pin_input_view, "OK"); + desktop_view_pin_input_set_label_secondary(desktop->pin_input_view, 44, 25, "Enter PIN:"); + desktop_view_pin_input_set_pin_position(desktop->pin_input_view, 64, 37); + desktop_view_pin_input_reset_pin(desktop->pin_input_view); + + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPinInput); +} + +bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + uint32_t pin_fails = 0; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopPinInputEventUnlockFailed: + pin_fails = furi_hal_rtc_get_pin_fails(); + pin_fails++; + furi_hal_rtc_set_pin_fails(pin_fails); + uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails); + if(pin_timeout > 0) { + desktop_helpers_emit_error_notification(); + scene_manager_set_scene_state( + desktop->scene_manager, DesktopScenePinTimeout, pin_timeout); + scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout); + } else { + desktop_scene_locked_light_red(true); + desktop_view_pin_input_set_label_primary(desktop->pin_input_view, 0, 0, NULL); + desktop_view_pin_input_set_label_secondary( + desktop->pin_input_view, 25, 25, "Wrong PIN try again:"); + desktop_scene_pin_input_set_timer(desktop, true, WRONG_PIN_HEADER_TIMEOUT); + desktop_view_pin_input_reset_pin(desktop->pin_input_view); + } + consumed = true; + break; + case DesktopPinInputEventResetWrongPinLabel: + desktop_scene_locked_light_red(false); + desktop_view_pin_input_set_label_primary(desktop->pin_input_view, 0, 0, NULL); + desktop_view_pin_input_set_label_secondary( + desktop->pin_input_view, 44, 25, "Enter PIN:"); + consumed = true; + break; + case DesktopPinInputEventUnlocked: + desktop_view_locked_unlock(desktop->locked_view); + furi_hal_rtc_set_pin_fails(0); + desktop_helpers_unlock_system(desktop); + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneMain); + consumed = true; + break; + case DesktopPinInputEventBack: + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneLocked); + consumed = true; + break; + } + } + + return consumed; +} + +void desktop_scene_pin_input_on_exit(void* context) { + Desktop* desktop = (Desktop*)context; + desktop_scene_locked_light_red(false); + + DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state( + desktop->scene_manager, DesktopScenePinInput); + xTimerStop(state->timer, portMAX_DELAY); + while(xTimerIsTimerActive(state->timer)) { + delay(1); + } + xTimerDelete(state->timer, portMAX_DELAY); + free(state); +} diff --git a/applications/desktop/scenes/desktop_scene_pin_timeout.c b/applications/desktop/scenes/desktop_scene_pin_timeout.c new file mode 100644 index 00000000000..3c267a0dd55 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_pin_timeout.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include + +#include "../desktop_i.h" +#include "../views/desktop_view_pin_timeout.h" +#include "desktop_scene.h" +#include "desktop_scene_i.h" + +static void desktop_scene_pin_timeout_callback(void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinTimeoutExit); +} + +void desktop_scene_pin_timeout_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + + uint32_t timeout = + scene_manager_get_scene_state(desktop->scene_manager, DesktopScenePinTimeout); + desktop_view_pin_timeout_start(desktop->pin_timeout_view, timeout); + desktop_view_pin_timeout_set_callback( + desktop->pin_timeout_view, desktop_scene_pin_timeout_callback, desktop); + + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPinTimeout); +} + +bool desktop_scene_pin_timeout_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopPinTimeoutExit: + scene_manager_previous_scene(desktop->scene_manager); + consumed = true; + break; + } + } + + return consumed; +} + +void desktop_scene_pin_timeout_on_exit(void* context) { +} diff --git a/applications/desktop/scenes/desktop_scene_pinsetup.c b/applications/desktop/scenes/desktop_scene_pinsetup.c deleted file mode 100644 index 6b1c968692e..00000000000 --- a/applications/desktop/scenes/desktop_scene_pinsetup.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "../desktop_i.h" - -#define SCENE_EXIT_EVENT (0U) - -void desktop_scene_ok_callback(void* context) { - Desktop* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); -} - -void desktop_scene_pinsetup_on_enter(void* context) { - Desktop* app = context; - CodeInput* code_input = app->code_input; - - code_input_set_result_callback( - code_input, - desktop_scene_ok_callback, - NULL, - app, - app->settings.pincode.data, - &app->settings.pincode.length, - true); - - view_dispatcher_switch_to_view(app->view_dispatcher, DesktopViewPinSetup); -} - -bool desktop_scene_pinsetup_on_event(void* context, SceneManagerEvent event) { - Desktop* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case SCENE_EXIT_EVENT: - scene_manager_previous_scene(app->scene_manager); - consumed = true; - break; - - default: - consumed = true; - break; - } - } - return consumed; -} - -void desktop_scene_pinsetup_on_exit(void* context) { - Desktop* app = context; - SAVE_DESKTOP_SETTINGS(&app->settings); - code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0); - code_input_set_header_text(app->code_input, ""); -} diff --git a/applications/desktop/views/desktop_events.h b/applications/desktop/views/desktop_events.h index 56d9729f42c..49d6f81e574 100644 --- a/applications/desktop/views/desktop_events.h +++ b/applications/desktop/views/desktop_events.h @@ -6,24 +6,35 @@ typedef enum { DesktopMainEventOpenFavorite, DesktopMainEventOpenMenu, DesktopMainEventOpenDebug, - DesktopMainEventUpdate, - DesktopMainEventUnlocked, DesktopMainEventRightShort, - DesktopMainEventCheckAnimation, - DesktopMainEventNewIdleAnimation, - DesktopMainEventInteractAnimation, DesktopMainEventBeforeAppStarted, DesktopMainEventAfterAppFinished, - DesktopLockedEventUnlock, - DesktopLockedEventCheckAnimation, - DesktopLockedEventMax, + + DesktopLockedEventUnlocked, + DesktopLockedEventUpdate, + DesktopLockedEventShowPinInput, + + DesktopPinInputEventResetWrongPinLabel, + DesktopPinInputEventUnlocked, + DesktopPinInputEventUnlockFailed, + DesktopPinInputEventBack, + + DesktopPinTimeoutExit, + DesktopDebugEventDeed, DesktopDebugEventWrongDeed, DesktopDebugEventSaveState, DesktopDebugEventExit, + DesktopFirstStartCompleted, DesktopFirstStartPoweroff, + DesktopLockMenuEventLock, DesktopLockMenuEventPinLock, DesktopLockMenuEventExit, + + DesktopAnimationEventCheckAnimation, + DesktopAnimationEventNewIdleAnimation, + DesktopAnimationEventInteractAnimation, + } DesktopEvent; diff --git a/applications/desktop/views/desktop_locked.c b/applications/desktop/views/desktop_locked.c deleted file mode 100644 index d8102f574f1..00000000000 --- a/applications/desktop/views/desktop_locked.c +++ /dev/null @@ -1,247 +0,0 @@ -#include "desktop/desktop_settings/desktop_settings.h" -#include "furi/check.h" -#include "gui/view.h" -#include "portmacro.h" -#include -#include -#include -#include "../desktop_i.h" -#include "desktop_locked.h" -#include - -#define DOOR_MOVING_INTERVAL_MS (1000 / 16) -#define UNLOCKED_HINT_TIMEOUT_MS (2000) - -struct DesktopLockedView { - View* view; - DesktopLockedViewCallback callback; - void* context; - - TimerHandle_t timer; - uint8_t lock_count; - uint32_t lock_lastpress; - - PinCode pincode; - PinCode pincode_input; -}; - -typedef struct { - uint32_t hint_icon_expire_at; - bool unlocked_hint; - bool locked; - bool pin_locked; - - int8_t door_left_x; - int8_t door_right_x; - bool animation_seq_end; -} DesktopLockedViewModel; - -static void desktop_locked_unlock(DesktopLockedView* locked_view); - -void desktop_locked_set_callback( - DesktopLockedView* locked_view, - DesktopLockedViewCallback callback, - void* context) { - furi_assert(locked_view); - furi_assert(callback); - locked_view->callback = callback; - locked_view->context = context; -} - -void locked_view_timer_callback(TimerHandle_t timer) { - DesktopLockedView* locked_view = pvTimerGetTimerID(timer); - locked_view->callback(DesktopMainEventUpdate, locked_view->context); -} - -static void desktop_locked_update_hint_icon_timeout(DesktopLockedView* locked_view) { - DesktopLockedViewModel* model = view_get_model(locked_view->view); - model->hint_icon_expire_at = osKernelGetTickCount() + osKernelGetTickFreq(); - view_commit_model(locked_view->view, true); -} - -static void desktop_locked_reset_door_pos(DesktopLockedView* locked_view) { - DesktopLockedViewModel* model = view_get_model(locked_view->view); - model->animation_seq_end = false; - model->door_left_x = DOOR_L_POS; - model->door_right_x = DOOR_R_POS; - view_commit_model(locked_view->view, true); -} - -void desktop_locked_update(DesktopLockedView* locked_view) { - bool stop_timer = false; - - DesktopLockedViewModel* model = view_get_model(locked_view->view); - if(model->locked) { - if(model->door_left_x != DOOR_L_POS_MAX) { - model->door_left_x = CLAMP(model->door_left_x + 5, DOOR_L_POS_MAX, DOOR_L_POS); - model->door_right_x = CLAMP(model->door_right_x - 5, DOOR_R_POS, DOOR_R_POS_MIN); - } else { - model->animation_seq_end = true; - } - stop_timer = model->animation_seq_end; - } else { - model->unlocked_hint = false; - stop_timer = true; - } - view_commit_model(locked_view->view, true); - - if(stop_timer) { - xTimerStop(locked_view->timer, portMAX_DELAY); - } -} - -void desktop_locked_draw(Canvas* canvas, void* model) { - DesktopLockedViewModel* m = model; - uint32_t now = osKernelGetTickCount(); - canvas_set_color(canvas, ColorBlack); - - if(m->locked) { - if(!m->animation_seq_end) { - canvas_draw_icon(canvas, m->door_left_x, 0 + STATUS_BAR_Y_SHIFT, &I_DoorLeft_70x55); - canvas_draw_icon(canvas, m->door_right_x, 0 + STATUS_BAR_Y_SHIFT, &I_DoorRight_70x55); - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked"); - } else if((now < m->hint_icon_expire_at) && !m->pin_locked) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_icon(canvas, 13, 2 + STATUS_BAR_Y_SHIFT, &I_LockPopup_100x49); - elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, "To unlock\npress:"); - } - } else { - if(m->unlocked_hint) { - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked"); - } - } -} - -View* desktop_locked_get_view(DesktopLockedView* locked_view) { - furi_assert(locked_view); - return locked_view->view; -} - -bool desktop_locked_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - DesktopLockedView* locked_view = context; - bool locked = false; - bool locked_with_pin = false; - uint32_t press_time = xTaskGetTickCount(); - - { - DesktopLockedViewModel* model = view_get_model(locked_view->view); - bool changed = false; - locked = model->locked; - locked_with_pin = model->pin_locked; - if(!locked && model->unlocked_hint && event->type == InputTypePress) { - model->unlocked_hint = false; - changed = true; - } - view_commit_model(locked_view->view, changed); - } - - if(!locked || (event->type != InputTypeShort)) { - return locked; - } - - if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { - locked_view->lock_lastpress = press_time; - locked_view->lock_count = 0; - locked_view->pincode_input.length = 0; - } - - if(locked_with_pin) { - locked_view->pincode_input.length = code_input_push( - locked_view->pincode_input.data, locked_view->pincode_input.length, event->key); - bool match = code_input_compare( - locked_view->pincode_input.data, - locked_view->pincode_input.length, - locked_view->pincode.data, - locked_view->pincode.length); - - if(match) { - desktop_locked_unlock(locked_view); - } - } else { - if(event->key == InputKeyBack) { - locked_view->lock_lastpress = press_time; - locked_view->lock_count++; - if(locked_view->lock_count == UNLOCK_CNT) { - desktop_locked_unlock(locked_view); - } - } else { - desktop_locked_update_hint_icon_timeout(locked_view); - locked_view->lock_count = 0; - } - } - - locked_view->lock_lastpress = press_time; - - return locked; -} - -DesktopLockedView* desktop_locked_alloc() { - DesktopLockedView* locked_view = furi_alloc(sizeof(DesktopLockedView)); - locked_view->view = view_alloc(); - locked_view->timer = - xTimerCreate("Locked view", 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback); - - view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopLockedViewModel)); - view_set_context(locked_view->view, locked_view); - view_set_draw_callback(locked_view->view, (ViewDrawCallback)desktop_locked_draw); - view_set_input_callback(locked_view->view, desktop_locked_input); - - return locked_view; -} - -void desktop_locked_free(DesktopLockedView* locked_view) { - furi_assert(locked_view); - osTimerDelete(locked_view->timer); - view_free(locked_view->view); - free(locked_view); -} - -void desktop_locked_lock(DesktopLockedView* locked_view) { - locked_view->pincode.length = 0; - DesktopLockedViewModel* model = view_get_model(locked_view->view); - model->locked = true; - model->pin_locked = false; - view_commit_model(locked_view->view, true); - desktop_locked_reset_door_pos(locked_view); - xTimerChangePeriod(locked_view->timer, DOOR_MOVING_INTERVAL_MS, portMAX_DELAY); - - Gui* gui = furi_record_open("gui"); - gui_set_lockdown(gui, true); - furi_record_close("gui"); -} - -void desktop_locked_lock_pincode(DesktopLockedView* locked_view, PinCode pincode) { - locked_view->pincode = pincode; - locked_view->pincode_input.length = 0; - DesktopLockedViewModel* model = view_get_model(locked_view->view); - model->locked = true; - model->pin_locked = true; - view_commit_model(locked_view->view, true); - desktop_locked_reset_door_pos(locked_view); - xTimerChangePeriod(locked_view->timer, DOOR_MOVING_INTERVAL_MS, portMAX_DELAY); - - Gui* gui = furi_record_open("gui"); - gui_set_lockdown(gui, true); - furi_record_close("gui"); -} - -static void desktop_locked_unlock(DesktopLockedView* locked_view) { - furi_assert(locked_view); - - locked_view->lock_count = 0; - DesktopLockedViewModel* model = view_get_model(locked_view->view); - model->locked = false; - model->pin_locked = false; - model->unlocked_hint = true; - view_commit_model(locked_view->view, true); - locked_view->callback(DesktopMainEventUnlocked, locked_view->context); - xTimerChangePeriod(locked_view->timer, UNLOCKED_HINT_TIMEOUT_MS, portMAX_DELAY); - - Gui* gui = furi_record_open("gui"); - gui_set_lockdown(gui, false); - furi_record_close("gui"); -} diff --git a/applications/desktop/views/desktop_locked.h b/applications/desktop/views/desktop_locked.h deleted file mode 100644 index 61e91e4ec46..00000000000 --- a/applications/desktop/views/desktop_locked.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include "desktop_events.h" - -#define UNLOCK_RST_TIMEOUT 300 -#define UNLOCK_CNT 3 - -#define DOOR_L_POS -57 -#define DOOR_L_POS_MAX 0 -#define DOOR_R_POS 115 -#define DOOR_R_POS_MIN 60 - -typedef enum { - DesktopLockedWithPin, - DesktopLockedNoPin, -} DesktopLockedSceneState; - -typedef struct DesktopLockedView DesktopLockedView; - -typedef void (*DesktopLockedViewCallback)(DesktopEvent event, void* context); - -void desktop_locked_set_callback( - DesktopLockedView* locked_view, - DesktopLockedViewCallback callback, - void* context); - -void desktop_locked_update(DesktopLockedView* locked_view); - -View* desktop_locked_get_view(DesktopLockedView* locked_view); -DesktopLockedView* desktop_locked_alloc(); -void desktop_locked_free(DesktopLockedView* locked_view); - -void desktop_locked_lock_pincode(DesktopLockedView* locked_view, PinCode pincode); -void desktop_locked_lock(DesktopLockedView* locked_view); diff --git a/applications/desktop/views/desktop_debug.c b/applications/desktop/views/desktop_view_debug.c similarity index 98% rename from applications/desktop/views/desktop_debug.c rename to applications/desktop/views/desktop_view_debug.c index 0d793dcae73..0ffd8d17a16 100644 --- a/applications/desktop/views/desktop_debug.c +++ b/applications/desktop/views/desktop_view_debug.c @@ -1,11 +1,11 @@ #include #include #include +#include +#include #include "../desktop_i.h" -#include "desktop_debug.h" -#include "dolphin/helpers/dolphin_state.h" -#include "dolphin/dolphin.h" +#include "desktop_view_debug.h" void desktop_debug_set_callback( DesktopDebugView* debug_view, diff --git a/applications/desktop/views/desktop_debug.h b/applications/desktop/views/desktop_view_debug.h similarity index 100% rename from applications/desktop/views/desktop_debug.h rename to applications/desktop/views/desktop_view_debug.h diff --git a/applications/desktop/views/desktop_first_start.c b/applications/desktop/views/desktop_view_first_start.c similarity index 99% rename from applications/desktop/views/desktop_first_start.c rename to applications/desktop/views/desktop_view_first_start.c index c0ef3a5f43c..5408dac0f11 100644 --- a/applications/desktop/views/desktop_first_start.c +++ b/applications/desktop/views/desktop_view_first_start.c @@ -1,8 +1,9 @@ #include #include #include + #include "../desktop_i.h" -#include "desktop_first_start.h" +#include "desktop_view_first_start.h" #define DESKTOP_FIRST_START_POWEROFF_SHORT 5000 #define DESKTOP_FIRST_START_POWEROFF_LONG (60 * 60 * 1000) diff --git a/applications/desktop/views/desktop_first_start.h b/applications/desktop/views/desktop_view_first_start.h similarity index 100% rename from applications/desktop/views/desktop_first_start.h rename to applications/desktop/views/desktop_view_first_start.h diff --git a/applications/desktop/views/desktop_lock_menu.c b/applications/desktop/views/desktop_view_lock_menu.c similarity index 99% rename from applications/desktop/views/desktop_lock_menu.c rename to applications/desktop/views/desktop_view_lock_menu.c index d65aa30d58f..005512cf95e 100644 --- a/applications/desktop/views/desktop_lock_menu.c +++ b/applications/desktop/views/desktop_view_lock_menu.c @@ -2,7 +2,7 @@ #include #include "../desktop_i.h" -#include "desktop_lock_menu.h" +#include "desktop_view_lock_menu.h" #define LOCK_MENU_ITEMS_NB 3 diff --git a/applications/desktop/views/desktop_lock_menu.h b/applications/desktop/views/desktop_view_lock_menu.h similarity index 100% rename from applications/desktop/views/desktop_lock_menu.h rename to applications/desktop/views/desktop_view_lock_menu.h diff --git a/applications/desktop/views/desktop_view_locked.c b/applications/desktop/views/desktop_view_locked.c new file mode 100644 index 00000000000..8e3f22ee0e2 --- /dev/null +++ b/applications/desktop/views/desktop_view_locked.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../desktop_settings/desktop_settings.h" +#include "../desktop_i.h" +#include "desktop_view_locked.h" + +#define DOOR_MOVING_INTERVAL_MS (1000 / 16) +#define UNLOCKED_HINT_TIMEOUT_MS (2000) + +#define DOOR_OFFSET_START -55 +#define DOOR_OFFSET_END 0 + +#define DOOR_L_FINAL_POS 0 +#define DOOR_R_FINAL_POS 60 + +#define UNLOCK_CNT 3 +#define UNLOCK_RST_TIMEOUT 600 + +struct DesktopViewLocked { + View* view; + DesktopViewLockedCallback callback; + void* context; + + TimerHandle_t timer; + uint8_t lock_count; + uint32_t lock_lastpress; +}; + +typedef struct { + uint32_t hint_icon_expire_at; + bool unlocked_hint; + bool locked; + bool pin_locked; + + int8_t door_offset; + bool doors_closing; +} DesktopViewLockedModel; + +void desktop_view_locked_set_callback( + DesktopViewLocked* locked_view, + DesktopViewLockedCallback callback, + void* context) { + furi_assert(locked_view); + furi_assert(callback); + locked_view->callback = callback; + locked_view->context = context; +} + +static void locked_view_timer_callback(TimerHandle_t timer) { + DesktopViewLocked* locked_view = pvTimerGetTimerID(timer); + locked_view->callback(DesktopLockedEventUpdate, locked_view->context); +} + +static void desktop_view_locked_doors_draw(Canvas* canvas, DesktopViewLockedModel* model) { + int8_t offset = model->door_offset; + uint8_t door_left_x = DOOR_L_FINAL_POS + offset; + uint8_t door_right_x = DOOR_R_FINAL_POS - offset; + uint8_t height = icon_get_height(&I_DoorLeft_70x55); + canvas_draw_icon(canvas, door_left_x, canvas_height(canvas) - height, &I_DoorLeft_70x55); + canvas_draw_icon(canvas, door_right_x, canvas_height(canvas) - height, &I_DoorRight_70x55); +} + +static bool desktop_view_locked_doors_move(DesktopViewLockedModel* model) { + bool stop = false; + if(model->door_offset < DOOR_OFFSET_END) { + model->door_offset = CLAMP(model->door_offset + 5, DOOR_OFFSET_END, DOOR_OFFSET_START); + stop = true; + } + + return stop; +} + +static void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* locked_view) { + DesktopViewLockedModel* model = view_get_model(locked_view->view); + model->hint_icon_expire_at = osKernelGetTickCount() + osKernelGetTickFreq(); + view_commit_model(locked_view->view, true); +} + +void desktop_view_locked_update(DesktopViewLocked* locked_view) { + bool stop_timer = false; + + DesktopViewLockedModel* model = view_get_model(locked_view->view); + if(model->locked) { + model->doors_closing = desktop_view_locked_doors_move(model); + stop_timer = !model->doors_closing; + } else { + model->unlocked_hint = false; + stop_timer = true; + } + view_commit_model(locked_view->view, true); + + if(stop_timer) { + xTimerStop(locked_view->timer, portMAX_DELAY); + } +} + +static void desktop_view_locked_draw(Canvas* canvas, void* model) { + DesktopViewLockedModel* m = model; + uint32_t now = osKernelGetTickCount(); + canvas_set_color(canvas, ColorBlack); + + if(m->locked) { + if(m->doors_closing) { + desktop_view_locked_doors_draw(canvas, m); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked"); + } else if((now < m->hint_icon_expire_at) && !m->pin_locked) { + canvas_set_font(canvas, FontSecondary); + elements_bold_rounded_frame(canvas, 14, 2 + STATUS_BAR_Y_SHIFT, 99, 48); + elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, "To unlock\npress:"); + canvas_draw_icon(canvas, 65, 36 + STATUS_BAR_Y_SHIFT, &I_Back3_45x8); + canvas_draw_icon(canvas, 16, 7 + STATUS_BAR_Y_SHIFT, &I_WarningDolphin_45x42); + canvas_draw_dot(canvas, 17, 61); + } + } else { + if(m->unlocked_hint) { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked"); + } + } +} + +View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) { + furi_assert(locked_view); + return locked_view->view; +} + +static bool desktop_view_locked_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + DesktopViewLocked* locked_view = context; + bool locked = false; + bool locked_with_pin = false; + bool doors_closing = false; + uint32_t press_time = xTaskGetTickCount(); + + { + DesktopViewLockedModel* model = view_get_model(locked_view->view); + bool changed = false; + locked = model->locked; + locked_with_pin = model->pin_locked; + doors_closing = model->doors_closing; + if(!locked && model->unlocked_hint && event->type == InputTypePress) { + model->unlocked_hint = false; + changed = true; + } + view_commit_model(locked_view->view, changed); + } + + if(!locked || doors_closing || (event->type != InputTypeShort)) { + return locked; + } + + if(locked_with_pin) { + locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context); + } else { + if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { + locked_view->lock_lastpress = press_time; + locked_view->lock_count = 0; + } + + desktop_view_locked_update_hint_icon_timeout(locked_view); + if(event->key == InputKeyBack) { + locked_view->lock_lastpress = press_time; + locked_view->lock_count++; + if(locked_view->lock_count == UNLOCK_CNT) { + desktop_view_locked_unlock(locked_view); + locked_view->callback(DesktopLockedEventUnlocked, locked_view->context); + } + } else { + locked_view->lock_count = 0; + } + + locked_view->lock_lastpress = press_time; + } + + return locked; +} + +DesktopViewLocked* desktop_view_locked_alloc() { + DesktopViewLocked* locked_view = furi_alloc(sizeof(DesktopViewLocked)); + locked_view->view = view_alloc(); + locked_view->timer = + xTimerCreate(NULL, 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback); + + locked_view->view = view_alloc(); + view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopViewLockedModel)); + view_set_context(locked_view->view, locked_view); + view_set_draw_callback(locked_view->view, desktop_view_locked_draw); + view_set_input_callback(locked_view->view, desktop_view_locked_input); + + return locked_view; +} + +void desktop_view_locked_free(DesktopViewLocked* locked_view) { + furi_assert(locked_view); + osTimerDelete(locked_view->timer); + view_free(locked_view->view); + free(locked_view); +} + +void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) { + DesktopViewLockedModel* model = view_get_model(locked_view->view); + model->doors_closing = true; + model->door_offset = DOOR_OFFSET_START; + view_commit_model(locked_view->view, true); + xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(DOOR_MOVING_INTERVAL_MS), portMAX_DELAY); +} + +void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked) { + DesktopViewLockedModel* model = view_get_model(locked_view->view); + model->locked = true; + model->pin_locked = pin_locked; + view_commit_model(locked_view->view, true); +} + +void desktop_view_locked_unlock(DesktopViewLocked* locked_view) { + furi_assert(locked_view); + + locked_view->lock_count = 0; + DesktopViewLockedModel* model = view_get_model(locked_view->view); + model->locked = false; + model->pin_locked = false; + model->unlocked_hint = true; + view_commit_model(locked_view->view, true); + xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(UNLOCKED_HINT_TIMEOUT_MS), portMAX_DELAY); +} diff --git a/applications/desktop/views/desktop_view_locked.h b/applications/desktop/views/desktop_view_locked.h new file mode 100644 index 00000000000..60fe791bcba --- /dev/null +++ b/applications/desktop/views/desktop_view_locked.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../desktop_settings/desktop_settings.h" +#include "../views/desktop_events.h" +#include + +typedef struct DesktopViewLocked DesktopViewLocked; + +typedef void (*DesktopViewLockedCallback)(DesktopEvent event, void* context); + +void desktop_view_locked_set_callback( + DesktopViewLocked* locked_view, + DesktopViewLockedCallback callback, + void* context); +void desktop_view_locked_update(DesktopViewLocked* locked_view); +View* desktop_view_locked_get_view(DesktopViewLocked* locked_view); +DesktopViewLocked* desktop_view_locked_alloc(); +void desktop_view_locked_free(DesktopViewLocked* locked_view); +void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked); +void desktop_view_locked_unlock(DesktopViewLocked* locked_view); +void desktop_view_locked_close_doors(DesktopViewLocked* locked_view); diff --git a/applications/desktop/views/desktop_main.c b/applications/desktop/views/desktop_view_main.c similarity index 98% rename from applications/desktop/views/desktop_main.c rename to applications/desktop/views/desktop_view_main.c index 83d561df5fc..51520863a49 100644 --- a/applications/desktop/views/desktop_main.c +++ b/applications/desktop/views/desktop_view_main.c @@ -7,7 +7,7 @@ #include #include "../desktop_i.h" -#include "desktop_main.h" +#include "desktop_view_main.h" struct DesktopMainView { View* view; diff --git a/applications/desktop/views/desktop_main.h b/applications/desktop/views/desktop_view_main.h similarity index 100% rename from applications/desktop/views/desktop_main.h rename to applications/desktop/views/desktop_view_main.h diff --git a/applications/desktop/views/desktop_view_pin_input.c b/applications/desktop/views/desktop_view_pin_input.c new file mode 100644 index 00000000000..b8976ca210b --- /dev/null +++ b/applications/desktop/views/desktop_view_pin_input.c @@ -0,0 +1,340 @@ +#include +#include +#include +#include +#include +#include + +#include "desktop_view_pin_input.h" +#include "../desktop_settings/desktop_settings.h" + +#define NO_ACTIVITY_TIMEOUT 15000 + +#define PIN_CELL_WIDTH 13 +#define DEFAULT_PIN_X 64 +#define DEFAULT_PIN_Y 32 + +struct DesktopViewPinInput { + View* view; + DesktopViewPinInputCallback back_callback; + DesktopViewPinInputCallback timeout_callback; + DesktopViewPinInputDoneCallback done_callback; + void* context; + TimerHandle_t timer; +}; + +typedef struct { + PinCode pin; + bool pin_hidden; + bool locked_input; + uint8_t pin_x; + uint8_t pin_y; + const char* primary_str; + uint8_t primary_str_x; + uint8_t primary_str_y; + const char* secondary_str; + uint8_t secondary_str_x; + uint8_t secondary_str_y; + const char* button_label; +} DesktopViewPinInputModel; + +static bool desktop_view_pin_input_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopViewPinInput* pin_input = context; + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + + bool call_back_callback = false; + bool call_done_callback = false; + PinCode pin_code = {0}; + + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyRight: + case InputKeyLeft: + case InputKeyDown: + case InputKeyUp: + if(!model->locked_input) { + if(model->pin.length < MAX_PIN_SIZE) { + model->pin.data[model->pin.length++] = event->key; + } + } + break; + case InputKeyOk: + if(model->pin.length >= MIN_PIN_SIZE) { + call_done_callback = true; + pin_code = model->pin; + } + break; + case InputKeyBack: + if(!model->locked_input) { + if(model->pin.length > 0) { + model->pin.length = 0; + } else { + call_back_callback = true; + } + } + break; + default: + furi_assert(0); + break; + } + } + view_commit_model(pin_input->view, true); + + if(call_done_callback && pin_input->done_callback) { + pin_input->done_callback(&pin_code, pin_input->context); + } else if(call_back_callback && pin_input->back_callback) { + pin_input->back_callback(pin_input->context); + } + + xTimerStart(pin_input->timer, 0); + + return true; +} + +static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInputModel* model) { + furi_assert(canvas); + furi_assert(model); + + uint8_t draw_pin_size = MAX(4, model->pin.length + 1); + if(model->locked_input || (model->pin.length == MAX_PIN_SIZE)) { + draw_pin_size = model->pin.length; + } + + uint8_t x = model->pin_x - (draw_pin_size * (PIN_CELL_WIDTH - 1)) / 2; + uint8_t y = model->pin_y - (PIN_CELL_WIDTH / 2); + + for(int i = 0; i < draw_pin_size; ++i) { + canvas_draw_frame(canvas, x, y, PIN_CELL_WIDTH, PIN_CELL_WIDTH); + if(i < model->pin.length) { + if(model->pin_hidden) { + canvas_draw_icon(canvas, x + 3, y + 3, &I_Pin_star_7x7); + } else { + switch(model->pin.data[i]) { + case InputKeyDown: + canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9); + break; + case InputKeyUp: + canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up7x9); + break; + case InputKeyLeft: + canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7); + break; + case InputKeyRight: + canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_right_9x7); + break; + default: + furi_assert(0); + break; + } + } + } else if(i == model->pin.length) { + canvas_draw_icon(canvas, x + 4, y + PIN_CELL_WIDTH + 1, &I_Pin_pointer_5x3); + } + x += PIN_CELL_WIDTH - 1; + } +} + +static void desktop_view_pin_input_draw(Canvas* canvas, void* context) { + furi_assert(canvas); + furi_assert(context); + + canvas_set_font(canvas, FontSecondary); + DesktopViewPinInputModel* model = context; + desktop_view_pin_input_draw_cells(canvas, model); + + if((model->pin.length > 0) && !model->locked_input) { + canvas_draw_icon(canvas, 4, 53, &I_Pin_back_full_40x8); + } + + if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) { + elements_button_center(canvas, model->button_label); + } + + if(model->primary_str) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, model->primary_str_x, model->primary_str_y, model->primary_str); + canvas_set_font(canvas, FontSecondary); + } + + if(model->secondary_str) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str( + canvas, model->secondary_str_x, model->secondary_str_y, model->secondary_str); + } +} + +void desktop_view_pin_input_timer_callback(TimerHandle_t timer) { + DesktopViewPinInput* pin_input = pvTimerGetTimerID(timer); + + if(pin_input->timeout_callback) { + pin_input->timeout_callback(pin_input->context); + } +} + +static void desktop_view_pin_input_enter(void* context) { + DesktopViewPinInput* pin_input = context; + xTimerStart(pin_input->timer, portMAX_DELAY); +} + +static void desktop_view_pin_input_exit(void* context) { + DesktopViewPinInput* pin_input = context; + xTimerStop(pin_input->timer, portMAX_DELAY); +} + +DesktopViewPinInput* desktop_view_pin_input_alloc(void) { + DesktopViewPinInput* pin_input = furi_alloc(sizeof(DesktopViewPinInput)); + pin_input->view = view_alloc(); + view_allocate_model(pin_input->view, ViewModelTypeLocking, sizeof(DesktopViewPinInputModel)); + view_set_context(pin_input->view, pin_input); + view_set_draw_callback(pin_input->view, desktop_view_pin_input_draw); + view_set_input_callback(pin_input->view, desktop_view_pin_input_input); + pin_input->timer = xTimerCreate( + NULL, + pdMS_TO_TICKS(NO_ACTIVITY_TIMEOUT), + pdFALSE, + pin_input, + desktop_view_pin_input_timer_callback); + view_set_enter_callback(pin_input->view, desktop_view_pin_input_enter); + view_set_exit_callback(pin_input->view, desktop_view_pin_input_exit); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->pin_x = DEFAULT_PIN_X; + model->pin_y = DEFAULT_PIN_Y; + model->pin.length = 0; + view_commit_model(pin_input->view, false); + + return pin_input; +} + +void desktop_view_pin_input_free(DesktopViewPinInput* pin_input) { + furi_assert(pin_input); + + xTimerStop(pin_input->timer, portMAX_DELAY); + while(xTimerIsTimerActive(pin_input->timer)) { + delay(1); + } + xTimerDelete(pin_input->timer, portMAX_DELAY); + + view_free(pin_input->view); + free(pin_input); +} + +void desktop_view_pin_input_lock_input(DesktopViewPinInput* pin_input) { + furi_assert(pin_input); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->locked_input = true; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_unlock_input(DesktopViewPinInput* pin_input) { + furi_assert(pin_input); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->locked_input = false; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const PinCode* pin) { + furi_assert(pin_input); + furi_assert(pin); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->pin = *pin; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_reset_pin(DesktopViewPinInput* pin_input) { + furi_assert(pin_input); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->pin.length = 0; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_hide_pin(DesktopViewPinInput* pin_input, bool pin_hidden) { + furi_assert(pin_input); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->pin_hidden = pin_hidden; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_set_label_button(DesktopViewPinInput* pin_input, const char* label) { + furi_assert(pin_input); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->button_label = label; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_set_label_primary( + DesktopViewPinInput* pin_input, + uint8_t x, + uint8_t y, + const char* label) { + furi_assert(pin_input); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->primary_str = label; + model->primary_str_x = x; + model->primary_str_y = y; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_set_label_secondary( + DesktopViewPinInput* pin_input, + uint8_t x, + uint8_t y, + const char* label) { + furi_assert(pin_input); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->secondary_str = label; + model->secondary_str_x = x; + model->secondary_str_y = y; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_set_pin_position(DesktopViewPinInput* pin_input, uint8_t x, uint8_t y) { + furi_assert(pin_input); + + DesktopViewPinInputModel* model = view_get_model(pin_input->view); + model->pin_x = x; + model->pin_y = y; + view_commit_model(pin_input->view, true); +} + +void desktop_view_pin_input_set_context(DesktopViewPinInput* pin_input, void* context) { + furi_assert(pin_input); + pin_input->context = context; +} + +void desktop_view_pin_input_set_timeout_callback( + DesktopViewPinInput* pin_input, + DesktopViewPinInputCallback callback) { + furi_assert(pin_input); + pin_input->timeout_callback = callback; +} + +void desktop_view_pin_input_set_back_callback( + DesktopViewPinInput* pin_input, + DesktopViewPinInputCallback callback) { + furi_assert(pin_input); + pin_input->back_callback = callback; +} + +void desktop_view_pin_input_set_done_callback( + DesktopViewPinInput* pin_input, + DesktopViewPinInputDoneCallback callback) { + furi_assert(pin_input); + pin_input->done_callback = callback; +} + +View* desktop_view_pin_input_get_view(DesktopViewPinInput* pin_input) { + furi_assert(pin_input); + return pin_input->view; +} diff --git a/applications/desktop/views/desktop_view_pin_input.h b/applications/desktop/views/desktop_view_pin_input.h new file mode 100644 index 00000000000..3e39fd207e4 --- /dev/null +++ b/applications/desktop/views/desktop_view_pin_input.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include "desktop/desktop_settings/desktop_settings.h" + +typedef void (*DesktopViewPinInputCallback)(void*); +typedef void (*DesktopViewPinInputDoneCallback)(const PinCode* pin_code, void*); +typedef struct DesktopViewPinInput DesktopViewPinInput; + +DesktopViewPinInput* desktop_view_pin_input_alloc(void); +void desktop_view_pin_input_free(DesktopViewPinInput*); + +void desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const PinCode* pin); +void desktop_view_pin_input_reset_pin(DesktopViewPinInput* pin_input); +void desktop_view_pin_input_hide_pin(DesktopViewPinInput* pin_input, bool pin_hidden); +void desktop_view_pin_input_set_label_button(DesktopViewPinInput* pin_input, const char* label); +void desktop_view_pin_input_set_label_primary( + DesktopViewPinInput* pin_input, + uint8_t x, + uint8_t y, + const char* label); +void desktop_view_pin_input_set_label_secondary( + DesktopViewPinInput* pin_input, + uint8_t x, + uint8_t y, + const char* label); +void desktop_view_pin_input_set_pin_position(DesktopViewPinInput* pin_input, uint8_t x, uint8_t y); +View* desktop_view_pin_input_get_view(DesktopViewPinInput*); +void desktop_view_pin_input_set_done_callback( + DesktopViewPinInput* pin_input, + DesktopViewPinInputDoneCallback callback); +void desktop_view_pin_input_set_back_callback( + DesktopViewPinInput* pin_input, + DesktopViewPinInputCallback callback); +void desktop_view_pin_input_set_timeout_callback( + DesktopViewPinInput* pin_input, + DesktopViewPinInputCallback callback); +void desktop_view_pin_input_set_context(DesktopViewPinInput* pin_input, void* context); +void desktop_view_pin_input_lock_input(DesktopViewPinInput* pin_input); +void desktop_view_pin_input_unlock_input(DesktopViewPinInput* pin_input); diff --git a/applications/desktop/views/desktop_view_pin_setup_done.c b/applications/desktop/views/desktop_view_pin_setup_done.c new file mode 100644 index 00000000000..a90903e98f5 --- /dev/null +++ b/applications/desktop/views/desktop_view_pin_setup_done.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../desktop_i.h" +#include "desktop_view_pin_setup_done.h" + +struct DesktopViewPinSetupDone { + View* view; + DesktopViewPinSetupDoneDoneCallback callback; + void* context; +}; + +static void desktop_view_pin_done_draw(Canvas* canvas, void* model) { + furi_assert(canvas); + furi_assert(model); + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, 64, 0, AlignCenter, AlignTop, "Prepare to use\narrows as\nPIN symbols"); + + canvas_set_font(canvas, FontSecondary); + elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols"); + + canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29); + elements_button_right(canvas, "Next"); +} + +static bool desktop_view_pin_done_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopViewPinSetupDone* instance = context; + bool consumed = false; + + if((event->key == InputKeyRight) && (event->type == InputTypeShort)) { + instance->callback(instance->context); + consumed = true; + } + + return consumed; +} + +void desktop_view_pin_done_set_callback( + DesktopViewPinSetupDone* instance, + DesktopViewPinSetupDoneDoneCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +DesktopViewPinSetupDone* desktop_view_pin_done_alloc() { + DesktopViewPinSetupDone* view = furi_alloc(sizeof(DesktopViewPinSetupDone)); + view->view = view_alloc(); + view_allocate_model(view->view, ViewModelTypeLockFree, 1); + view_set_context(view->view, view); + view_set_draw_callback(view->view, desktop_view_pin_done_draw); + view_set_input_callback(view->view, desktop_view_pin_done_input); + + return view; +} + +void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/desktop/views/desktop_view_pin_setup_done.h b/applications/desktop/views/desktop_view_pin_setup_done.h new file mode 100644 index 00000000000..b55677dc587 --- /dev/null +++ b/applications/desktop/views/desktop_view_pin_setup_done.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +typedef struct DesktopViewPinSetupDone DesktopViewPinSetupDone; + +typedef void (*DesktopViewPinSetupDoneDoneCallback)(void*); + +void desktop_view_pin_done_set_callback( + DesktopViewPinSetupDone* instance, + DesktopViewPinSetupDoneDoneCallback callback, + void* context); +DesktopViewPinSetupDone* desktop_view_pin_done_alloc(); +void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance); +View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance); diff --git a/applications/desktop/views/desktop_view_pin_timeout.c b/applications/desktop/views/desktop_view_pin_timeout.c new file mode 100644 index 00000000000..7cc0860f386 --- /dev/null +++ b/applications/desktop/views/desktop_view_pin_timeout.c @@ -0,0 +1,109 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "desktop_view_pin_timeout.h" + +struct DesktopViewPinTimeout { + View* view; + TimerHandle_t timer; + DesktopViewPinTimeoutDoneCallback callback; + void* context; +}; + +typedef struct { + uint32_t time_left; +} DesktopViewPinTimeoutModel; + +void desktop_view_pin_timeout_set_callback( + DesktopViewPinTimeout* instance, + DesktopViewPinTimeoutDoneCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +static void desktop_view_pin_timeout_timer_callback(TimerHandle_t timer) { + DesktopViewPinTimeout* instance = pvTimerGetTimerID(timer); + bool stop = false; + + DesktopViewPinTimeoutModel* model = view_get_model(instance->view); + if(model->time_left > 0) { + --model->time_left; + } else { + stop = true; + } + view_commit_model(instance->view, true); + + if(stop) { + xTimerStop(instance->timer, portMAX_DELAY); + instance->callback(instance->context); + } +} + +static bool desktop_view_pin_timeout_input(InputEvent* event, void* context) { + return true; +} + +static void desktop_view_pin_timeout_draw(Canvas* canvas, void* _model) { + furi_assert(canvas); + furi_assert(_model); + + DesktopViewPinTimeoutModel* model = _model; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 36, 31, "Wrong PIN!"); + + canvas_set_font(canvas, FontSecondary); + char str[30] = {0}; + snprintf(str, sizeof(str), "Timeout: %lds", model->time_left); + canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, str); +} + +void desktop_view_pin_timeout_free(DesktopViewPinTimeout* instance) { + view_free(instance->view); + xTimerDelete(instance->timer, portMAX_DELAY); + + free(instance); +} + +DesktopViewPinTimeout* desktop_view_pin_timeout_alloc(void) { + DesktopViewPinTimeout* instance = furi_alloc(sizeof(DesktopViewPinTimeout)); + instance->timer = xTimerCreate( + NULL, pdMS_TO_TICKS(1000), pdTRUE, instance, desktop_view_pin_timeout_timer_callback); + + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(DesktopViewPinTimeoutModel)); + + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, desktop_view_pin_timeout_draw); + view_set_input_callback(instance->view, desktop_view_pin_timeout_input); + + return instance; +} + +void desktop_view_pin_timeout_start(DesktopViewPinTimeout* instance, uint32_t time_left) { + furi_assert(instance); + + DesktopViewPinTimeoutModel* model = view_get_model(instance->view); + // no race - always called when timer is stopped + model->time_left = time_left; + view_commit_model(instance->view, true); + + xTimerStart(instance->timer, portMAX_DELAY); +} + +View* desktop_view_pin_timeout_get_view(DesktopViewPinTimeout* instance) { + furi_assert(instance); + + return instance->view; +} diff --git a/applications/desktop/views/desktop_view_pin_timeout.h b/applications/desktop/views/desktop_view_pin_timeout.h new file mode 100644 index 00000000000..76d84ba5504 --- /dev/null +++ b/applications/desktop/views/desktop_view_pin_timeout.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +typedef void (*DesktopViewPinTimeoutDoneCallback)(void*); +typedef struct DesktopViewPinTimeout DesktopViewPinTimeout; + +void desktop_view_pin_timeout_set_callback( + DesktopViewPinTimeout* instance, + DesktopViewPinTimeoutDoneCallback callback, + void* context); +DesktopViewPinTimeout* desktop_view_pin_timeout_alloc(void); +void desktop_view_pin_timeout_free(DesktopViewPinTimeout*); +void desktop_view_pin_timeout_start(DesktopViewPinTimeout* instance, uint32_t time_left); +View* desktop_view_pin_timeout_get_view(DesktopViewPinTimeout* instance); diff --git a/applications/dolphin/dolphin.c b/applications/dolphin/dolphin.c index 2213ebb98b8..92c6d7a7087 100644 --- a/applications/dolphin/dolphin.c +++ b/applications/dolphin/dolphin.c @@ -80,15 +80,11 @@ Dolphin* dolphin_alloc() { dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL); dolphin->pubsub = furi_pubsub_alloc(); dolphin->butthurt_timer = xTimerCreate( - "Butthurt timer", HOURS_IN_TICKS(2 * 24), pdTRUE, dolphin, dolphin_butthurt_timer_callback); + NULL, HOURS_IN_TICKS(2 * 24), pdTRUE, dolphin, dolphin_butthurt_timer_callback); dolphin->flush_timer = - xTimerCreate("Flush timer", 30 * 1000, pdFALSE, dolphin, dolphin_flush_timer_callback); + xTimerCreate(NULL, 30 * 1000, pdFALSE, dolphin, dolphin_flush_timer_callback); dolphin->clear_limits_timer = xTimerCreate( - "Clear limits timer", - HOURS_IN_TICKS(24), - pdTRUE, - dolphin, - dolphin_clear_limits_timer_callback); + NULL, HOURS_IN_TICKS(24), pdTRUE, dolphin, dolphin_clear_limits_timer_callback); return dolphin; } diff --git a/applications/gui/elements.c b/applications/gui/elements.c index 0cd341b36e4..2b874ec1c24 100644 --- a/applications/gui/elements.c +++ b/applications/gui/elements.c @@ -1,4 +1,7 @@ #include "elements.h" +#include +#include "furi_hal_resources.h" +#include #include "gui/canvas.h" #include @@ -337,6 +340,47 @@ void elements_slightly_rounded_box( canvas_draw_rbox(canvas, x, y, width, height, 1); } +void elements_bold_rounded_frame( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height) { + furi_assert(canvas); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x + 2, y + 2, width - 3, height - 3); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_line(canvas, x + 3, y, x + width - 3, y); + canvas_draw_line(canvas, x + 2, y + 1, x + width - 2, y + 1); + + canvas_draw_line(canvas, x, y + 3, x, y + height - 3); + canvas_draw_line(canvas, x + 1, y + 2, x + 1, y + height - 2); + + canvas_draw_line(canvas, x + width, y + 3, x + width, y + height - 3); + canvas_draw_line(canvas, x + width - 1, y + 2, x + width - 1, y + height - 2); + + canvas_draw_line(canvas, x + 3, y + height, x + width - 3, y + height); + canvas_draw_line(canvas, x + 2, y + height - 1, x + width - 2, y + height - 1); + + canvas_draw_dot(canvas, x + 2, y + 2); + canvas_draw_dot(canvas, x + 3, y + 2); + canvas_draw_dot(canvas, x + 2, y + 3); + + canvas_draw_dot(canvas, x + width - 2, y + 2); + canvas_draw_dot(canvas, x + width - 3, y + 2); + canvas_draw_dot(canvas, x + width - 2, y + 3); + + canvas_draw_dot(canvas, x + 2, y + height - 2); + canvas_draw_dot(canvas, x + 3, y + height - 2); + canvas_draw_dot(canvas, x + 2, y + height - 3); + + canvas_draw_dot(canvas, x + width - 2, y + height - 2); + canvas_draw_dot(canvas, x + width - 3, y + height - 2); + canvas_draw_dot(canvas, x + width - 2, y + height - 3); +} + void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { furi_assert(canvas); canvas_draw_rframe(canvas, x + 4, y, width, height, 3); diff --git a/applications/gui/elements.h b/applications/gui/elements.h index 2d576e5b644..28010b36152 100644 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -150,6 +150,19 @@ void elements_slightly_rounded_box( uint8_t width, uint8_t height); +/** Draw bold rounded frame + * + * @param canvas Canvas instance + * @param x, y top left corner coordinates + * @param width, height size of frame + */ +void elements_bold_rounded_frame( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height); + /** Draw bubble frame for text * * @param canvas Canvas instance diff --git a/applications/gui/modules/code_input.c b/applications/gui/modules/code_input.c deleted file mode 100644 index 988742f9fc6..00000000000 --- a/applications/gui/modules/code_input.c +++ /dev/null @@ -1,478 +0,0 @@ -#include "code_input.h" -#include -#include - -#define MAX_CODE_LEN 10 - -struct CodeInput { - View* view; -}; - -typedef enum { - CodeInputStateVerify, - CodeInputStateUpdate, - CodeInputStateTotal, -} CodeInputStateEnum; - -typedef enum { - CodeInputFirst, - CodeInputSecond, - CodeInputTotal, -} CodeInputsEnum; - -typedef struct { - uint8_t state; - uint8_t current; - bool ext_update; - - uint8_t input_length[CodeInputTotal]; - uint8_t local_buffer[CodeInputTotal][MAX_CODE_LEN]; - - CodeInputOkCallback ok_callback; - CodeInputFailCallback fail_callback; - void* callback_context; - - const char* header; - - uint8_t* ext_buffer; - uint8_t* ext_buffer_length; -} CodeInputModel; - -static const Icon* keys_assets[] = { - [InputKeyUp] = &I_ButtonUp_7x4, - [InputKeyDown] = &I_ButtonDown_7x4, - [InputKeyRight] = &I_ButtonRight_4x7, - [InputKeyLeft] = &I_ButtonLeft_4x7, -}; - -/** - * @brief Compare buffers - * - * @param in Input buffer pointer - * @param len_in Input array length - * @param src Source buffer pointer - * @param len_src Source array length - */ - -bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src) { - bool result = false; - do { - result = (len_in && len_src); - if(!result) { - break; - } - result = (len_in == len_src); - if(!result) { - break; - } - for(size_t i = 0; i < len_in; i++) { - result = (in[i] == src[i]); - if(!result) { - break; - } - } - } while(false); - - return result; -} - -/** - * @brief Compare local buffers - * - * @param model - */ -static bool code_input_compare_local(CodeInputModel* model) { - uint8_t* source = model->local_buffer[CodeInputFirst]; - size_t source_length = model->input_length[CodeInputFirst]; - - uint8_t* input = model->local_buffer[CodeInputSecond]; - size_t input_length = model->input_length[CodeInputSecond]; - - return code_input_compare(input, input_length, source, source_length); -} - -/** - * @brief Compare ext with local - * - * @param model - */ -static bool code_input_compare_ext(CodeInputModel* model) { - uint8_t* input = model->local_buffer[CodeInputFirst]; - size_t input_length = model->input_length[CodeInputFirst]; - - uint8_t* source = model->ext_buffer; - size_t source_length = *model->ext_buffer_length; - - return code_input_compare(input, input_length, source, source_length); -} - -/** - * @brief Set ext buffer - * - * @param model - */ -static void code_input_set_ext(CodeInputModel* model) { - *model->ext_buffer_length = model->input_length[CodeInputFirst]; - for(size_t i = 0; i <= model->input_length[CodeInputFirst]; i++) { - model->ext_buffer[i] = model->local_buffer[CodeInputFirst][i]; - } -} - -/** - * @brief Draw input sequence - * - * @param canvas - * @param buffer - * @param length - * @param x - * @param y - * @param active - */ -static void code_input_draw_sequence( - Canvas* canvas, - uint8_t* buffer, - uint8_t length, - uint8_t x, - uint8_t y, - bool active) { - uint8_t pos_x = x + 6; - uint8_t pos_y = y + 3; - - if(active) canvas_draw_icon(canvas, x - 4, y + 5, &I_ButtonRightSmall_3x5); - - elements_slightly_rounded_frame(canvas, x, y, 116, 15); - - for(size_t i = 0; i < length; i++) { - // maybe symmetrical assets? :-/ - uint8_t offset_y = buffer[i] < 2 ? 2 + (buffer[i] * 2) : 1; - canvas_draw_icon(canvas, pos_x, pos_y + offset_y, keys_assets[buffer[i]]); - pos_x += buffer[i] > 1 ? 9 : 11; - } -} - -/** - * @brief Reset input count - * - * @param model - */ -static void code_input_reset_count(CodeInputModel* model) { - model->input_length[model->current] = 0; -} - -/** - * @brief Call input callback - * - * @param model - */ -static void code_input_call_ok_callback(CodeInputModel* model) { - if(model->ok_callback != NULL) { - model->ok_callback(model->callback_context); - } -} - -/** - * @brief Call changed callback - * - * @param model - */ -static void code_input_call_fail_callback(CodeInputModel* model) { - if(model->fail_callback != NULL) { - model->fail_callback(model->callback_context); - } -} - -/** - * @brief Handle Back button - * - * @param model - */ -static bool code_input_handle_back(CodeInputModel* model) { - if(model->current && !model->input_length[model->current]) { - --model->current; - return true; - } - - if(model->input_length[model->current]) { - code_input_reset_count(model); - return true; - } - - code_input_call_fail_callback(model); - return false; -} - -/** - * @brief Handle OK button - * - * @param model - */ -static void code_input_handle_ok(CodeInputModel* model) { - switch(model->state) { - case CodeInputStateVerify: - - if(code_input_compare_ext(model)) { - if(model->ext_update) { - model->state = CodeInputStateUpdate; - } else { - code_input_call_ok_callback(model); - } - } - code_input_reset_count(model); - break; - - case CodeInputStateUpdate: - - if(!model->current && model->input_length[model->current]) { - model->current++; - } else { - if(code_input_compare_local(model)) { - if(model->ext_update) { - code_input_set_ext(model); - } - code_input_call_ok_callback(model); - } else { - code_input_reset_count(model); - } - } - - break; - default: - break; - } -} - -/** - * @brief Handle input - * - * @param model - * @param key - */ - -size_t code_input_push(uint8_t* buffer, size_t length, InputKey key) { - buffer[length] = key; - length = CLAMP(length + 1, MAX_CODE_LEN, 0); - return length; -} - -/** - * @brief Handle D-pad keys - * - * @param model - * @param key - */ -static void code_input_handle_dpad(CodeInputModel* model, InputKey key) { - uint8_t at = model->current; - size_t new_length = code_input_push(model->local_buffer[at], model->input_length[at], key); - model->input_length[at] = new_length; -} - -/** - * @brief Draw callback - * - * @param canvas - * @param _model - */ -static void code_input_view_draw_callback(Canvas* canvas, void* _model) { - CodeInputModel* model = _model; - uint8_t y_offset = 0; - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(model->header && strlen(model->header)) { - canvas_draw_str(canvas, 2, 9, model->header); - } else { - y_offset = 4; - } - - canvas_set_font(canvas, FontSecondary); - - switch(model->state) { - case CodeInputStateVerify: - code_input_draw_sequence( - canvas, - model->local_buffer[CodeInputFirst], - model->input_length[CodeInputFirst], - 6, - 30 + y_offset, - true); - break; - case CodeInputStateUpdate: - code_input_draw_sequence( - canvas, - model->local_buffer[CodeInputFirst], - model->input_length[CodeInputFirst], - 6, - 14 + y_offset, - !model->current); - code_input_draw_sequence( - canvas, - model->local_buffer[CodeInputSecond], - model->input_length[CodeInputSecond], - 6, - 44 + y_offset, - model->current); - - if(model->current) canvas_draw_str(canvas, 2, 39 + y_offset, "Repeat code"); - - break; - default: - break; - } -} - -/** - * @brief Input callback - * - * @param event - * @param context - * @return true - * @return false - */ -static bool code_input_view_input_callback(InputEvent* event, void* context) { - CodeInput* code_input = context; - furi_assert(code_input); - bool consumed = false; - - if(event->type == InputTypeShort || event->type == InputTypeRepeat) { - switch(event->key) { - case InputKeyBack: - with_view_model( - code_input->view, (CodeInputModel * model) { - consumed = code_input_handle_back(model); - return true; - }); - break; - - case InputKeyOk: - with_view_model( - code_input->view, (CodeInputModel * model) { - code_input_handle_ok(model); - return true; - }); - consumed = true; - break; - default: - - with_view_model( - code_input->view, (CodeInputModel * model) { - code_input_handle_dpad(model, event->key); - return true; - }); - consumed = true; - break; - } - } - - return consumed; -} - -/** - * @brief Reset all input-related data in model - * - * @param model CodeInputModel - */ -static void code_input_reset_model_input_data(CodeInputModel* model) { - model->current = 0; - model->input_length[CodeInputFirst] = 0; - model->input_length[CodeInputSecond] = 0; - model->ext_buffer = NULL; - model->ext_update = false; - model->state = 0; -} - -/** - * @brief Allocate and initialize code input. This code input is used to enter codes. - * - * @return CodeInput instance pointer - */ -CodeInput* code_input_alloc() { - CodeInput* code_input = furi_alloc(sizeof(CodeInput)); - code_input->view = view_alloc(); - view_set_context(code_input->view, code_input); - view_allocate_model(code_input->view, ViewModelTypeLocking, sizeof(CodeInputModel)); - view_set_draw_callback(code_input->view, code_input_view_draw_callback); - view_set_input_callback(code_input->view, code_input_view_input_callback); - - with_view_model( - code_input->view, (CodeInputModel * model) { - model->header = ""; - model->ok_callback = NULL; - model->fail_callback = NULL; - model->callback_context = NULL; - code_input_reset_model_input_data(model); - return true; - }); - - return code_input; -} - -/** - * @brief Deinitialize and free code input - * - * @param code_input Code input instance - */ -void code_input_free(CodeInput* code_input) { - furi_assert(code_input); - view_free(code_input->view); - free(code_input); -} - -/** - * @brief Get code input view - * - * @param code_input code input instance - * @return View instance that can be used for embedding - */ -View* code_input_get_view(CodeInput* code_input) { - furi_assert(code_input); - return code_input->view; -} - -/** - * @brief Set code input callbacks - * - * @param code_input code input instance - * @param ok_callback input callback fn - * @param fail_callback code match callback fn - * @param callback_context callback context - * @param buffer buffer - * @param buffer_length ptr to buffer length uint - * @param ext_update true to update buffer - */ -void code_input_set_result_callback( - CodeInput* code_input, - CodeInputOkCallback ok_callback, - CodeInputFailCallback fail_callback, - void* callback_context, - uint8_t* buffer, - uint8_t* buffer_length, - bool ext_update) { - with_view_model( - code_input->view, (CodeInputModel * model) { - code_input_reset_model_input_data(model); - model->ok_callback = ok_callback; - model->fail_callback = fail_callback; - model->callback_context = callback_context; - - model->ext_buffer = buffer; - model->ext_buffer_length = buffer_length; - model->state = (*buffer_length == 0) ? 1 : 0; - model->ext_update = ext_update; - - return true; - }); -} - -/** - * @brief Set code input header text - * - * @param code_input code input instance - * @param text text to be shown - */ -void code_input_set_header_text(CodeInput* code_input, const char* text) { - with_view_model( - code_input->view, (CodeInputModel * model) { - model->header = text; - return true; - }); -} diff --git a/applications/gui/modules/code_input.h b/applications/gui/modules/code_input.h deleted file mode 100644 index d6a43fc1146..00000000000 --- a/applications/gui/modules/code_input.h +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @file code_input.h - * GUI: CodeInput keyboard view module API - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Code input anonymous structure */ -typedef struct CodeInput CodeInput; - -/** callback that is executed when entered code matches ext buffer */ -typedef void (*CodeInputOkCallback)(void* context); - -/** callback that is executed when entered code does not matches ext buffer */ -typedef void (*CodeInputFailCallback)(void* context); - -/** Allocate and initialize code input. This code input is used to enter codes. - * - * @return CodeInput instance pointer - */ -CodeInput* code_input_alloc(); - -/** Deinitialize and free code input - * - * @param code_input Code input instance - */ -void code_input_free(CodeInput* code_input); - -/** Get code input view - * - * @param code_input code input instance - * - * @return View instance that can be used for embedding - */ -View* code_input_get_view(CodeInput* code_input); - -/** Set code input result callback - * - * @param code_input code input instance - * @param ok_callback ok callback fn - * @param fail_callback fail callback fn - * @param callback_context callback context - * @param buffer buffer to use - * @param buffer_length buffer length - * @param update set true to update buffer - */ -void code_input_set_result_callback( - CodeInput* code_input, - CodeInputOkCallback ok_callback, - CodeInputFailCallback fail_callback, - void* callback_context, - uint8_t* buffer, - uint8_t* buffer_length, - bool update); - -/** Set code input header text - * - * @param code_input code input instance - * @param text text to be shown - */ -void code_input_set_header_text(CodeInput* code_input, const char* text); - -/** Compare two buffers - * - * @param in buffer to compare to source - * @param len_in length of input buffer - * @param src source buffer - * @param len_src length of insourceput buffer - * @return true if buffers match - */ - -bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src); - -/** Push input into the end of array - * - * @param buffer buffer - * @param length length of buffer - * @param key input key - * @return new length of input buffer - */ -size_t code_input_push(uint8_t* buffer, size_t length, InputKey key); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/loader/loader.c b/applications/loader/loader.c index 95beab452b7..b8f67a59f48 100644 --- a/applications/loader/loader.c +++ b/applications/loader/loader.c @@ -1,3 +1,4 @@ +#include "applications.h" #include #include "loader/loader.h" #include "loader_i.h" @@ -79,6 +80,12 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { } } + for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { + if(strcmp(name, FLIPPER_SETTINGS_APPS[i].name) == 0) { + application = &FLIPPER_SETTINGS_APPS[i]; + } + } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { if(strcmp(name, FLIPPER_DEBUG_APPS[i].name) == 0) { diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index 4a779743efb..f45b07199f5 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -205,9 +205,6 @@ const uint8_t *_I_DoorLocked_10x56[] = {_I_DoorLocked_10x56_0}; const uint8_t _I_DoorRight_70x55_0[] = {0x01,0x00,0x16,0x01,0x81,0xcc,0x01,0x0f,0x60,0x04,0x3f,0x00,0x10,0xf8,0x08,0x0c,0x02,0x05,0x01,0x84,0x02,0x06,0x26,0x0a,0x10,0x8a,0xcc,0xe0,0x1d,0x68,0xe0,0x18,0xab,0xd0,0x0b,0x18,0x10,0x46,0xe6,0x16,0x1e,0x18,0x10,0x46,0xe4,0x28,0x2c,0x98,0x14,0x68,0x00,0x21,0x1d,0x10,0x8c,0x40,0x02,0x0e,0x10,0xa1,0x08,0xc8,0x40,0x42,0x62,0x11,0x94,0x03,0xfd,0xff,0x00,0x0c,0xff,0x0c,0x08,0x28,0x60,0xe4,0xc0,0x85,0x00,0x83,0x00,0x87,0xf1,0x00,0x8c,0x02,0x0b,0x07,0x24,0x84,0xff,0x04,0xc7,0x80,0xa0,0xe4,0xa0,0x81,0x41,0x04,0x17,0x02,0x41,0x49,0x81,0x0e,0x10,0xb2,0xa0,0x82,0x0e,0x9f,0xfc,0x0a,0x62,0xf2,0xc0,0x03,0x92,0xf0,0x08,0x2d,0x78,0x20,0xff,0x02,0x01,0x08,0xae,0x60,0x64,0x38,0x0d,0xb0,0x8d,0x08,0x82,0x11,0x58,0xc4,0x13,0xc0,0x35,0x68,0x62,0x68,0x81,0x09,0x08,0x84,0x40,0x81,0x0d,0x18,0x69,0x10,0x47,0x44,0x66,0x5f,0x21,0xa9,0x29,0x94,0x10,0x2f,0x23,0x53,0x14,0x60,0x42,0x3c,0x08,0xfc,0x02,0x2c,0x62,0x23,0x58,0xd0,0x22,0x00,0x83,0x3e,0x98,0x44,0x43,0x46,0x22,0x30,0x89,0xce,0x01,0x0f,0x70,0x04,0x3f,0x81,0x8a,0x3c,0x21,0xaa,0x70,0x1a,0xe3,0x44,0x1a,0xa6,0x01,0xd2,0x38,0x90,0x8a,0x40,0x20,0xe5,0x96,0x80,0x43,0x81,0x06,0x6b,0x28,0x07,0xf3,0xfe,0x00,0x19,0xf9,0x34,0xc1,0x08,0x8f,0x20,0xf1,0x3e,0x16,0x00,0xa8,0x19,0x00,0x10,0x76,0x03,0xe2,0x3e,0x90,0x45,0x38,0x01,0x42,0x05,0x88,0x44,0x67,0x15,0x70,0x41,0x38,0x04,0x10,0x24,0x03,0x00,0x10,0x20,0x4a,0x46,0xe9,0x46,0xe1,0x04,0x50,0x66,0x40,0x85,0x19,0x98,0x00,0xc0,}; const uint8_t *_I_DoorRight_70x55[] = {_I_DoorRight_70x55_0}; -const uint8_t _I_LockPopup_100x49_0[] = {0x01,0x00,0x37,0x01,0xfc,0x7f,0xc0,0x13,0x01,0xfe,0x03,0x2a,0x07,0x06,0x12,0xd4,0x1a,0x06,0x0c,0xa8,0x60,0x33,0xe0,0x12,0x08,0x40,0x32,0x3f,0xd0,0x70,0x64,0xe0,0x20,0x31,0x8a,0x00,0x32,0x2c,0x10,0x0b,0x00,0x32,0x62,0x10,0x0c,0x06,0x00,0x19,0x00,0x82,0xc0,0x83,0x22,0x08,0x04,0x18,0x11,0x6a,0x01,0x25,0x02,0x84,0x83,0x1e,0x02,0x04,0x10,0xe1,0x03,0x1e,0x3c,0x0c,0x9c,0x1c,0x02,0x43,0x00,0x84,0x4f,0xc1,0x8f,0x80,0xaf,0x40,0x39,0x14,0x00,0x63,0xd0,0x36,0xf0,0x09,0xc6,0x00,0x18,0xd4,0x3a,0x06,0x9c,0x08,0x20,0xc9,0xdf,0xc0,0x20,0x7f,0x00,0x65,0x40,0x3f,0x80,0xc7,0xd0,0x10,0x06,0x01,0x7f,0x06,0x34,0x8e,0xa1,0x3d,0x80,0x70,0x0b,0x4f,0x23,0xd0,0x50,0xa0,0x1f,0x08,0x78,0x66,0x11,0xe3,0xfc,0x83,0x83,0x1e,0x40,0x0c,0x1f,0xfb,0xec,0x41,0x8c,0x03,0x1e,0x07,0x00,0x4d,0x10,0x0a,0x04,0xc0,0x9b,0x30,0x0c,0x1f,0xff,0xff,0x9f,0x06,0x3e,0x01,0x80,0x48,0xe7,0x99,0x83,0x0d,0x6a,0xe0,0xc4,0x90,0x03,0x1a,0x76,0x0c,0x38,0xe0,0x34,0x45,0x25,0x02,0x06,0x0d,0xe0,0x18,0x3c,0x08,0x19,0x40,0x78,0x00,0xc1,0x81,0xc3,0x27,0xf8,0x48,0x26,0x82,0x7d,0x00,0xfc,0x40,0xfc,0x10,0xfc,0x04,0xfc,0x18,0x30,0x28,0x7d,0x02,0x3f,0x00,0x98,0x41,0x38,0x31,0x08,0x25,0x0e,0x19,0x1f,0x81,0x42,0x70,0x11,0xa2,0x08,0xe2,0x30,0x72,0x08,0x76,0x0a,0x19,0x0f,0x85,0x42,0x60,0x11,0x51,0x78,0xc2,0x20,0x32,0x08,0x26,0x00,0x18,0x91,0x00,0x60,0x91,0x44,0x08,0x34,0x08,0x64,0x1f,0xe4,0x07,0x3f,0x84,0x0d,0x58,0x44,0x01,0x83,0xdc,0x60,0x43,0xe1,0x39,0xa9,0xd0,0x60,0x70,0x16,0x78,0xca,0x01,0x8f,0x83,0x3d,0x10,0x33,0x29,0x00,0xc7,0xa1,0x83,0x3f,0x10,0x0c,0x79,0x30,0x32,0xa0,0xdf,0xc7,0xa0,0x80,0x22,0x07,0xf8,0x06,0x54,0x04,}; -const uint8_t *_I_LockPopup_100x49[] = {_I_LockPopup_100x49_0}; - const uint8_t _I_PassportBottom_128x17_0[] = {0x01,0x00,0x5e,0x00,0x96,0x01,0x97,0xe1,0xff,0x00,0x2e,0x3e,0x68,0x0f,0x5a,0xc5,0x54,0x00,0xb9,0x50,0xfb,0x6a,0x35,0x40,0x05,0xcd,0x4e,0x03,0xfd,0x30,0x0f,0xf8,0x7f,0xa0,0x81,0xfe,0xf9,0x1b,0xfb,0xf3,0x01,0x47,0x66,0x02,0x1b,0x03,0x07,0xe7,0x02,0x0b,0x02,0x07,0xe5,0x82,0x0b,0xf2,0x1c,0xb0,0x01,0x67,0xf0,0x5f,0xd0,0x3f,0x23,0xf0,0x9b,0xc9,0xe5,0x80,0x03,0xd5,0xc0,0x00,0x86,0x01,0xf3,0xe6,0x1e,0x58,0x00,0x36,0xa8,0x06,0xac,0x04,0x30,0x6c,0x30,0xee,0x60,0x1f,0xe0,0x10,0xff,0x0d,0xfb,0x00,}; const uint8_t *_I_PassportBottom_128x17[] = {_I_PassportBottom_128x17_0}; @@ -447,6 +444,36 @@ const uint8_t *_I_Detailed_chip_17x13[] = {_I_Detailed_chip_17x13_0}; const uint8_t _I_Medium_chip_22x21_0[] = {0x01,0x00,0x35,0x00,0xfe,0x7f,0xe1,0xf0,0x28,0x04,0x43,0xf3,0xff,0x93,0xe1,0x6a,0x52,0x8e,0x2f,0xfe,0x51,0x25,0x80,0x4a,0x72,0xb6,0x79,0x55,0x76,0xc1,0x2e,0xaa,0xc0,0x25,0x51,0xdc,0x00,0x14,0x70,0x00,0x56,0xae,0x81,0x47,0x2b,0x7d,0x95,0x07,0x48,0x46,0x42,0x92,0x17,0x90,0xd4,0x87,0x64,}; const uint8_t *_I_Medium_chip_22x21[] = {_I_Medium_chip_22x21_0}; +const uint8_t _I_Pin_arrow_down_7x9_0[] = {0x00,0x1C,0x1C,0x1C,0x1C,0x1C,0x7F,0x3E,0x1C,0x08,}; +const uint8_t *_I_Pin_arrow_down_7x9[] = {_I_Pin_arrow_down_7x9_0}; + +const uint8_t _I_Pin_arrow_left_9x7_0[] = {0x00,0x08,0x00,0x0C,0x00,0xFE,0x01,0xFF,0x01,0xFE,0x01,0x0C,0x00,0x08,0x00,}; +const uint8_t *_I_Pin_arrow_left_9x7[] = {_I_Pin_arrow_left_9x7_0}; + +const uint8_t _I_Pin_arrow_right_9x7_0[] = {0x00,0x20,0x00,0x60,0x00,0xFF,0x00,0xFF,0x01,0xFF,0x00,0x60,0x00,0x20,0x00,}; +const uint8_t *_I_Pin_arrow_right_9x7[] = {_I_Pin_arrow_right_9x7_0}; + +const uint8_t _I_Pin_arrow_up7x9_0[] = {0x00,0x08,0x1C,0x3E,0x7F,0x1C,0x1C,0x1C,0x1C,0x1C,}; +const uint8_t *_I_Pin_arrow_up7x9[] = {_I_Pin_arrow_up7x9_0}; + +const uint8_t _I_Pin_attention_dpad_29x29_0[] = {0x01,0x00,0x56,0x00,0x80,0x7f,0x20,0xe0,0x31,0x81,0xc6,0x20,0x1c,0x08,0x05,0x82,0x01,0x20,0xa0,0x60,0x20,0x11,0x0f,0x04,0x02,0x03,0x08,0xf8,0x40,0x60,0x50,0x4f,0xc4,0x0e,0x09,0x04,0x05,0x8c,0x12,0x04,0x03,0x18,0x44,0x08,0x42,0x30,0x88,0x08,0x0c,0x62,0x14,0x18,0x05,0x02,0x21,0x61,0x14,0x8c,0x43,0xe3,0x01,0xf8,0x44,0x7f,0x20,0x31,0x89,0x81,0xcc,0x1e,0x61,0x73,0x0f,0x98,0x9c,0xc5,0xe6,0x37,0x31,0xf9,0x91,0xcc,0x9e,0x65,0x73,0x2f,0x99,0x9c,0xcd,0xe6,}; +const uint8_t *_I_Pin_attention_dpad_29x29[] = {_I_Pin_attention_dpad_29x29_0}; + +const uint8_t _I_Pin_back_arrow_10x8_0[] = {0x00,0x04,0x00,0x06,0x00,0xFF,0x00,0x06,0x01,0x04,0x02,0x00,0x02,0x00,0x01,0xF8,0x00,}; +const uint8_t *_I_Pin_back_arrow_10x8[] = {_I_Pin_back_arrow_10x8_0}; + +const uint8_t _I_Pin_back_full_40x8_0[] = {0x01,0x00,0x26,0x00,0x82,0x01,0x0e,0x0c,0x02,0x18,0x14,0x03,0xfe,0x04,0x38,0x37,0xc6,0xc3,0x32,0xf7,0x41,0x20,0x59,0x0a,0x54,0xa6,0x01,0xf2,0x88,0xde,0x80,0x83,0x01,0xc8,0x42,0xa5,0x3f,0x88,0x05,0x82,0x65,0x2e,}; +const uint8_t *_I_Pin_back_full_40x8[] = {_I_Pin_back_full_40x8_0}; + +const uint8_t _I_Pin_cell_13x13_0[] = {0x01,0x00,0x0a,0x00,0xff,0xc7,0xe0,0x31,0x00,0x0f,0x80,0x4c,0x2e,0x20,}; +const uint8_t *_I_Pin_cell_13x13[] = {_I_Pin_cell_13x13_0}; + +const uint8_t _I_Pin_pointer_5x3_0[] = {0x00,0x04,0x0E,0x1F,}; +const uint8_t *_I_Pin_pointer_5x3[] = {_I_Pin_pointer_5x3_0}; + +const uint8_t _I_Pin_star_7x7_0[] = {0x00,0x49,0x2A,0x1C,0x7F,0x1C,0x2A,0x49,}; +const uint8_t *_I_Pin_star_7x7[] = {_I_Pin_star_7x7_0}; + const uint8_t _I_passport_bad1_46x49_0[] = {0x01,0x00,0xd2,0x00,0xff,0x7f,0xc0,0x05,0x1f,0x02,0x1f,0xfe,0x7e,0x02,0x18,0x0f,0xe0,0x0a,0x57,0xff,0xf7,0x9c,0x0a,0x59,0xf8,0x0e,0x60,0x0a,0x56,0xf8,0x05,0x83,0xfc,0x05,0x18,0xbc,0x03,0x01,0xfd,0x02,0x8c,0x2c,0x5a,0x3f,0xa0,0x28,0xc1,0x40,0xa3,0xf4,0x02,0x8c,0x08,0x0a,0x77,0xf8,0x08,0x14,0x7d,0x13,0xfd,0xf9,0x14,0x80,0xab,0xd0,0x9f,0xd7,0xe0,0x10,0x60,0x2a,0x42,0x20,0x1a,0x09,0xfc,0xbe,0x01,0x10,0x02,0xa5,0x9c,0x0a,0x78,0x0e,0x74,0x04,0x0a,0x31,0x7a,0x06,0x7a,0x06,0x05,0x39,0xb0,0x44,0x80,0xa3,0x7e,0x02,0xa5,0xf0,0x0a,0x78,0x0a,0x00,0x14,0xf8,0x13,0xf0,0x29,0xc8,0x07,0x66,0x70,0x11,0xd8,0xea,0xa7,0xf1,0xb2,0x99,0x4c,0x00,0xa9,0xc0,0x9f,0x01,0x4e,0x01,0x3d,0x02,0x8c,0x38,0x0a,0x33,0xa8,0x6c,0x02,0x62,0x05,0x19,0xa0,0x14,0x78,0x00,0x51,0x94,0x01,0x46,0x01,0x03,0x02,0xa4,0x30,0x0a,0x2a,0x02,0x98,0x7c,0x25,0x60,0x52,0xe0,0x43,0xe5,0x80,0x51,0xc0,0x27,0x46,0x51,0x09,0x05,0x88,0xc0,0x66,0x80,0x52,0xfe,0x40,0x27,0x60,0x52,0xf8,0x7f,0xe7,0xa0,0x52,0xe0,0x5f,0xe7,0xc0,0x52,0x80,0x6f,0xe7,0xe0,0x53,0xde,0x01,0x50,0xe2,0x20,0x5f,0x02,0xbf,0xfb,0xfe,0x00,0x28,0xf8,}; const uint8_t *_I_passport_bad1_46x49[] = {_I_passport_bad1_46x49_0}; @@ -685,7 +712,6 @@ const Icon I_Back3_45x8 = {.width=45,.height=8,.frame_count=1,.frame_rate=0,.fra const Icon I_DoorLeft_70x55 = {.width=70,.height=55,.frame_count=1,.frame_rate=0,.frames=_I_DoorLeft_70x55}; const Icon I_DoorLocked_10x56 = {.width=10,.height=56,.frame_count=1,.frame_rate=0,.frames=_I_DoorLocked_10x56}; const Icon I_DoorRight_70x55 = {.width=70,.height=55,.frame_count=1,.frame_rate=0,.frames=_I_DoorRight_70x55}; -const Icon I_LockPopup_100x49 = {.width=100,.height=49,.frame_count=1,.frame_rate=0,.frames=_I_LockPopup_100x49}; const Icon I_PassportBottom_128x17 = {.width=128,.height=17,.frame_count=1,.frame_rate=0,.frames=_I_PassportBottom_128x17}; const Icon I_PassportLeft_6x47 = {.width=6,.height=47,.frame_count=1,.frame_rate=0,.frames=_I_PassportLeft_6x47}; const Icon I_WarningDolphin_45x42 = {.width=45,.height=42,.frame_count=1,.frame_rate=0,.frames=_I_WarningDolphin_45x42}; @@ -733,6 +759,16 @@ const Icon A_U2F_14 = {.width=14,.height=14,.frame_count=4,.frame_rate=3,.frames const Icon A_iButton_14 = {.width=14,.height=14,.frame_count=7,.frame_rate=3,.frames=_A_iButton_14}; const Icon I_Detailed_chip_17x13 = {.width=17,.height=13,.frame_count=1,.frame_rate=0,.frames=_I_Detailed_chip_17x13}; const Icon I_Medium_chip_22x21 = {.width=22,.height=21,.frame_count=1,.frame_rate=0,.frames=_I_Medium_chip_22x21}; +const Icon I_Pin_arrow_down_7x9 = {.width=7,.height=9,.frame_count=1,.frame_rate=0,.frames=_I_Pin_arrow_down_7x9}; +const Icon I_Pin_arrow_left_9x7 = {.width=9,.height=7,.frame_count=1,.frame_rate=0,.frames=_I_Pin_arrow_left_9x7}; +const Icon I_Pin_arrow_right_9x7 = {.width=9,.height=7,.frame_count=1,.frame_rate=0,.frames=_I_Pin_arrow_right_9x7}; +const Icon I_Pin_arrow_up7x9 = {.width=7,.height=9,.frame_count=1,.frame_rate=0,.frames=_I_Pin_arrow_up7x9}; +const Icon I_Pin_attention_dpad_29x29 = {.width=29,.height=29,.frame_count=1,.frame_rate=0,.frames=_I_Pin_attention_dpad_29x29}; +const Icon I_Pin_back_arrow_10x8 = {.width=10,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Pin_back_arrow_10x8}; +const Icon I_Pin_back_full_40x8 = {.width=40,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Pin_back_full_40x8}; +const Icon I_Pin_cell_13x13 = {.width=13,.height=13,.frame_count=1,.frame_rate=0,.frames=_I_Pin_cell_13x13}; +const Icon I_Pin_pointer_5x3 = {.width=5,.height=3,.frame_count=1,.frame_rate=0,.frames=_I_Pin_pointer_5x3}; +const Icon I_Pin_star_7x7 = {.width=7,.height=7,.frame_count=1,.frame_rate=0,.frames=_I_Pin_star_7x7}; const Icon I_passport_bad1_46x49 = {.width=46,.height=49,.frame_count=1,.frame_rate=0,.frames=_I_passport_bad1_46x49}; const Icon I_passport_bad2_46x49 = {.width=46,.height=49,.frame_count=1,.frame_rate=0,.frames=_I_passport_bad2_46x49}; const Icon I_passport_bad3_46x49 = {.width=46,.height=49,.frame_count=1,.frame_rate=0,.frames=_I_passport_bad3_46x49}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index 8f53bc36d23..26e26beffab 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -62,7 +62,6 @@ extern const Icon I_Back3_45x8; extern const Icon I_DoorLeft_70x55; extern const Icon I_DoorLocked_10x56; extern const Icon I_DoorRight_70x55; -extern const Icon I_LockPopup_100x49; extern const Icon I_PassportBottom_128x17; extern const Icon I_PassportLeft_6x47; extern const Icon I_WarningDolphin_45x42; @@ -110,6 +109,16 @@ extern const Icon A_U2F_14; extern const Icon A_iButton_14; extern const Icon I_Detailed_chip_17x13; extern const Icon I_Medium_chip_22x21; +extern const Icon I_Pin_arrow_down_7x9; +extern const Icon I_Pin_arrow_left_9x7; +extern const Icon I_Pin_arrow_right_9x7; +extern const Icon I_Pin_arrow_up7x9; +extern const Icon I_Pin_attention_dpad_29x29; +extern const Icon I_Pin_back_arrow_10x8; +extern const Icon I_Pin_back_full_40x8; +extern const Icon I_Pin_cell_13x13; +extern const Icon I_Pin_pointer_5x3; +extern const Icon I_Pin_star_7x7; extern const Icon I_passport_bad1_46x49; extern const Icon I_passport_bad2_46x49; extern const Icon I_passport_bad3_46x49; diff --git a/assets/icons/Interface/LockPopup_100x49.png b/assets/icons/Interface/LockPopup_100x49.png deleted file mode 100644 index f30d88a9e3c6c60af669592f53fafd0fbb1c5118..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 577 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 QX&eA7&1_6djNPvM5BL~PlmGw# literal 0 HcmV?d00001 diff --git a/assets/icons/PIN/Pin_arrow_left_9x7.png b/assets/icons/PIN/Pin_arrow_left_9x7.png new file mode 100644 index 0000000000000000000000000000000000000000..fb4ded78fde8d1bf4f053ba0b353e7acf14f031a GIT binary patch literal 3603 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 literal 0 HcmV?d00001 diff --git a/assets/icons/PIN/Pin_arrow_up7x9.png b/assets/icons/PIN/Pin_arrow_up7x9.png new file mode 100644 index 0000000000000000000000000000000000000000..a91a6fd5e99a72112e28865cd8a004c7d1933fff GIT binary patch literal 3603 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 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 diff --git a/assets/icons/PIN/Pin_back_full_40x8.png b/assets/icons/PIN/Pin_back_full_40x8.png new file mode 100644 index 0000000000000000000000000000000000000000..cd1301512db1c06700fec26d03231c63b9be75d3 GIT binary patch literal 3641 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 Cv0zgG literal 0 HcmV?d00001 diff --git a/assets/icons/PIN/Pin_cell_13x13.png b/assets/icons/PIN/Pin_cell_13x13.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1ff0c2fbe6a1f30e974340f4b54760fe504677 GIT binary patch literal 3593 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 LwKgd=cDwXHDN#*w literal 0 HcmV?d00001 diff --git a/firmware/targets/f6/furi_hal/furi_hal_power.c b/firmware/targets/f6/furi_hal/furi_hal_power.c index a211b8c587c..e34ba46ede3 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_power.c +++ b/firmware/targets/f6/furi_hal/furi_hal_power.c @@ -89,17 +89,17 @@ uint16_t furi_hal_power_insomnia_level() { } void furi_hal_power_insomnia_enter() { - vTaskSuspendAll(); + FURI_CRITICAL_ENTER(); furi_assert(furi_hal_power.insomnia < UINT8_MAX); furi_hal_power.insomnia++; - xTaskResumeAll(); + FURI_CRITICAL_EXIT(); } void furi_hal_power_insomnia_exit() { - vTaskSuspendAll(); + FURI_CRITICAL_ENTER(); furi_assert(furi_hal_power.insomnia > 0); furi_hal_power.insomnia--; - xTaskResumeAll(); + FURI_CRITICAL_EXIT(); } bool furi_hal_power_sleep_available() { diff --git a/firmware/targets/f6/furi_hal/furi_hal_rtc.c b/firmware/targets/f6/furi_hal/furi_hal_rtc.c index 4d45c748979..1f262692b7d 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f6/furi_hal/furi_hal_rtc.c @@ -163,3 +163,11 @@ void furi_hal_rtc_set_fault_data(uint32_t value) { uint32_t furi_hal_rtc_get_fault_data() { return furi_hal_rtc_get_register(FuriHalRtcRegisterFaultData); } + +void furi_hal_rtc_set_pin_fails(uint32_t value) { + furi_hal_rtc_set_register(FuriHalRtcRegisterPinFails, value); +} + +uint32_t furi_hal_rtc_get_pin_fails() { + return furi_hal_rtc_get_register(FuriHalRtcRegisterPinFails); +} diff --git a/firmware/targets/f6/furi_hal/furi_hal_usb.c b/firmware/targets/f6/furi_hal/furi_hal_usb.c index 881081b6a6e..7ca8ffdfa93 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f6/furi_hal/furi_hal_usb.c @@ -1,6 +1,7 @@ #include "furi_hal_version.h" #include "furi_hal_usb_i.h" #include "furi_hal_usb.h" +#include #include #include "usb.h" @@ -189,6 +190,8 @@ static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { if((usb_if_cur != NULL) && (usb_config.connected == true)) { usb_config.connected = false; usb_if_cur->suspend(&udev); + + furi_hal_power_insomnia_exit(); } if(callback != NULL) { callback(FuriHalUsbStateEventSuspend, cb_ctx); @@ -199,6 +202,8 @@ static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { if((usb_if_cur != NULL) && (usb_config.connected == false)) { usb_config.connected = true; usb_if_cur->wakeup(&udev); + + furi_hal_power_insomnia_enter(); } if(callback != NULL) { callback(FuriHalUsbStateEventWakeup, cb_ctx); diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index a211b8c587c..e34ba46ede3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -89,17 +89,17 @@ uint16_t furi_hal_power_insomnia_level() { } void furi_hal_power_insomnia_enter() { - vTaskSuspendAll(); + FURI_CRITICAL_ENTER(); furi_assert(furi_hal_power.insomnia < UINT8_MAX); furi_hal_power.insomnia++; - xTaskResumeAll(); + FURI_CRITICAL_EXIT(); } void furi_hal_power_insomnia_exit() { - vTaskSuspendAll(); + FURI_CRITICAL_ENTER(); furi_assert(furi_hal_power.insomnia > 0); furi_hal_power.insomnia--; - xTaskResumeAll(); + FURI_CRITICAL_EXIT(); } bool furi_hal_power_sleep_available() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 4d45c748979..1f262692b7d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -163,3 +163,11 @@ void furi_hal_rtc_set_fault_data(uint32_t value) { uint32_t furi_hal_rtc_get_fault_data() { return furi_hal_rtc_get_register(FuriHalRtcRegisterFaultData); } + +void furi_hal_rtc_set_pin_fails(uint32_t value) { + furi_hal_rtc_set_register(FuriHalRtcRegisterPinFails, value); +} + +uint32_t furi_hal_rtc_get_pin_fails() { + return furi_hal_rtc_get_register(FuriHalRtcRegisterPinFails); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index 881081b6a6e..7ca8ffdfa93 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -1,6 +1,7 @@ #include "furi_hal_version.h" #include "furi_hal_usb_i.h" #include "furi_hal_usb.h" +#include #include #include "usb.h" @@ -189,6 +190,8 @@ static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { if((usb_if_cur != NULL) && (usb_config.connected == true)) { usb_config.connected = false; usb_if_cur->suspend(&udev); + + furi_hal_power_insomnia_exit(); } if(callback != NULL) { callback(FuriHalUsbStateEventSuspend, cb_ctx); @@ -199,6 +202,8 @@ static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { if((usb_if_cur != NULL) && (usb_config.connected == false)) { usb_config.connected = true; usb_if_cur->wakeup(&udev); + + furi_hal_power_insomnia_enter(); } if(callback != NULL) { callback(FuriHalUsbStateEventWakeup, cb_ctx); diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index 8e79c95a106..ee8eb1bb349 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -38,6 +38,7 @@ typedef enum { FuriHalRtcRegisterSystemVersion, FuriHalRtcRegisterLfsFingerprint, FuriHalRtcRegisterFaultData, + FuriHalRtcRegisterPinFails, } FuriHalRtcRegister; /** Initialize RTC subsystem */ @@ -67,6 +68,10 @@ void furi_hal_rtc_set_fault_data(uint32_t value); uint32_t furi_hal_rtc_get_fault_data(); +void furi_hal_rtc_set_pin_fails(uint32_t value); + +uint32_t furi_hal_rtc_get_pin_fails(); + #ifdef __cplusplus } #endif From 2c616983cf05f6739092f098ddb78e0cde09c40c Mon Sep 17 00:00:00 2001 From: Albert Kharisov Date: Thu, 10 Feb 2022 23:06:45 +0400 Subject: [PATCH 9/9] Add animations: box, cry, read, hack (#990) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add animations: box, cry, read, hack * Desktop: unload animation if application is already started Co-authored-by: あく --- .../desktop/scenes/desktop_scene_main.c | 6 +++ .../external/L1_Boxing_128x64/frame_0.png | Bin 0 -> 1548 bytes .../external/L1_Boxing_128x64/frame_1.png | Bin 0 -> 1550 bytes .../external/L1_Boxing_128x64/frame_2.png | Bin 0 -> 1609 bytes .../external/L1_Boxing_128x64/frame_3.png | Bin 0 -> 1518 bytes .../external/L1_Boxing_128x64/frame_4.png | Bin 0 -> 1537 bytes .../external/L1_Boxing_128x64/frame_5.png | Bin 0 -> 1555 bytes .../external/L1_Boxing_128x64/frame_6.png | Bin 0 -> 1543 bytes .../external/L1_Boxing_128x64/meta.txt | 32 ++++++++++++++ .../external/L1_Cry_128x64/frame_0.png | Bin 0 -> 1942 bytes .../external/L1_Cry_128x64/frame_1.png | Bin 0 -> 1952 bytes .../external/L1_Cry_128x64/frame_2.png | Bin 0 -> 1945 bytes .../external/L1_Cry_128x64/frame_3.png | Bin 0 -> 1941 bytes .../external/L1_Cry_128x64/frame_4.png | Bin 0 -> 1938 bytes .../external/L1_Cry_128x64/frame_5.png | Bin 0 -> 1928 bytes .../external/L1_Cry_128x64/frame_6.png | Bin 0 -> 1946 bytes .../external/L1_Cry_128x64/frame_7.png | Bin 0 -> 1950 bytes .../dolphin/external/L1_Cry_128x64/meta.txt | 41 ++++++++++++++++++ .../external/L1_Read_books_128x64/frame_0.png | Bin 0 -> 1762 bytes .../external/L1_Read_books_128x64/frame_1.png | Bin 0 -> 1762 bytes .../external/L1_Read_books_128x64/frame_2.png | Bin 0 -> 1756 bytes .../external/L1_Read_books_128x64/frame_3.png | Bin 0 -> 1759 bytes .../external/L1_Read_books_128x64/frame_4.png | Bin 0 -> 1771 bytes .../external/L1_Read_books_128x64/frame_5.png | Bin 0 -> 1754 bytes .../external/L1_Read_books_128x64/frame_6.png | Bin 0 -> 1750 bytes .../external/L1_Read_books_128x64/frame_7.png | Bin 0 -> 1747 bytes .../external/L1_Read_books_128x64/frame_8.png | Bin 0 -> 1750 bytes .../external/L1_Read_books_128x64/meta.txt | 23 ++++++++++ .../external/L2_Hacking_pc_128x64/frame_0.png | Bin 0 -> 1604 bytes .../external/L2_Hacking_pc_128x64/frame_1.png | Bin 0 -> 1601 bytes .../external/L2_Hacking_pc_128x64/frame_2.png | Bin 0 -> 1606 bytes .../external/L2_Hacking_pc_128x64/frame_3.png | Bin 0 -> 1654 bytes .../external/L2_Hacking_pc_128x64/frame_4.png | Bin 0 -> 1658 bytes .../external/L2_Hacking_pc_128x64/meta.txt | 32 ++++++++++++++ assets/dolphin/external/manifest.txt | 35 ++++++++++++--- .../dolphin/L1_Boxing_128x64/frame_0.bm | Bin 0 -> 481 bytes .../dolphin/L1_Boxing_128x64/frame_1.bm | Bin 0 -> 461 bytes .../dolphin/L1_Boxing_128x64/frame_2.bm | Bin 0 -> 531 bytes .../dolphin/L1_Boxing_128x64/frame_3.bm | Bin 0 -> 437 bytes .../dolphin/L1_Boxing_128x64/frame_4.bm | Bin 0 -> 459 bytes .../dolphin/L1_Boxing_128x64/frame_5.bm | Bin 0 -> 450 bytes .../dolphin/L1_Boxing_128x64/frame_6.bm | Bin 0 -> 442 bytes .../dolphin/L1_Boxing_128x64/meta.txt | 32 ++++++++++++++ .../dolphin/L1_Cry_128x64/frame_0.bm | Bin 0 -> 889 bytes .../dolphin/L1_Cry_128x64/frame_1.bm | Bin 0 -> 911 bytes .../dolphin/L1_Cry_128x64/frame_2.bm | Bin 0 -> 910 bytes .../dolphin/L1_Cry_128x64/frame_3.bm | Bin 0 -> 923 bytes .../dolphin/L1_Cry_128x64/frame_4.bm | Bin 0 -> 894 bytes .../dolphin/L1_Cry_128x64/frame_5.bm | Bin 0 -> 940 bytes .../dolphin/L1_Cry_128x64/frame_6.bm | Bin 0 -> 915 bytes .../dolphin/L1_Cry_128x64/frame_7.bm | Bin 0 -> 934 bytes .../resources/dolphin/L1_Cry_128x64/meta.txt | 41 ++++++++++++++++++ .../dolphin/L1_Read_books_128x64/frame_0.bm | Bin 0 -> 653 bytes .../dolphin/L1_Read_books_128x64/frame_1.bm | Bin 0 -> 653 bytes .../dolphin/L1_Read_books_128x64/frame_2.bm | Bin 0 -> 650 bytes .../dolphin/L1_Read_books_128x64/frame_3.bm | Bin 0 -> 646 bytes .../dolphin/L1_Read_books_128x64/frame_4.bm | Bin 0 -> 650 bytes .../dolphin/L1_Read_books_128x64/frame_5.bm | Bin 0 -> 652 bytes .../dolphin/L1_Read_books_128x64/frame_6.bm | Bin 0 -> 646 bytes .../dolphin/L1_Read_books_128x64/frame_7.bm | Bin 0 -> 647 bytes .../dolphin/L1_Read_books_128x64/frame_8.bm | Bin 0 -> 643 bytes .../dolphin/L1_Read_books_128x64/meta.txt | 23 ++++++++++ .../dolphin/L2_Hacking_pc_128x64/frame_0.bm | Bin 0 -> 543 bytes .../dolphin/L2_Hacking_pc_128x64/frame_1.bm | Bin 0 -> 545 bytes .../dolphin/L2_Hacking_pc_128x64/frame_2.bm | Bin 0 -> 548 bytes .../dolphin/L2_Hacking_pc_128x64/frame_3.bm | Bin 0 -> 608 bytes .../dolphin/L2_Hacking_pc_128x64/frame_4.bm | Bin 0 -> 609 bytes .../dolphin/L2_Hacking_pc_128x64/meta.txt | 32 ++++++++++++++ assets/resources/dolphin/manifest.txt | 28 ++++++++++++ 69 files changed, 318 insertions(+), 7 deletions(-) create mode 100644 assets/dolphin/external/L1_Boxing_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Boxing_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Boxing_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Boxing_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Boxing_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Boxing_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Boxing_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Boxing_128x64/meta.txt create mode 100644 assets/dolphin/external/L1_Cry_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Cry_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Cry_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Cry_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Cry_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Cry_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Cry_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Cry_128x64/frame_7.png create mode 100644 assets/dolphin/external/L1_Cry_128x64/meta.txt create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_7.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/frame_8.png create mode 100644 assets/dolphin/external/L1_Read_books_128x64/meta.txt create mode 100644 assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png create mode 100644 assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png create mode 100644 assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png create mode 100644 assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png create mode 100644 assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png create mode 100644 assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt create mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm create mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm create mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm create mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm create mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm create mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm create mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm create mode 100644 assets/resources/dolphin/L1_Boxing_128x64/meta.txt create mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_0.bm create mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_1.bm create mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_2.bm create mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_3.bm create mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_4.bm create mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_5.bm create mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_6.bm create mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_7.bm create mode 100644 assets/resources/dolphin/L1_Cry_128x64/meta.txt create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm create mode 100644 assets/resources/dolphin/L1_Read_books_128x64/meta.txt create mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm create mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm create mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm create mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm create mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm create mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt diff --git a/applications/desktop/scenes/desktop_scene_main.c b/applications/desktop/scenes/desktop_scene_main.c index 8131d0c324b..f37ac6c65f4 100644 --- a/applications/desktop/scenes/desktop_scene_main.c +++ b/applications/desktop/scenes/desktop_scene_main.c @@ -87,6 +87,12 @@ void desktop_scene_main_on_enter(void* context) { Loader* loader = furi_record_open("loader"); desktop->app_start_stop_subscription = furi_pubsub_subscribe( loader_get_pubsub(loader), desktop_scene_main_app_started_callback, desktop); + + // Special case: application is already running (autostart application) + if(loader_is_locked(loader)) { + animation_manager_unload_and_stall_animation(desktop->animation_manager); + } + furi_record_close("loader"); desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_0.png b/assets/dolphin/external/L1_Boxing_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b2c75b8fbe7ac7ad06e4c1f3df878dabc56887 GIT binary patch literal 1548 zcmaJ>dvMcq98bGZ%5HTV$lG%va1(_z&$dZyq1)O)Wu3iJV94;ACh1zYCMC_bZpT4+ zIOTERsVB!!K@m<-xgiP+1S&&BZYOXE@;DtnP~@c?r}8@IuiJ|J!I4Yy`~AN8e!kz& z_myS7swt+tQF#yqnLJZnez2y%<22@g_XXo>FIYxu?rLqi64sh{Rf0-IB_zR~7+)*- zC0=Zv_qAk)pq$odpjxZ;R&s(8GxHf8b21hOXb7@TPR4nmPSW6zR2!8Y$RB(5BXCr7 zAhRr9+#4^KBGIWWsx-Z&Dj>Af39N`ro&?*I954`*G#*aI>g5ENbRfNUIk3-&F$C^a z(dryXAE|1u4=z_!3AULDRKRf@wy|bOhXdgwKajk`g4I3K1m1lSn^ayf1xy zoeCr%jChs}$FN}r7kCE6d5}@YLQ80fqHL(m77Ec;!9ws9i(r5vmg$`PIw>mx?U~u$ z8x3%{H(ZhoCQ1dPQ@r!tYhdgQzRG|btUWzFo6fBN9)k3F9#?4~dG^X|&)`lt|Fxc; z-y2It585G)AH%+0r5+}Sl^t%(yJ4t5n|@kX>H2dfbffUD?(?DPPUK*d-FSZVV>g)8 zkOdvfYoYV=+a7^VHk+Z-+oAZrilSigKRVcWq*4;IC0gS%DuZ|n+SAD&7m*9(x>-)<||lH5^TLRT<5$y;k#S& zHy*6Mn9F<{XCsCJmN_xE^<&@LYp!zG4$JKkr+*#rPSIQMr7h4_-8hR=cXz|0>LneH zIDGMxbC*79kdLP$Qi?K*(WY_ zGRHbnL)xX!xXkZmTO+}Z>9vzx-9MzENbSh3_4T>= zC!y=~mVJ#TvF<=_YTcd1_SCuC`dI7x%_GhXe>v7}*uNbsgbuxs{y?rIkIgEe=NA{| z)2DKN8r$ZcTh`_5Zr^MM{ukl~Vpu1_rp&2`eK6m29 RlWQ~oXOFwewY4m`_&*QaAY=dl literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_1.png b/assets/dolphin/external/L1_Boxing_128x64/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..347d71cc2087a490525faca17adf953c89a888a6 GIT binary patch literal 1550 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_3.png b/assets/dolphin/external/L1_Boxing_128x64/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..7004e10c30dad43342b1818440c9917f5b2eefbb GIT binary patch literal 1518 zcmaJ=eQXnD7{9X6HDeP7TVxZK^Dst)>wUJpmUU~l7FlRv+1hMcHM`!uUC*t(a(AQM z039D07-DG52r~jA0R&CuV=5>LH9{gEl5PJ06J&~n2umF55KLsg-B#oe!sYJX_kG^y z_dLJn>+RZQFBRmK<{=1DP_x8U2iH#cISe`Qf5WiF3zrhj-Kf>eAuY};0C9?P5TG>? zz8Ta3UTj}+29zU6PM73s)Ed1Wn;=Jwd`8EZh{PZoLCPx=FRgK_-NBO`3u{`;MiB9M+!$w!veHaYR;!gDX@aJ4sDZ2PQH@XFQFUI{f(xjE zBE>XGj-nZhd{A!F>=-0F5k(~C^*%HlRVQ-=vqmKN7(p3HA`;1jHmM#*kJVOv z?J+>q0ab2O1Xz;Lyet@|@5zobija+Mxgx>3@U1RcXp4ZTR^zf`@Wv=gqRmQDRz76r za0_b=;jD=Z;=I`maF*pl0!xx+I>cn{JPz-)a&)!X;^rtfOHuCXD$d0^s~8JY#krj> zlFDLhqN>J61(5YELEn>D*Aua}Y6b9`toUTPHQNZaVOf*aupC2a#z>+sczHpJW+d}7 zRhkIe1r%u|5Em+U?`I@|Uk(xc5V#;#4ZW zBT_V4Qh)TmzSZF16CD?)<=i+v4w)E46bd{7o_K~#a|8H{BZ7; z?!8xvb|kn}*Bd@r|E&JOO~1#vsBHJv`KGj|;JmnNpz~a3uYQkV=YV6U&UkSBnbPFS zQGaQzp=xh2m-KoVckXw|?u*iyVp_=?6=lR*Ba^SjJ zI?~;g`gh}{;{3t2bC>U0n~xlxNta&wHoU0s{TwWi+??y7`c9=A{K-B9%e#B!ny&M+ z<>&8DNv+*DpWKw^SupJQGH-ebR=3rE$1(cmx>vss?xjg7a`N>(>)`Fy3~W$0vti?FB}T|ytmz{)9rbt zvFGWcwpoSA)P-rk4Ev^@WYq7DRW#jJs|%W*vkd$D7dFgW>c7=>HX$3(fuZ!OtGlJ` n8&=#P(yLSVrVW)Ll~eMOtxd&vvW3|NI{>M1FLQlb)wJ$Ed`=v~ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_4.png b/assets/dolphin/external/L1_Boxing_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..556cb8948d15edd4308f66442fd5fc8a4a74f038 GIT binary patch literal 1537 zcmaJ>eNYr-7~k_$K)k#hLpdvU)+r_3+pqiBT{*df1;IRI&a*Hp+1uOWR@mEfZ-Iks z2B|SW8a2u$45OM%YO=u^wGT%R3{XeW1{(-7DX}JV{HRD9O?nSF^AE?J-F@Hp+2{8> zzvpW=6c;TtOfpS^AjnWyz68Bc-vSTrnsW)T-jr+9Iz|TP`quBzpmz6=i_|S=4wqDp!OeY}A2_*k!>!F2)de zL`5riAfu#8+{JLNqKdHHOrQde_UJ; z)gNFzT>h9laB?6)O$)OaR$X0fuBOb2T80q}!(cdxkt7N<&`3>4MwoF-mgAv~Xh$@U_&<$%YDc^^ zVG;9)5v5WUKuH3#5@3+N`#Xv&0ygXtRRVS4D>y}{l*N!%$T<-3#q5{-EQ1paAF$D= z-C_%%7Ax&Td7Di{EfzW;Sa94%2B?Icd*NLSP3GEYk|y#j1d*4ULvt2a4rQlu=sXvP z6A5f#D5CKpK}`6TfbTIZabGN(tBSm)s9r^>NHjumP|=h~Pzl2%Wyaw-ZeEZ=aY<&p zN~1w@qAFF1{(M!D;gK}6(mg2PHi{0Aei3DGzXi2WgcXF(2T+m{M4s{yB*Bx&7~X$Z z`bIkyNI)3j1RIuS0yHl0G>Y>eqqNl`qCSeUqjtN`XR!%Zf~Ob+0~E1%=iJpv2@z<| z`1a0dfWw{P5<_63R4_Wti+lUP*i9^6?9Bt~@bK`?@0-s;kZ!n;b9tlvml`*0ZpSBc zUBkmYb9Dw=_-dA_lc2$2vck4+AI%lZs+A{^}5-6xj7uzu( zGB$^joI|%ns1*D8?XI^{plioMCx7f7H>W4(e6Pv)#_oa6pP)^AM)TITzaJ=i#rmvq z?K_uSF5GsH|8RkM^RJh#To{CgDzEKtp1W*Et6I<=U7x%?<6>9VY5wuqyL(w<_(9iS zA01EWY&@|te^tq@3CEgFPhHdfoPN+0n`bz_&v^>k>)f04?%^X_CvHb$^4bp5`tCpb z`gh+f_3As;HTSw(ov%+^qt(N7Pu);?Y`G~tMjGlCo?MlZ)>%JehtUL`|X@( z`l)?W8=8VXxnySn@z{Z#w()WrFBP?16e>P8Ffqr!y5v;tdQ$gS!8faq8fUV4VKTHY zHI~$N@aD14mXtmJ$)Pi6h|A+zp6Pi1#oFnyQ`z(P>u;stP_}N``D-&9Z1JCRVO|mU IX-;YVe~?!xZ~y=R literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_5.png b/assets/dolphin/external/L1_Boxing_128x64/frame_5.png new file mode 100644 index 0000000000000000000000000000000000000000..fd6170340449e3dcfef8acf374f27e539a1ccd24 GIT binary patch literal 1555 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 P%Lm1Uv-v%aiu(Tm75_Op literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Boxing_128x64/meta.txt b/assets/dolphin/external/L1_Boxing_128x64/meta.txt new file mode 100644 index 00000000000..c66998e7d85 --- /dev/null +++ b/assets/dolphin/external/L1_Boxing_128x64/meta.txt @@ -0,0 +1,32 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 9 +Active frames: 7 +Frames order: 0 1 2 1 3 1 2 3 1 4 5 6 5 6 5 4 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 2 + +Slot: 0 +X: 78 +Y: 16 +Text: F*&K!!! +AlignH: Left +AlignV: Bottom +StartFrame: 2 +EndFrame: 4 + +Slot: 1 +X: 78 +Y: 16 +Text: What ya\nlookin at?! +AlignH: Left +AlignV: Center +StartFrame: 10 +EndFrame: 15 diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_0.png b/assets/dolphin/external/L1_Cry_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..b2636f005f042f1e3f163c5b349d026338f06f6b GIT binary patch literal 1942 zcmaJ?eNYo;8ec?^A|48UVL?KI9txXecSAP25D*fmsU(dElGYU1Y<9^4$;QoMBDRR= zRqfS-da)-WoL|*}QQF#`y;h;>SS^)etvXXw6bcMfMW$!l8_U;i6ukc7bZ2+p=Y971 zJs-dKd1iMS4KK|KUK9)fz%1QLZMLvh3eSolf8pI9RIL}52tLEauVHE4O>h*DW@Rmu zNM|RCsBDU`Zd~6+B?Ex}HYUf!oAg;Y$=aoa&qnI8I|VcVB&T|u1X)7yA`4Z-IMm|d z(_e^1j8!epOVrEtP7P&aR+e+rnsP%9Szbb7R&i>IDA|Jx4(t?9h&=XEhYRFoEK-WeGPk2Cw zOETp_OSwpnaqvk>kozc^hwG_u}psbl3YZpBs8E2l8fRZK>X}JXf>Po zL@|oAPzpJrLLu=a-ugT8p7yDr5sGk5<$^0P8X_@>1{EX;gM?LqfSAA`NFyYKPzbD| zA%$2bq$uPkc5Q!sgj%Q4V3AIAL&r-BpW(p$jx-N6`5ApwCB z#xB)QzvQOQB>$8()|%X3J@aPkk1vjT4n})7XewUzuK~+G_y)`gwdMCCi>IGf8+EgH z{UzKa$vaAn{iCN~T}4CDoZ#@>6m2BHjQ+az*n5(+mCnEYC#WDMQhBH4PGMxy{pOSN zPI$SR{;Ri_!=|or{JY1E*Ov|j_!}=AY%{zzfBOx;Z-K1F&_l~pUq#~@_YA(Rs(-mE zM(bbs+sW5~_aR+x+o>?izU~j#Em(c#+~tDZeWiF&;J$O^F{_xdfP91ixDG_mfa|h`Z%HRWX)A90eiFN0HycuiHdE# zhNTZE!yUh})BoCGPdOK`wW+SF;>6*>pGPtWuschmk~`Vl12S9qP{6`DpXLJ%L#ULHhY z;WeEx&m28%@3DxI?=}y9o6?^0ue(>y4kl-|YPzWruhJ<#!QuxWN zOZb(@o>c!8_spk$Jn9 z#O*L20{U9F9rLy~xjON+ocN+-aV5WnF?W9LE=DU|eoL1m)z@X_8D@BFFBLYvAg>4R z&o<3{t2^n$)}Q}8&)$n!nlDmGSZr=WPPXt`ZyQ`mZcnpgV{JumYUy%53Wz?>VUO2!4Gx>Nxz;INNv}Rs)?AW(!MrW$Q3DWhhR8qOj>L_vuJ0`PX3i#TIZ)^T zpmYU3k39FL$vg9L|Fn)f>)X+k-nk7!MSvr=xGH~1TEy*LS0DHT$Qe5J^1k5NpYH`4 zat}E2Mzv8Htqt$&AV=u0W2Bq;*qYj&`RA`5vAtW{^5uDR(x|sOnm%c8uj0l7&PMwM Y0`{AMfs!t%$@iz!Wf-*0>4jDQ1IiTYMgRZ+ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_1.png b/assets/dolphin/external/L1_Cry_128x64/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..e73499e9455bf597f1821f3eb835c0fe7745ab19 GIT binary patch literal 1952 zcmaJ?dr%Yi7G6Liind^xwovJ;i$x`DvX8u*2q6iQ)?k_h!MUJrHXD*kvN2gmfEK|= z1)ZXWYOABv7u7N?Lv4$f%Ht09wp41-65C3l45JjPMI8_=qSB3m?H^8ecK7$#^PTgZ z?|05@g+6`7{JAl60RWh@EXGJ%886|c0HuXCQ=ryC z}f@A%2N}pWp~&lTr*r`0;v@xrKlAakR%2Z76A@RSq)(;PNF!4V-hPW zfcPv$exPptQAaadY;gvrj~jzsJZ>$@$@Y_*ZJeqvNSYJgNsj9h?wuabTQa9A3Rlj#{Ly;U`RPps-Q;#h=XOC_1I^%C6yd%ff z%!#o?M4csyM_w(w7FYFAehpu*xEL{>xMma5u(Yko_noI@drjM>b3K6?IqJC$sgDnnuOIa~o4jBd}iot&=Irn=2;m;m$86^@eb5Hn97w&^p4~ z$?Yrd{kdPYy;Huc=@?It3HU|2p4g)?>I*HOec&HBSTMVGjv;+CD7oP|V`We5y6!R0 zsoNov1dS41wmzc$W=LsC>Gk?ywQ*v9T_PPcj~KosNG*!^DdW)H((W};i)Lp;L`}}S zmjNuTE`TwIVM4s$k4>z$h%R@jk1Le-kyUELEnA=%&Xg7n|D5R%av1J z?}Pt_D7r2chde*Bza!$BqZ$HtUN|sxs1XeL-}Ch(l0W+`jWvHZ|qLKy6ul2QJ{BM&s_7~oU|P72py)c@%BCSb^R4)q5b%tSwJ`6*q%RJzEP6> zI&YTIVb~g(Q&zD2e*3KECDf(Jl)I_1Q9B*O|swPC_qN zUmtKhW$Ev@>WjatOX2_tU6=g}9J`TJj{T&U`%2rNM>3%F8^=WoYk2Q?1zoYD^6*XI ztB=pwJ_){*XI2{ z^ub>hY&p8;m((Rc-KKfVbXr6+*)nwc-O%9|x)n7v19fm;e9( literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_2.png b/assets/dolphin/external/L1_Cry_128x64/frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..16005a990f1003ea79408df53bddb8fc1a4f48f3 GIT binary patch literal 1945 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_4.png b/assets/dolphin/external/L1_Cry_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..3f7dff4db63ab16d65b5524967813afd7320d7c8 GIT binary patch literal 1938 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 Po`0iKks_~3She~8EY<;+ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_6.png b/assets/dolphin/external/L1_Cry_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..7871674dbb919e67eee73a6335c249c2860c00c5 GIT binary patch literal 1946 zcmaJ?dsGv577hg?P_QCrsii`vgG#kdl9>s4L4c4UC2D940db4k$vjBJWMVRyfUSU4 zE4EN-QK6m!TD97Oio0rCJcxQwi}ek4?ZV266|&0XETE#S>_ox#4^QXJ%=JDbZ+;*JEp;*8~Wm{}Yu)pC1 z2v|rZxHv{9(%IFN*^-j)pwjYFjYNJ9At%9vcp%QL;2c;f76;tcl{TlstpuO?Rd93f zwh#oKny@)a@ENI0ogPp#4hoP8V2BWjM1WK-phz4sp)^j+0}v4c3t{e-La>E4hwfK_)3f+a;=n&)nZDJ`ywDM zq=J^wBC$y(flvgMK_XO)Kr(`$AQ5gt5Q3nklvpz1=S6r_BTZH%qnH$vz%ZPwR*4a{ zOopOzBq=5dM${8ntpVr1ihJI8K;@C0|+N5 z@YZ6BjbvQTIo>mRmNG^;EUPI}<6x}7Q;`*x7Z8xpcnmEvp^!)-h9NO2p&%0~l|fQD zMVKhD2$xA=a2!wmLA+;m%I$DT*eAFs#Bv%Ygfdh6y7bu_WX6{mOlyfL`N^VXtlTt7Lu#(87+)(c$I@%0Oh$zx+<+%)~vwY6M_ zeyPP&M)$9~D<>ar7X_(D`U*UMhvxBSjon)~E!%qVt!cbZz)jabo_@WR>H6@i6PH{> z<5+ip|8M;TDc8GR8CkY;|8eX|(dg^Hmi@Q$pS&lpWh}=&fP>Ai7_5t`%x~AMtz8v+87)tMo8XWPkpaS4Vx`9B;tTI!CM1eR=^m1&C_4zGt7VhsoVtLN?NXZ9V{ zzoa#1X=dm(fx44dA9U?*864fVVq5oMXu7<(G^hGnMB6?fh0KGdH%D3%+iwpw`duOP ziESDElH0AB?$X&ksWGidv#W;4D%IwWM_V+ZfqBe%XsqMm)Fno0UF{buJr93q|D`~+ z@sM0elq$cGrZw`hdtt4PsXJHux#qOBHBEjwtHYD_pQ>wvkGqElqqZM;%q!ALxATv0 zRqtHU+LtryjB(wLVP{a;h+j0n&JkMPhycpm=E2%y$~_=^r`RX>{^qQ+<(qr*E{1i6 z%PQ+1>~842*D~qYk=SoH?lTPi%v+u{x2_k^Gs|Arn+wt!c1$mmUx+>Y37s)@YGXZp ze$%=5JJs-4{@-7HKP;|$3iP=2#=n%C5??wwbYy>r`iQ5Os`I@XaL%WsZ;Sfa-k{(s zeW`!(@Yl)a7KI0k57)$`R^BOZYU`^F^^M4+=CrUK2}5gme}V`Ea)0Y;C~<7 z;wh}}%8qQxaIe`Be`Z(p&5tIh-~B`mcfJi3d60+?qddA#SSFBzwZAq^8$we literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_7.png b/assets/dolphin/external/L1_Cry_128x64/frame_7.png new file mode 100644 index 0000000000000000000000000000000000000000..5865e91e114fe28637c1a8d7e105328f358606cf GIT binary patch literal 1950 zcmaJ?dsGv57M`y7z?CAt8^H|&CD<~VM+TCN2@(lN4um4a!b(*plNk~qnUD-kcqkFs zQc5dIwF*md+lpA$-J+Flt?Pm`vf_~nh3!F*;?`PGURJDow#Z>83U2@Kbk5BD9&^8Y zzx(~}JyV{PxY=j1U@-syKAJdnGC#WbCu-3={(WmvrIsIF;9}Ca6qex}q>Tnt23AiC zHD)rCPNqpier^Y?0DyU=CS4krrcEFy)+{DHHe#pQ%A)~5vC(NIscf1P>gi0AMF~!v zYXOBOgAz;)*CJYLG;K7+71-#Mf5g+g&>GfDi_lR64OfhY<|MFo?H+?D-asbV3$; z(3}S~ZKrG|D`#RYLXSsM&*pJTkSF^j3$s$D-(#+rqXMJ7zoAP~Wj;S8deKu83RLbxGAemDU+h$EZ*=7@t)Kvzr!bCo#R5_at5X3CgE@9G`GbUr)T=KG5E#P)}k$ZKiT-kS-h~Pk#D*MNeQ}`nVA`Wy0xum zH{YRk8nsI2?60lfb9w+-BB_s3wu_NOq3NDUoE?5p1OY) zeoeWvaaG^%;?uN1>%;#Pi6To~Rn7OAuLEex%l{T(V=1|y0bMcpTG7!WFuQ5j_zqlj z=^O9DkRYabg&bSAHtGDKFhAA5o2;o-sikTAhp$ak1^l6G3)~KDprM1Gn{L zddB*w6O6+5_?rBEFIHc+2VZ}+!)yEHs+%EQ$7_Rpt@ENf3v1pftoz%?3L=7){nrgF zQIyph`y$)+ir>GQ5LA|V^SzAeis!Cz_1^>f{O-Td_;`W2r74)q3qK#yzqYQp2u=q= zZcX}wS)+Bg<2#Gt8@c^{r-F+AY1b~P!AFO4w;sE5xTuQlTr=!! zs6Q|jb$>;LyIr?nL*smQCEyPr@6D@mxmQL5_a60pxuaR`4ZQthrJYj0Ia(Ukm6hK0`I@7zB#UCGb;Elz-jv*_y5s&1 zkNdDd7$W#0!F3fmv3K+5Bjtv^uUxv9hYGr8r<^R3OcYcHW!Mduiwtc3$c)jUUmUuhZ zw>q~o(^Vh;NBTTzq%QciW~IA-Qy%D-+}4^mywWuuaHM^6-K)Lbqux=w2mAJor;Lxq zYBnA@yyV2XsG-%p2d=%0S;j_AR{yZ|_`yK)(Hs7c8fw06{E2A|AkGZk{c-;f(4ygV h_q(CBz$3p5;5*&?>Ck?~QqLb#6O*X^>!ys7{{eU+08an_ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Cry_128x64/meta.txt b/assets/dolphin/external/L1_Cry_128x64/meta.txt new file mode 100644 index 00000000000..e96c8d7c617 --- /dev/null +++ b/assets/dolphin/external/L1_Cry_128x64/meta.txt @@ -0,0 +1,41 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 8 +Active frames: 4 +Frames order: 0 1 2 3 4 2 3 4 5 6 7 6 +Active cycles: 2 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 2 + +Slot: 0 +X: 22 +Y: 40 +Text: I miss you +AlignH: Right +AlignV: Bottom +StartFrame: 9 +EndFrame: 11 + +Slot: 0 +X: 17 +Y: 40 +Text: my friend... +AlignH: Right +AlignV: Bottom +StartFrame: 12 +EndFrame: 15 + +Slot: 1 +X: 2 +Y: 29 +Text: Why are you\nalways away? +AlignH: Right +AlignV: Bottom +StartFrame: 9 +EndFrame: 15 \ No newline at end of file diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_0.png b/assets/dolphin/external/L1_Read_books_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..fbf265286418916cb7d41a3d6c38579b229ade15 GIT binary patch literal 1762 zcmaJ?c~BE)6yFexfE*quU=h|uDk@2Kb7zMu7s_Cefe2Xa=w^2V5tEIZ1!AliP-nDQ znHI3ZXvJ3XsCTDo3y4;rt>rWm71Kt)hS5=^mRd1MBO z5i3_*Amadl>oOa1m>m6l6sIf#%&jAESZpjB0OArIHViKy8PG`PnXU2AdfQ4i*&|z6_wWE%BXxJ{wuH9lG1P-e( z1@X`bsT_SesG(>QlnW3(4#O}gR|-f16B)%O437mxum}+%>?7wRa#SQn5hXZ!L99cX zFrgXRLJMhSQnc|9`$a&Q2~??7 zOJ%T1A=YRW2%^&{)Dopumn@NK)hbPLvdClOZCI&Xs+34{N`**?3}dA#wOWe6YMB&C zmTS}=Y>L&+U{;*;_%*YBN3n``Vo?oEVhlwaC~CQ<2-5Q@hO*~VHc%uMz+g{;ES6v*zEmj{@d={@<6|;h&X*I2M5aI_q(p&3LYAV? zT{HiyA&-bHO!x9lQ?Q#iZADtyUZUBinK7QXfNeNl`h0_KXlRICF8rah1OU8xQnV_A zyU#{#a z2?W-RJqN8S+5cdy_t~em``>oIYW4~(4$K%R`KHV=IL7Pxr~Jp^ill(fQcl&Sr%$gp zrSdimY~h$Ev__PDRyyGr6qt0;^@u!ss?7^O-B4$80our2vG+L=1Z)fS*%ExV<DLQn6SY%FW#t_{r2|m7Ib7v3&-t6i&MbP*f@9KsEA~+~CzuGtE`)Mv!5i%#PoYNPU9{lm?jfgN<_p=>*_Txu4 zK&A?ErcUs4cqjGbX_qcZR~L2lcf6ii-Tpq@TXxg?NIN&n`&Q%P>8C48XBeK(UL6}L zjV*%gnziU&YvuAfwGsKMZ2FjM5sK3~vlzo@u8`4$2t!jFOW~u@+a9d~2skCRO z+qxEJCHB`hTx&GeRn@9wQJv%N#MR0A_UIH@XG$}B6zlN1>etT3nxcr@!OGaJ{U1c9 l{;m0G?^?7!_m@Zx4=73vd>EbE8t(qjiXXGyBi3QY{)JoFrtC+ zfryX6Vr{EptuGXOwo76Oe# zG_+WfB22NU$ZTU`v6W0OPR+oJb8$HV#mohx>sL!$K5BQ9i4|r;E%CX6KvfSuP7| zlE$q@3uC0rpwl9zrwW*8h$TA~g~^hV^3t%G9?unU_>Bk%ffQNSZ3J;_Q(aX1Ty@`rhowsmQWXd|)-Q=Yr*_DZIc$nwcbZG{k-p5Jo~F zSI0>rgt0P_N-aYWjY<|Lma8@K;s|wItSUYpb=i0wRuZQ{5sgqHL* zg0j(}&hr@yS4~=tg(MMgrA**xf)&Qs5YWq{m;@K=`67u-&KDbm624w4A^0K;#W68r z(CY~)G>#`;N!(bCvJy53i;IiGKO^kR&UMc`7tlt9E0GDJ*@WjF-0 z6k%u0yi!9h5nGtf<;$jEH!s_YG_$>AWt+y#t?ghN&Rv_Fp&1z&VV4Wfb*ut_w|-4h z$7a}X{i&O{`4=J2Z)Bv8`>AfPVCE;Gg{7M!Lk`sA*RE@pf~%j*E*p71gg$iLK8WMk z<+H7orGFe-b@F+prEF+=!XfxlO~Op%qq>`cL|uF2^zHdE!~bY1HQWaL!+Xl^+J|j5 zS^d4hLz_1IWbeVYRZ3x=PeW#I*GQNHp||cGX!kn(*ECNi;q*6j4iH8!`NJ=3s@j@_q%xsBqDUp>kK-s@gJ zBkV+_YUk!44%o4%G{-yShHVJ&ShRFCvtaJc>ZyNkFOTnAH^H}~n#*~yrmApPzgO%1 z7DJ%6B=3Pod2(4>mjtcw;C5b>O5PD8si*uJgT%m9~nHc^7`?* zDUQvjU+5CsE*#0q^coC{Yfu8k_9?ABV`o&8eD^H<~HZFJZ za&m}6H63XWbDvimM2OI40~nT{&R9!9I^P}N-ds<8E`vnMFAOt=}<7*1gE13D2(K; zyNZSa0J+ep(c;>;Scx7pF=4k3(_yj@XaER}a9Ch{7K(#9G~H+pqYu>oN(YTd7(Im_ z$Bwhe(F|i$o)t~Zi`VG$vh-qv9uW?PIwXVv6N6smD|#3_uPf#Vhli)FXlnRYG{v!=5+VzHRThFB29AT$`ZTr&=7u^h04#bL5pCX+j~QEeNZfc{70 zz1lWSt_5W!pf)VWswYZf2%G>D>HDxFw<5ttvdn5E>H=pgFnx{*HRI8WFgo#ti5L-y zSP{t=uw^2yTp{9clyXrdPpnX?cmhSFOs-Nv6E@z56>#{GGDxWsL1K;@D-$V|DmhoF z6o}*^Zlr1g8*R4Xuvw2z_%#xK$Fa%}VkL4b3gehngJIbdO`y)eaLks0SwM)(WP?E| zMl*uhZHwLY84Fi|T8-;bL}kTH;Any+#`h4=iG(m;&(krue36*JGqCv#osf?(xG~IyRRFLkzx{4>1rO4`#puy^tY9I6Q%f!$Wx@ zJ)K2RWVw6he?2rIA{x`ZeK!=u;oVrFW@47C#L&d-nwCin9912wQI3p^5Zj~+w+aBj zqdQt5(>OYxXm@$cx?{R`Yo+qaJ_D6oo`Hg77c2D=)FyS^Q~R+rw@Lh2rHYLS*@yN3(YIzBe%x95%20hfQ1l{5>3J5Hv8F8@2vn~TV; zY=2+r)okp3(;7&6@Dwd-r*vpW1+8?~>~+9OD$e?7D1VMGxr@RldxEb)bj5V%4$^5o zs7;{F`e*C8NtT9&4e=+=Ofq~a@a+!Ka@BXHUvw=A&~ja5OMNwcW6Pw7GE!qrUEg^U zZ(UQvyuzpDt6eB*ck7nK$HOGKtIX*`;%|`4s5Iq4({1pcr#P(QM#=^{MoV~i}Y0jiioPFy7>YYm~{$NrMrc0Lxc^pywqV^HD6y06$ zB7d_hsid!h?qi`yAKDdZsp`n}ZG)L3XOr#teWK6N3gh52(**r_62Jxu+taOg>KpYsgoG!Q~rL z)RwlSqt(Ly&stUnxg_wK#-?D@|5&bjBz zR%WCvp6nOw2LQn2q(pTlJ(toW&WAz2@A+&`rKj0cyq3x$4O9VY#Q_CI>Tod0gc|Wo z9K{M(ci;;FfKf_lw3IeAMW!cBY}BR0c9<+Q8UPk9a#&D(9!`Ne+(?+^&_MG!2qZ8$ zv_hE5O|_`-Tq3c^if0w2Y4k;TdMO4iiUk)sWV8VjPNAT~lyA1l9CB#Pu8dy0#2g45 zQ=#(Y&^Re=Y6hqxtvD!VBe0&!<$_`<8^=(dj&DHqF(8l2LpTWih+#x5e2@6G#X3%93)sc2e4$)-CqcB-gQ(qc3+a_{FXN}`PEgXc+<(N#a(8jcFR3`pE zjn``1G=&zNlZo3%yH!sgi6Pt#rqlOkM=nL0jV#?t(C32Yt4Y1xgqx`(wH%_qurUIY zN!3cBh^vtBRcZ->#H%Dqfm9uzAP}jQ3ROY^&u!y%SdmbuQiu?#geOH@ScNh{A{FqZ zqIiW^C6RF5*d()!Ld|;I?U$haPGBW(#L84w9HmIBh9vXdMUat8Qlu@Hw17N5n+q;j zL6|YJz!v2?pYd?jxRqFgV+mH$1db(GM!bfAP9jEydVvn+3nfxmVBiX2omhy$e3Yk0 z1&BeX!^F@89(yHm<26c4=pZa^E;0kA^KB zO;Rf~jvJ4(ThbUvK*Q+hp}^+6yC;%Xg?Lsr&l6m$l5{qMB}3kiKR*}Bbl)S>7%y%Q zbg@pBK=O{!3-F1Q`P(|zMn|!pF`GD1x5|p57Svy6+Q}etrK?XMr~m4H&~$JX!2h7aw_Z-_rNcx)A0bOZ9`c zP=>PTN5JZ(RZinss!NK)Y|L6#7w}IEm)YX4V|vB?!O#%=e?O9=fH6Q&)xLL9kYY+s zx%2ru9}jwm9Rwo$?+2)duLqmvuioUGQ`!Lib$n>+i#0wsw|f|?jz6%4wJ^>;4lN2LCiW7e%i42;=#~p->dbpmw*i(k+!(s%C4&~61xYC{>SWP zO9z6Js=?bS>pqG3%wvD@)~>ak6_K@bpZDZ83W9DS`)&*V{-OJKuhOgYzG4K|se^n= zO3n{Qh&Fpjn0>$w=btua)d6eH~nuJC+OVfJCkeA)wPk z_3@hP1pt6gd>oQWuXg&4_F&N8yB-yZ^unTIvZyptOIc780Vr@%LxAymG?z#vP`r3m z2eAYI7=&f65|cp{X(B)|7vf-iJ|7fIxde{F8i5wY!aY+Mx;8 z%2E;alsoz)XXjCrQO4t0EEcXsz$Hz&JV+{)^7t?hhB>qb$6RcnP%Fn^4t7~U2s38V z87UoU0G$?54OvLZ*)-V~QRt0{i7yQs%u~6dv&OTcMjpiF^YnUWXp`DzDwX)3#%s0B z>BUBZmr9t)LK8-pL>ue^)9HJ&Bc~$GMwVpK(RD!!5E3iY69y_Ck+bPPTwI6Cq=-@! z!BD zo#=4Pneh8yKT+K4*c%k=6C5@0q_DF`F&LJYTN8WPw0dmJvum@jp(pY&T9%ZwwrN!p z(beQeh2JReyL)h5TlMuu&e4&E+B+*8bAckDJND0;J^oaM!#Y##w{f?xcm2SR^%Vl< zhR568u6WdM1c?FD`1qQmtaj7cX#+K_fur-^jI+#(f1si&XPM$hHo4FF`E)Q>xgdd((jTZY#u74IAG@iTV+P)Sfn{Y|E`>#{oY@^( za=K;NY8hH+FqE2GE5q$?_s_F;aN|B%lzq6Q zD6-#EA-eEfU2~!JJ-g!ef#|{I{qw1o!n%*&?G4%XMX!%1kd`53?7~0&y=wOb?cBI^ z;Vgf6#=>%8)jnHm^!EvQE3Pqe`u(;vO6{Hu;TO+U@vn!Hl?e&5cLz7Q#3X;5CV3^@dI z2<@e)A@1~t{bw~`r8Fg?vFVm5D$^Yf9Y3MIxKOm}#9~{+y}FPS$WQL2ze-#V2n`1v|u*78;Fo>OcoQ-v8X6Y z9Ut{YtCh6l8$m#wGB_$?QJ8TAMQw2aJ9ZSos8_ZjkHmhEXOGu5A*5pJT?b|27uY|9tVc!kThr}v#fRn^yJKW2(%Ik zXt78K>l`XF+d41bNiNDyNyYPXa2Wx`&jDw9?!E~CM9t+3OWhii95`7NkP z7w)t=Xe(t0eHJk@l}9TehU`!jHiu64*09|*oGT`4d=KW}BRrUIv-v`MqwS&%nha;8#LuD=NM*%VE=vMC3M3V1LWz1V6e zD7R~t?|g>BRg+HZa+1(GDI54E!E)<+2$-c3OoR)~T!Ba`;|eXXh-;RJ2(AD_aZHF< z%w|FY4daP-5;s(%jD!io;pZZ^5N22)#89qCCPKM{S%`5lF)ras2t+8BB0^Fq#UVaJ zk?*UScWTHlVhYo@eA^Vv=51S%cBYq{Ow-7gj-1XkT!20~RWmp^$Sl+9TUP_Xh|VOn zGSzeYFXI^dMVJ#cIM~h7<#wFAZD#MLqi>0;vU`5!+;uMj2LFAC{o%jIBXuV_zsZOS zs5n2+^lYVL^us*cPv>IKf1)r)8=DbtdAa*W?9Ya&u02-tUU3uao<@*5zY zQuCzg%U|k7wvJ%S$KK}C?1D`QuJE0y$}6blcKb{h1S`B6&FLq9=36J09II|!cZt*9cO!ZGI`F`*QdV>h zF*&?)$Fh*3_OoHtUd;QbVbgf|uQP&Z)6G0D_gGi$DX8`R=qHN_lDkOVH}S}vEy*!^UB~=J+DGc YKx;?vKP_DqJAHqfBu$FCBGI(=KMzur`v3p{ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_6.png b/assets/dolphin/external/L1_Read_books_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..eabe9def3375a009bfa5cb8604e7e880eee5c187 GIT binary patch literal 1750 zcmaJ?eNYou5Wkoj5fIc$1!XiITC3%f%OxQnf)bJ-Fa{7}#bS|5?h>w$T(}%2P^1w? z5Q?Lt#nFy-;z(5xkW!>pWk9KlR7-`b2m*gtks^v6p(;#EFML@4aJ;*B@4dbK?f!P( z?p$GP^!hpOi`)SKm=hVHieqL6GeX_i%=@9+UJWz((dq;`o;1)ll)?c8M(S{GqzN_R zaX5-)reDW{0f6lwvbWCo>Yj>Hf!4QA5!ZbvRfhK)RiBA9bQ(^RBB!-SjZNL2{PeBogP zCYPzg#6hq^DpabZ0)bj74HL;!>TpqzDomja4@ahLybUW6Db!-2Odv&M0vA>$R7jNy zSf)^`V5t<5Ok*R>RvIwG4|RpAt|9mm2c(!`xeu$*`c0i9HWiuEELBos?!kjMawA)Q2wK|&PK zqauMpr^A>4Ch^!CiJPoZM#2PPndTxlU^-YRLJ>$T6C)6&6QK|qq?bSvOdtx93PiX_ zst5TDMZT+M-l(Bz5mT713$b%JH&$VdIzoX8GEu?w3l> zXUFeuOTE%K^6SushFLMyNsrw|@K+g+q5RwtfA-a$QB@PtYir2aSuMTvq@!S4@ycC2 zuQFQ4*6tr&6CK#`Jl@c|%(t!}@l1Wscnt8MMbUCGslw8glbif)M4iOT-*&l7_Te4ti$_n-9rpYkxUs6n?PSJiN8|Uy|Mi2v#Nx?!p6&e@tlP2n z&S$(e8+}ffeA>M!ls$X#m>LS#uDJXt%(&ds|DNsEYC^hBIWkbuDjBHVL&O)9dWCNC zII;T9K*7VGkHvJAfj0q9NAut^_KkaoetNK#`$bcK?3rUhe#wsoNf?ZD}qy z*8AK(Q*62l06(-B@$%BsEPpNckLM4R(wT#8jc&*NL$hmY>`$Y7wq};x2AxNuk0Ov? z#CInS+$CQ==(stQZa{SS|?k&FNU literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_7.png b/assets/dolphin/external/L1_Read_books_128x64/frame_7.png new file mode 100644 index 0000000000000000000000000000000000000000..3ff0aee0c806af1bcc5c9a2a268a3b6e124f925e GIT binary patch literal 1747 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 P%?>2%Q?Mi2jJ*EWEY0Z5h&|e?+_1gcJ8Wed zt8Snuk!^(O#DPO_S)vGvx_TYd|a3eQNZA3N-4pYBKPR4ZP5r2?J1CW&y36h?2ii1k26?8xk-|Ojx zA=;~li`908J!qoJXlrGNDyl4SdMe93xEIdJhO#0$-hiLtNGRf88DMo0Jv?q#$FF0@ z2n>y@aOHaV2`QJo5Hc|#3ew6^nMa{eKw4Z*c}c8P=_5TE5T?LT1m$n74Atr|r4GfR zi3{c(hP*zVgRo4v<9B+vjN^hj1PO=3@~~3QgjOIZj^l^|LoiInYslC&0gjBw0&IHR z0zt8!5FO-bCIH1OlBG-qr-ym6PonS#?e?dJ1MEbu_^crjGKipZ1>*O|LL1j+IS2JG zjc01J&NV>_aZoH%5%TaQ@ukPXeEOd6D5l7>(Jcwld|k+u1mmgjQvuFK=wbeg+)I0P zYFw+em~ld-QCd*cY{D&s8OJacVbZEqIA)C7cor5jWLiu(foe?#MGR{+s*Jc+skCTR zMgwY5$Fa5m%aH*O757W?ekZVs=VEoH5JhrK$jLA(<4sUl#&8T<#sncuDOW(!Vmjbu z!tA10eV&9%P$7CX<+X$uKQx|T9sMiYS8CNVwMwNyHA*E$ zdbRKb-ur(Nhwy}uScm+lW8z1A8^)GTM}Xfv9U>~gPfmy*5r;eJI6q!Vh51f1FUQ8l zS}!(T1wgpWMi`utOT#;_u092&ZEAhE{`T%eny5je>02$nv{jb#^VYgA+fH1*_U9K} z$L{!7c{)cg{xQEdIChfl!Ud16q_UO!*C$DO;7X9p}Wqym3U--!=oAR#@bAy(0up&@F`1 zoX)&0U#H}n1=rIc)t(2r$s_A_Nx-YQbE;NNZvod6I&Ww_*p-~Hqmx)z+Yao)*AhPf zF5pTWs;k|y_u!5OrK>vsZH>^CSan{zFkf_frV}h5wuuUsx-(Q2#UD=5jO{HsR{c|6 z>#QY$dg0@mcIVPIu`8t!Yj+i$>h^cmJ^<&0jiURF{Z+SKFwAY9baHm$!0%ZpiVKpG zzkd6BYI9R{&HXD4-#XO}!P$3vKI-{QAYI-f-hK0K_IIhHk~wM9jr-)9^Il}1H9EC= z$ekWM3Uzfzd+ftRP0P8|79`0h9F@zs;`Tc;FJv6(W~`rn-=~oZ-96P+we24d?yL9I z-RQOqv|YAm{Wvf%cMtu;>-myP%cRnwZD%B}By{Y0tMgjPJaO%Ns4!J_bUw3SLtfNv z`JB1QmS;a&H<}W?Cr~bKAf#h2Pj8ZM9XZQjxBI%2N77G#o$sfW2lWejWB+xVxqvue HbZ`6zdoeq{ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..45aeb57b557469d696703ab104b5a6cf276ac9db GIT binary patch literal 1601 zcmbVMeM}Q)7=Mp1YX?IGH7M$Fpi^giy=zOkN`-zph-j(Yqg-uf& znqYK(%_t;OoFiK@6~CQ~#cIUK)GU5P<_hA*#BGSO8OdCx?26Rw591%Z+}-oO?>)cg z`8{7(S5!DZK{`na07$Upn~Fsl5T9I0wD`U+*=7|*ijZd)N;s!bN%Az%JGe3$ws^?Z zbTLgjsw&#)YyhHJ#%34n)&ecXdE{hRNACA{MKl1}IesrmxoH6|qgOMm4tf05IRs`L zI;2!>Ram`7+QsBo^K?mdp^d6`Q-lM_nFnY4wW0wJEs(I^Q_lLdejPGwS1Yc=$0!01 zs|ao#@{*L@S_B(8o`yAYOhzdb3RpwPX$OgyDV-!W2gVgRhGOEakzpDwuGC@#JaQqT zL*C)k7Msi??!=uAaS4J~i=vg4mGVlZoa0xc7(o!I0!MLNCThrhRjfezWvnkfV!=fF zD4y{O49CJ@i)0zMM$jQ5*_TmxyjJTg!>n&4S7O#sKj}p=xdQcg!l4ao`-EcpUmCB~ z_SveuG+IpixHUW_mc*GJ0gLH-v!k%0$VR)6XT-XYV$vbv7rBFRXw`&9 zW6mQ?s!XLB!}5%T*_21%xXNVIs8s}Rh}d`?7T0H)jf6?9G3pgztihl%5E`Y@oT)PC zF|#^?wXi;cWGOo0ml6GrU{!C#YK=Ti3LJ0axbjF76uCHo^SL-Lj4R~|cxEZXI=D*T ztZ;o^hHIjEW-aY7^PC4BPOz4F9Ra7>sZwXDWLX%2$y8ZtLPjVxYMEN4%EU62N}P0P zkP*D&|0E6-38CQ*`A^40j>I+$FJFy-xOp{1G%HRHFOCRR|I{UpS6oqnEl-p~Lqkn> z_ICjgRcbNmZT=q~)_Z&xVM*QZ|?6rS@oo1MLdsi?}b8l2Tz~5 zpU^gV>czBk-kf_+kEx-7&cLzwzWq?|eEmMiWuFdaUY)%2#PyuHpy2eMeLtOf$DZ*s z=((}1CN(YQ^5fCai|W=k6ut_^d=fZX7Z0tP@~Pz5LL`u$nA=(!ePzSn{mEZKr4vnH z%mmp-!9yDAQH~8<=-EBC6O4=M{Gg-T2nq)hVmG|_hSc=$ft0cA#(~j+1%c+-fPnIK z_a>fK0`ow#)CAZxSo-<+wO|P{7W$?7Ss;p;k=}FS?MCT##Q?cg8W6xz>BDjLNuaML z74UJ}8;%|bsvA;svva3zsv8xA{)pi->^}TZ&c|C^)^=(A$>mX7b_8Y@S=&=V3)p)c z+nJnslh1gZwyWV9SX{#lZXNY4I2)T1-1xOC&{liNaHXgEc-^%evit7M-wz&6DTMY$ zJ)N@BJ%7pKU{Xw|wRhjrL!k*Fdt#$3NCbLoGqblz{sNOE^Q0}ICrGfheNq2|Cv$-g zopZxIy@~FS#N{TjU5^$d2A(awbfly6@VmO}iS6ez(&7i(W;8s!52t+pV{&S8R9Eb& z3&^_i1LG_owm_lSl7kO6$JBDop3&~{d%7kPmz`Yls%6GqTbnm^_b;P*$A3{$@!Rc3 z6FLqj)}>nFyP|_@<-J8U1#OciW4BtGaOapsN0L@ft$2QN^RMqSr}}ohRom5aYgRAi zo;OQIe+ONh>h-O--S~WW{`wZ#h4k+BWc+!0U>Xz$=IG;>H#M#+3;)zDd4;AUhE*H> E0U%63I{*Lx literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..a4508f14b5a5802274f21c878a226c2aefc3612a GIT binary patch literal 1606 zcmbVMeM}Q)7=J0>1SVi`L5*CB@ng_luOD2mQly{QX<1h)bkVsUy(?7c-FZD|W$LO? z5rdH_;>Qp_vSl-IOv8dE4D66ZV8anzjLfO{IWi<}Y-5`k*%hhVAI3j+xx43m-+O+~ z^LxIo&Qe&AlrT2|0FY!_p||ofz(4tMk^K8X++H&;(m8{jE23ST4`)fBA?Q*PGI{Va z(n{jQ#tnUBE&!2~+h*tN=2a>u?GfW49kJiT@Mr*Xm-`vqSwV78DOu*G)WXLd7le?T zPzy^GW{H{6k>&0cH7r?FQ)qM6R5&q0xI7Qa^{aRT9+JZ$zo(M&s{CrqR)WUL(V^lEg^ZCR+nV4qFUo#kSF^*3J+s8zc5UBXL7}74ff*h~wHPXs8FS=X z4PsP;u_nsP;gpjM`*rhvXRyjwVpTep#5tO^(R5|F2`uF_M|;a@29nCe5@<<@n<8kR zcWJ0T&%@P|th<^dj4bVerW34kzl?xO;gTzqa*-0j5RqJ|z(kk~RfrUFc@B~zlSy#` z70%#^|C2bFCj^H&$Wp zH}iInmEBwZY`CVo>&6k_r@EP|Ps##kvwCG9sU#jGRO?vuNm(>F1)$jT>+;gB9 z^x)X%g4=zgk5fVSy#p!DAXQMgBp9L8yuEdHa$3Ogbab4UxEPU91PLthgSO$u<1t_; z!ao>W3XGQ3`S0F|1y{Aq%B|TS?gCursx$~6j2fgYu_H?*kJRx24xFBq4T|f&eJ#Hr zqIW_5&)btSl&<}ey@?IAcuS8Ykh`_#uxU@{sZ7IPwB=*}%JRS0BVi?D=q~Vjn^9`e|i$A*lbawTh3tH&|uItf` zgzvMuiVg1+I~K+Df8{cZuH9(9Vb9246W9y};yr#U<@X4A^1~$~O+Og|XZx*VmnO47 zC!BqyqTtQe^-+<>?~RA67XcH-8d{{7@0 z{Fb7eqZyY627Gr5J0A-7bX=YbR<5Z(AxKTswG5_zumEj~o^@@*E(a`bxwI@jQ&6}0 z<2Ill4R+5%yH=J9dh#5_1zDNpd;J~twV^+|$xx_o I)jGEQ1JdV1B>(^b literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..350002167dcadbc5d4793f9bdf255e95c5341361 GIT binary patch literal 1654 zcmbVNYfuwc6y6X6=)@5ers~wX4Rw^3WV4%uWHZE&kObopM2HZ12urd+3fYaj3rVz$ z(JE2_MaQH;vHjurqdRx^+{gLO z`R;klevy)#ICaYGDF6UWH7?SnveL`G@u6Jy`$y;&6DwvhdJB_A7BDW1!U2tq%*R2a z11rQ+am?mf*@njf0GF_vEsVvKq_mO_5$4wsxgAax4FGX!w-d7#;|!RO7upGxV5s$s z0JPgwf()5SY;q>xMfOFjC_HUdve~+-*oxW&>V;sOTge)5;0y-39VG;8rUEUV2uRLV1yM9__ecsC$RE&VwDLLjxi)slq9pB zm1ARL`>)mC1c0DwqfTRXU%y{>BlQbLQcQQqV!7ruHhw@3Y5?uq%p(CWNX=U-TXoK2$;P;xjXsz@JUzou85h-+czFM)Lao1r7z*6 zWL6EEa{=NgCwVyWK2KwHM21%6@xbBpj$-tMrEF5}vb?AFI=;ErALi?9jOXNtc{2yk zT}j$kIj@^L66yS2?U`5g9uTp{&dut~d^uD*gOkBIHKhpUUZE6aH~4#Ev{~FpZ|ts~ z=H!rRDb3h}y)oYYS&baC;`u3}t>ykgI<4}qL~NSRxhv?%E((obXO5O_SA2Rw;5`=H(UUTemoVqUIA8N*TNwA^SJPX}>mSw}*(@Rk z|Nb*om<7nlo#x8FX4fuVyT77|9Oxg~u{&o6k$E8F>ZXOf zV@p;SdR`)f^&y#`T#8#%dJLaG z+jSv52>C4JI>!=KS6DgHd*^aYt0H+pZJ#!4gGSQ0L_sTB)*q(A<=@J3uwq%~@4KsK zaSq3-%Xgfao7Z??bYRTX&9ArI%fEUHs4e>m4=0}2C!H8M2&jYjz?W{mYP+H<(f=nf M>XUWHw0Z0P0mtBHEC2ui literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..7addbf932786cadeeeda3e7e344b670344f3f31d GIT binary patch literal 1658 zcmbVLdsNeQ6i#^!3n=rj`Iwub&iPE!q%9?t3iKr$C}6FCib_aR+Ch^p4Nw?tmCd0t z#i>(alesA}P~1E!Zt6iE3Mg`hQ;$=oq5?jcV;(m^9I{lw?GKNCEGNnRJ??k!cVBDc zVix%EC-VUS@QaL4>pAJPg&~ z{oZLZ1OVJAiy@Is)J4k)+A73dIzorl#-RZqB-CNUi8PW0Q^-^cr4aNUJ0SoqMui|j zsuSsKD$;C;$YIDuIWYzzCyl_2g3$S3h(pd9u#zkeI;`oGUG7i_p4ydjYu7O>0H3O` zX$rwJQi-}aP(?E&C=((OArgr|873r+IGQ3h;lx}J6`=@>aIXwPWO7t2M=)^Y5pWI} zqe-q;Ye(F1JB7f^vNkykXJusxv&2H0Nre#%!(b5#qbS5_K=y2k#T^i3pEhbiP1*^@ zVq+~d1-dNaDRc&_5O8FlMPaq+bk7Y__K{q1S%V$84Mv0_*lKl!_Eg)>>dAj;yinV2 z$hMKNp0v{$48fJeG;I{jrSIjAT#6hUc`Re$>Vl`MX(Gc)Qf#DJA>eL=MvGA{#bh$A z22)Fd#99Q=s4%TsgQ2KIt&&M47^)n#@gl5Pt&wO^R3%e|i(FWxQli9UVzD+zq6|m0 z(ot+AWoL1UAV>XLIKLxUX`=`8y! zSACv^t0oysCTY|%v=w}sV7cW51W=RIB#{P7pkM?;AW5(kgD|m73P~lBAS6gEMscG| zFoHM!pTxl&A=uR+|LK^~Bd!fy%jYA&ZJrMiNpX|Ia3fNhwD%-8US4t01`Q{Nhlk59 ze{~fA+%`t4!wrrrzZFL@CqZBJ#r_j)<#=N0H`9$po6pq0y|;p=SUS~%=-ZHW{MMVj zjs=RgDJM+B58&TrQ?}h7GxHkUL9r0!=5{D$PN)djstpt8=%I>zIePg}gxcl)Y_8zEM)6ZOvXy&zoU z-Dqc=a=#wonO6eaMSFQajBUGK02R*(ei#?m7g(~V5UBKjlz8c{%MXeT9r4TN_wS>q zp1S7p?w_I7&I7{Qq{#HeLE3_r<8J=Tk)-xJ?en}MDh`(f(xqP z>_)dCp9xzw=Pjsq_vi2PSYlH(hW6oS{fgc0ybEaSR=+w=SqN+!BNW8l+rGLwAamE) zZ`+f}9Y@QEo&abWKshhmteP=>L;OeKye;=LuMDRCIeUMr*=zY?-Jwl6=ekpyuL+Za zpf8&B$WgG<(hJd3-x=x;OTAbUyLNC+)XGz>4b6sxkn*XYm1?D70ST?{XUa?4*7xmh zdF}j@?OivcjW7@F4Vwo%EKj=^SUIQLL3S#kQ^SYD0CL|{{% Vukz#EMy@vjAW{>f-mgr~{|5nAZIu83 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt b/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt new file mode 100644 index 00000000000..8ad8d42a345 --- /dev/null +++ b/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt @@ -0,0 +1,32 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 3 +Active frames: 2 +Frames order: 0 1 2 3 4 +Active cycles: 4 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 22 +Y: 25 +Text: Mess with\nthe best, +AlignH: Right +AlignV: Center +StartFrame: 4 +EndFrame: 7 + +Slot: 0 +X: 31 +Y: 25 +Text: die like\nthe rest. +AlignH: Right +AlignV: Center +StartFrame: 8 +EndFrame: 10 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index d5a8e2a1906..d6e16dd13c1 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -1,7 +1,6 @@ Filetype: Flipper Animation Manifest Version: 1 -# Animation 1 Name: L1_Waves_128x50 Min butthurt: 0 Max butthurt: 5 @@ -9,7 +8,6 @@ Min level: 1 Max level: 3 Weight: 3 -# Animation 2 Name: L1_Laptop_128x51 Min butthurt: 0 Max butthurt: 9 @@ -17,7 +15,6 @@ Min level: 1 Max level: 3 Weight: 3 -# Animation 3 Name: L1_Sleep_128x64 Min butthurt: 0 Max butthurt: 10 @@ -25,7 +22,6 @@ Min level: 1 Max level: 3 Weight: 3 -# Animation 4 Name: L1_Recording_128x51 Min butthurt: 0 Max butthurt: 8 @@ -33,7 +29,6 @@ Min level: 1 Max level: 1 Weight: 3 -# Animation 5 Name: L1_Furippa1_128x64 Min butthurt: 0 Max butthurt: 6 @@ -41,7 +36,6 @@ Min level: 1 Max level: 1 Weight: 3 -# Animation 6 Name: L2_Furippa2_128x64 Min butthurt: 0 Max butthurt: 6 @@ -49,10 +43,37 @@ Min level: 2 Max level: 2 Weight: 3 -# Animation 7 Name: L3_Furippa3_128x64 Min butthurt: 0 Max butthurt: 6 Min level: 3 Max level: 3 Weight: 3 + +Name: L1_Read_books_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 1 +Max level: 1 +Weight: 3 + +Name: L2_Hacking_pc_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L1_Cry_128x64 +Min butthurt: 8 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Boxing_128x64 +Min butthurt: 10 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 3 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm new file mode 100644 index 0000000000000000000000000000000000000000..46079c3728bbc787dac3b6bd912534c3cb26c2dc GIT binary patch literal 481 zcmV<70UrJV0Nnuq41gct{-6W=AJ_nYgZv-l{|EQLAJWJOKV)zscEkbuK!`yZ+7IZ2 zA`k^H{0tv403h%n5x{%GBY^UdKr8^Whl&a-KGuK#@pCZ$fTREYfXmDOv4I8_7$4|8 zWM9wy{zW2%1H=e2Efj7e5vqyGsa1l5#-&xN^>_NMRwDg?{NgbPBEkVj5nrNFDx-1l zhs9(-K@X&ej6;zJl%Rf5@%aPDK9qp^!U4m790)wJ1NH}yWI^RnAHn>AY(wU59C%i$P0j1>yyZb2eSZ&<<EXw3P3+Y`9bg> zGw`qs{?L2IGCmF}0sTSa4~A5K;qqV!{Q=`2l1RPCF<=n=r}ZkY=Heb41yFw9{7xhI zFDwB4@9UBO1B(J5LHS?bB2PRR{qOIQ2c8r@kbrq&0D=!9D38oEA@{_G&~zj7CzJxB z|4{mWm<1(&sQl034`;q07 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm new file mode 100644 index 0000000000000000000000000000000000000000..e12d70796299cef4674cbc2e84e90f9f89cdf652 GIT binary patch literal 461 zcmV;;0W$sp0LcLW41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m%@IofB=C?8Ndg{z#eBYLYzDVKl;Doq5uEy!2|jNkN@@qEZ_g1{{Pp(1NLHZ5`cK(00m#7Q7XS*{roa9 zP>mvRO#zP{B1I8dfDER%u^N0_S{9+ME27rFS^biz)e|IKu$OrfJzlYp@m=A^i z13({@Y<|qu!2#onAFWhAQ3xC$9wqgWREOoTfP>0NKU-)#WTfGU z2i*Rg2mm~v^OfMhED%ZsA%8#U20{r$lt1U=0Kh=#f%SiFb)nte>GVQX8}N{kLpDxphzMk`jYS_5(oPa(oq11 z=rM!(lE5Gm0saHX2qW|VhsXpn@ObC|;s8K;MZx|L9)LMu1M+~tA^`CL5CH-H5Ac5g D_X*BS literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm new file mode 100644 index 0000000000000000000000000000000000000000..b416740f2772d3a057f2fd7817d1b16adc40af47 GIT binary patch literal 531 zcmV+u0_^<(01pBH41gct{-6W=AJ_nYgZv-l{|EQLAJWJOKV)zscEkbuK!`yZ+7IZ2 zA`k^H^c){C0wF+S0QZDP0pybz04(9)A^*i^+Ry*~UJ(!=fCu&iFE9Sa1Q@u)f57#T ze?Ry66pAD$83+_~QMimosuwDyRuT^yr~rDq{Z^|He!zZl7=w{wfTM`7(I}N)@Bh9Z z7$`hyLG_Ulh;k|-k`Ky0KOlJr#iT$%52PR*H}SxO$b3We2asfNK=G&#;Qm@60K^CI zA1_o5fFt{N6@Y$KvHOF|KwuI3io_mN1^}7x9D%Rn9wc*+m!`v1q^8X)k4#Xu+Y4~RT;0Q1NX)2sad=j0k7 zaDWCve^7ZuGEfLWJcsCiJcrH!4+o6|{`>pHGlCB@`lHGpQCE}$15`htJfxB+0svwd z5&O^ABUOPy0pcG)`Qk?-1cXR1Blo|)MymygNFZzV2b72aGx@y&3n3r1{5(Bg1R{Y6 zU#L8X11Nw*Xb}=HkIJeaNU6AJ8B+R4hv^D{NTeDU`iIgI4;;aiNAlho790vA_Wy=N zK0XWpePmzYc#p;<0FqDuqCY?QJb*(NfJdkTf&dsjCHM#kKP&VI06m}p@_;}v5)gQS VL;#<_c)&s99)L&z_y-VwgV=lG-g*E4 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm new file mode 100644 index 0000000000000000000000000000000000000000..b7e15ddeec980a2775b4df50901a2996a7991609 GIT binary patch literal 437 zcmV;m0ZRS>0I>l841gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m%=`-fB=C?8DIy+z#ecc0u1NLHZ5`cK(00m#7Q7XSuV1`CY z4;p}dWM9E}$YfC2*hpfPa4=c@OXF0}r_UegN^% z56ZSbf5?6Z=>q-_)qejQ`Mi1+LHxrffbqZ|-TF0H6yP2(0sBV>RDt862aJFoGC}&=L+8+l2i*Q}m=6*VKNtN# zd{AKc(iigvh_l84D1Xf4Vj}Sms{J?+L-`NuDi0_HKz}+YI5=bx0sQ4bu z1P4KoAI(-jfESHKe=;lefF3as{MB|b@OZ!2ev*QC=rM!(lAnlwav*r0gnpm!`2dDq f4<3LVKnM@;dPTwU03VaEVh!?X5CFmc5Ac5gJVnKO literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm new file mode 100644 index 0000000000000000000000000000000000000000..202ad6e37bf56c5959bb6ca06bfe101de96248f1 GIT binary patch literal 459 zcmV;+0W|&r0LK9U41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m-wj?004;!0}Kbnz#dcx1i%(>@DTs%|B8qI|1SuL5I_U_0hVw7&;Nho;}HS~ zJtSYC8Azf)@d6BsMH`63YQNBU)oQSjz<~y<)!*v1SdHWl*@?s;hzKA7uhA%#U$6fD z85k%$YC-jpe+AokJ_ZaE5rzZL-w|V$E$z-5DKJ! zoPXj14;BGG-Ua9f3$0RibCfPNrA{TK*8!OC5Y B#Yg}E literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm new file mode 100644 index 0000000000000000000000000000000000000000..cdc0a2a343182740de054af8ad6e8a42848e616b GIT binary patch literal 450 zcmV;z0X_Z!0KNeL41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znX0XaGQi!2g5h;14N;0O*kL5dZ4`iiiLI3xIt9f&d@b46}d!fBXLhfO-M#BK-i# zBvAk$$h1+oj7F>d2aQ&$4;p|FYQ0_lt5t~JK>e7QLr8QOfB{$NluEDH|9=dKJY)g( zk$(l_A<-p>esS^pWnd5*0R6+|3BXV)Gy(gE%ZraLjs~bdnU163^}>(p6OjCW@Zt6! zoEJb3A^FzF>jDRu{(^{EsSrG-1O1RM=zd`FU&wr5PzwNkhw2X% skiXadA0QBe#zDjX0t5hH_RvoO$`6hhIbZ|wE{qWVE+GIYKf(SF;Hdb~#sB~S literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm new file mode 100644 index 0000000000000000000000000000000000000000..e8ea3aa683605683a11be4c74d8946e2c7f720d3 GIT binary patch literal 442 zcmV;r0Y&}+0JZ@D41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znX0XaGQi!2g5h;14N;0O*kL5dZ4`iiiLI3xIt9f&d@b46}d!fBXLhfO-M#BK-i# zBvAk$$h1+oj7F>d2aQ&$4;p|FYQ0_lt5t~JK>e7QLr8QOfB{$NluEDH|9=dKJY)g( zk$(l_A<kmk_ZF^z(e$o2g*laz(8aN z_Oye?{SlyG8UjCRz&v2k{s#g9fFHF~9zU3V0>V%c`&5DB@du6k9x@U7+d<={hrlQ1 z0jVG2QwV@Zpb$J${v@;nc|k+!zd8+o9#D7u)oK9pfUoBhNC%b$e&O<~gNFe_f6RwK z4=LdOvY_&)kLwT*DZu`N=kOoah#oWvfc~nm5O}~B^NEB5&3_^LAEb%}06`1=f8p{0 k2s~sQKky(x1`lln@EoA{;e(a{KO$(s3FH7^e}nuVz*X;M1& literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/meta.txt b/assets/resources/dolphin/L1_Boxing_128x64/meta.txt new file mode 100644 index 00000000000..c66998e7d85 --- /dev/null +++ b/assets/resources/dolphin/L1_Boxing_128x64/meta.txt @@ -0,0 +1,32 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 9 +Active frames: 7 +Frames order: 0 1 2 1 3 1 2 3 1 4 5 6 5 6 5 4 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 2 + +Slot: 0 +X: 78 +Y: 16 +Text: F*&K!!! +AlignH: Left +AlignV: Bottom +StartFrame: 2 +EndFrame: 4 + +Slot: 1 +X: 78 +Y: 16 +Text: What ya\nlookin at?! +AlignH: Left +AlignV: Center +StartFrame: 10 +EndFrame: 15 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm new file mode 100644 index 0000000000000000000000000000000000000000..9147714c1e291e52f40294d265c696fc6ae5c708 GIT binary patch literal 889 zcmV-<1BUzo0CfYDU~v!(1pp2(7#2ba0isYMQizTMiKK)^720#ulnk15?AeiDP5lAF}9xx0=5)*)eL;*vIpiChW z2vinH0iZ(RGDw921%bdwu@VA^!67(^gd$0#2aG2o4-<&cAdtL-LlH72#WvB=6^$ zQ4eDvkYafN;s=b1k(9{N4`V=qLP6;57zC8|VIja=LFk?n*oQ|+K#)c(0rfqO2M~K0 zLcBrgfS|x-GYE)0a1;oDJz%jH=$^&_5eJOHQYR5Y3=H-;3J z;z8oY7x91w7JEuWz-l1((jLbHfPrEcLJv$`nD!u1$iSfx51=%mkPI2@tdRit)S(|j z=))uM2pR0k!v={`0p0qmg=NQEQeV1R?l2Z%kE5OOgnjNpVn!Fb7!WeAc$ z1f!Ci1qa|V88Pf!BLt8DgdQNl02G+^G6UF@MnQu3AJPpFJ)KZ6R{(lZk7QmNq$-fb z5c;0Q7EYlDjAR%wz!1ZaX#~JRBoNTZ*umpY&^ko+GK>x)fGHaQC}0`G*q8?;2qK8& zqX6?gh@e5^EWpA6;sF>G18D?3jFJVBghWB(z(^j_0Z;-Ah@3DOh1zfsJ0dJZgCxkx zBv>4DAMhV5A-G`&u@;PjAq;>|!R5p~i~txiC=7!EIPg0}9$AS2XlM{9j0y$u|HHxM zgo#2j7?=dAD@2h4#s&{F*vT*;@sUtu7a#e&;DGUjA4EDN0kK~j=L9v1ET|GFO%lZK PKzQIK5;%cCMPd(Nc|%A2 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm new file mode 100644 index 0000000000000000000000000000000000000000..789273d9da32277651ffe8631e3a657068af1b5d GIT binary patch literal 911 zcmV;A191ES0E+{N=4y}%1Og=ih+t@t4n&eg0Fhv70YQRPq!LI-7;z9u1TsYts7N6& zNR z$o4WBCPEN{DF6geBP212?4l5f4nX!&3>F~_FbN^-b_y{ZiR@&UCTm-JP96%9yJ9qKr|GH z6vS{)br8r8V*q^)hY(Aq0kl${A!ui1e~BN=8CK2M3m7kc%A! zN|87@PGy}NWfDhP$O924kJkioWNEfk>LZ!5Ro1U96{xXJ()mZ83-379(Dvl z9?48N5cq?L9xxmLSadvsXyjl+Q2-YLLnMO4G#QEnN+Sih1JV+EAn3s&QYadmV_R1v{}fpA@PAsAdmpy;2~$R%tRnq7ckPkiSeHU z#tT3_o+gt3p-ctm0E5y8r2>IMXcZuOz}4dt*p5b%K*~-7>U$ZKjtdf`VltnAX+t0w zv)W9uSO9pJMj0O$j6(o>7zBeQ0NQ*3-lnVhs$3g!A^0FS%5hBEJ4 l45L(JRvY7d;D@rr(jpWI1Om$MMTnlp5kSylWRMF8JwP+AOc?+G literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm new file mode 100644 index 0000000000000000000000000000000000000000..a3c87e0a6de7d598766d94945ad530339de2d96f GIT binary patch literal 910 zcmWO3QA`tO007{-UTg2VlWR{l4p!RhabVLjJ?1UZrRd-G=&p`Qkv=S$$tsjd+zY5# zOlnxK6n0muwjFDVS+F2;WG|z-B|aEaLWKikfIckx))b6;=n~i*F-tb*>-YM>Py$hf z1*6D16uKlZ+#2hY7#aLxoMIV_jt6P(W}6dc<>Hu^O~T6vgj_qoJB^|`!{gT(Hyndk zu*s5}hDPLaKNSO&Z~#Bm2MZ1R4LUI<*f|E27LK91wpMox%#&iA1w6Q{y0@V5O`R8& z;bj;oo*zuc-=`nzpv28JiEd7M`G{R>fK3WhD$^tBuvEQBrG|!s-N1Yypcw5J^aUBT znp&PcazMLt!icV#>h91NNZHjtnVZwZK5N2Ld%@`C4g5Crfg*&U-5lsV@$c37;rHJl zmr~feKIp&K&tHnRT7losYBnNNauj~}iBh{;e~o2Qge6`F@7R=Lh;ry|9}rgsm(W}B zxT}$N9bLzrkE<3AZKfYM&&nR#QB>#Q2|I7_L#d3<1gn;1Ghq+m-!7$9bizL~rdi`k zX1EsS(5w%m52LR2K|I~;^ZGZDC*r|->HaY3sMBbO_u>f&?@Y!JTN(_5zjnm?`n!HS z+hwXE2{jYv`uW}ZLm1RW9;@Q z2Q4h62Pot!f1d1FeDD*zD}zBJ+Qqm*>WuU}745z8#Xd@B*!Y;)%V)E)l}Z{$lTE==&kxd&ed~{#*&6c=+@X{`Af>@ zOGD93Ti-4170XD=v*KJ(Y*>0t=(8kfrf{D!TLrGqUr}$Cp@ePuExPdalNZ`dsD-r8 in?r|oA0o2lG%nSK^SznFf@KnUs?wiQ+WepN-~R)iPDSnj literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm new file mode 100644 index 0000000000000000000000000000000000000000..ba3012b7e5a476c4d58f100bc9a8ff16e28a31ec GIT binary patch literal 923 zcmWN~-%lEM008j22iDH-bR?NuJW+c(CdW#W3JB1r=X;5%peN8vT34J+c05U-O*d;@yWx$5HozO?*(cecOtOb9P@p&`EO#ew@na zA3d|`+nnDFC=`LL@xGU#Q-aOc{WA1oLf2WZ0v+KD>BoGBmG*$@B-G^>o+sRlhn%c{ zB^Meq;rJcjFILI+SDWBn^`8+ebdPSo+!hr*%m5LwJ)tE#`=PJXX$c48%ScpWPsb@f zQVOwuJ!D@Cj&!+_o`4ceeu1?yH)k!-89iw`q$E4l-1|DzJP2Nt^&32BeE%E#Oxm$s z2ieoNC7@>%U1)NK2wxW(F;-!UygG|yqe6E=PHvkjw^S!ms{tzvl`b#BH>=s;DvesW z{{MF@+^631wT~~yA0y#YoZ0b*qoA)z2q9qZ0aqRHZuk_+>-$d*SBIEt3sFJQGYJFC zWNhCR8}l*DsiLtQeQtQ{C|7UnYb)}6(q+_1^G3L(P2WWTeSIFt>8UO995IS0!gRcK z11cncz;~e&6BZKJnWu9|^$5=M^u$mWD!a{DofF%g8yP?NIZlzXTkCs6_Ky+biB99| z>Mu_IIu|64`K>-zp*U7DU3^I?_GJVOb{mU~0g5BVauYn)C2d1Sm1#R(F|j!|x)0L} z$d#~l7V9&{xNnK5Me$?o9W7D3_R(tZ;}CfaMXJm6jE^03Ve)Rgh1WLt7(e$Gx#lqr zH9pCYq##jW<02w^PfxVq_dp}hKLDj-3H0=|&O+C^3_&TkO u!B>y&mLZ0pg#?B-%Q>X*d-qc`wX#V$vsWyO$N#~c`oU33;av~mpZ^Cg14kzS literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm new file mode 100644 index 0000000000000000000000000000000000000000..1ce28c7adb733388b90d31ce7bc1d5e8a5998ebd GIT binary patch literal 894 zcmV-^1A+Vj0D1#}QXrB{1tLKaqDU~1NDLxH1(HIc0P%!K!Q(>2$RZR721bbxq)8@` z08rs5fbq~&q!=1RRsjSSh}dBuA);uaPzV$z3jm-|P9nz~L_$Pm91uKUFjy2IL^!V}{~{_Hpxj^)EDQpmbsSgo z|BsPCQDe+Vl!7HlfWf2i4fGlXM37=i0zDXoCjrEPz&;RIhcNa~DO46E(13XeL%^cM zp)^PbAoe2xfMO}W=V}K8gLXqMJ5Il1yu>guJWSJm(@FFL)gveqLix52pBLIaS%2ATcSpn!e6j;Fr zqbQJMECBPsNKs=7J(Vs2kPoH;14w|L%4bqzjL>o?5f6?e_Hrr|l1KSZjQAc|0*Hvk z{xAs?+g=G8r&DRN@aDL`WVW!2lE(9w)Mhf(H@!KcpD8G72RMBpWM;Jt)BP zhEX6=7$iz!A7m)vL4ye*3}hHFz!1Z*IKx;|QifF+lO)Bo63%I7J2t2!!3I0Ryrg$^jCf zz#&webR9w>p|T#v0!0w`(IF=BpNq?gXc$rKau|pt26^C*gUd*kc%7yW>EmNf6W9Z91RfmQV4{nAz{7?M0O{Eq`@%&afrxd UkMj?d2=*|_Op+oi5P6M82x}2S2LJ#7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm new file mode 100644 index 0000000000000000000000000000000000000000..4cbc4968951c8b4391c7da7a5a90cd8bacf4b245 GIT binary patch literal 940 zcmV~$T}&DW007|Q?l?|B?+4tlrFdKqi%ys0T07Q2oE;wbWtuP&H<;dtx?j^-axY%(8X+J`L~)QX_U8Z9ud{ z$>+pFOveYBQn$Ug3U1KaRUQgZ(uYiM9tjX|W|ir+Fo9Y)A60z&614IFo~{;ZsB}8G z>lV(_4p<4=ne-g*jG*{J0+%vKmTWQl?=Wl73gna`S8yez4&Y5;6QHw*o+PKQCFA&v z>Pn8iUM(WsXR1McYhBpj5T?Ofj+i(w^$Gb{Ekwr=Is_ETi|tEL&i{bU`kj`qnA@X* zX=_^{16!p&47ymo~9}?nvBU)V5th52&`>CH?B68DV_ue1Z0Y|}sd=m`$>P;`B9+>0-W?$EqiBJTcng%6I^rVw7^)5R$I52k`b+v zsW|0TBE|14D?*WL1Uy4#3HSV72k*TeC^mH(2tjQ@ZSG*QJXb`2{^qI`xVFX0<8b;% zmP)O-#cf^Acbh{U8iCEDUN^R0h|ETfH%SLxgf?)!MN4N%HAqu`sbLpJEQee!=Y(jx z!pn)ifOOH=!N*2@#*^ZFd65lMo;VrOJ-3~%ToWkP`jF1zqr$1gpYtjm$<0xHG;zZM z41JE?rLW?8qYcC^yFGDEsz&U3u`j)=J7EwK?pjGbD?WV#l@A+cr3z)HQeU^<#l+Y? z83vi*2yFRRvr{knUi6z4wRpkx{Bko}zj<9_C`Bl<74CpCUyj%z`yJJ;uFFy3Sx93n z!EI6w?8KE2M@^r|^$bJ!ZueAwv+R3o43$Q@c59@yIB<}S(QgCS`Ah+`4pKN*C3`>G z0X8>G&wvTT=3%QIDWs|SP*p$BhI*1@^CH-6} z8sRvjnX=J@Zeneb(+ySPWJmCLTJss|d8A#+48_@t=YLJ&c;0kzj4P5zIS<^Ydb;0CV@8DJs>ST3f;!oz4Qq#njP8&mm z4|j#EhrP~gN6cL2($%IhK9#u#qYoY{?dYOrGj2g+A}O6SW-+iX4O`;o_kqBt3O?#Z zy{|zO%#%O^lPe|j2@k}h@-3jD)Eb66(Rs>kxy;_2fAg?}U|V56H2x`~b>r`_QP_da$rUj{+@SF>ERH?Ar$xD$G(q!Iu3& z=AhSa?sDtyU3N5nnF?^=HMz#d(oZh_^?y;GZ^^YCx=uUhb z*rJtC_V9yegYZ@iTzB(ns?pDdzih%yBL3cf8-4BqxkJ_++E*{}i9*;dVIpiD1mvdE z7c&Gv=0mgY!jG2}o8T!fF{&x$yNAduRwusRC`s&?xnPMca21^r2;Bl6PuVQ>DsI!# znEg$zz1fjM`hf1?8vwSUP?n3h=8o#pPa$u3W)0G_ZgYb!n{ZWFLE1pI%<$1kyQGe%XP&cIJ^jLqS-rA;12@yr8SeWpszx|iGk7$a@z0+K>dJOmfhpA^% zp`zMER+wYtqd7%o@3dATQi(Z<_=sxZB7EJih4VkW(?%qFfF-MZ1NfD~P3uoAnWu1Q=4FIwVrhUItE9sJ{Sg>VWYLoBNZiP&N0vi{VmU zlo6L93Ggo*x&0_h+v`^S;Zz!yC`rNTB(T^K%_YPMq-) zLHj8>d8J~H_}h~|L5QoC+jNmv$*&xR0`YM68=;J;7$Ad_T{PAmg|^g1?JA<;o3aAwlK%8M1$Hce;#> n8<&_P_`=$s9S@Xh?bL)CyiFcD^mvAzRDm-T{+8Av+L!(ZB@#`e literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm new file mode 100644 index 0000000000000000000000000000000000000000..da28419fd279dac8ad18145dc9a1385df41ce7c0 GIT binary patch literal 934 zcmV;X16lk50HOnsz-kZ(M1mp+97qUo5J?0QMUkjTDKZEx3I&0PKp`+d(7=O31fa+R zBLalLU{HW~;-FFtKon{cAQ;53p}~MeF;oZ^A}JDzNH~-MaL6i9ff$3v6Cr_7BpBKt z;9!voNZe?YD9})NluS(mB`Si%xCF7F4h91dz|kNai6n@6FalXYDpCm~B=#VYkV6c@ z1rwOWs1giFWN;_4kYJz^oJ@jCN(Kq-AyP~fLQw#qz-U4tg1{pOvVuV(GMJSH2|}bq zFbA=KKygr+Bt|k1875I^FcaA=&O*l;4ImyzvaDb{pZE-lg#gGJ#(^Z2Kuu!+{(tH6 zD1?(B#Suuo#z27>`~v+Vkw_vCi4H*aoQNC);R4V=HWdg16i;X16ORH4C=-~%G(eF( zl)wrw2pp1;B!IMqqBy8Tk}?k{H4qLl3LJuxe-qflLFJ63LM)mXlRQWx37|p2D$)hA@jDQ|FpU6CaMgfJ$u_5tN3?zy_5P3yl5jYkGK>(#mBQ7Dh z1IFYZ7?^x%FbWg`Bn%(KA_3%xz#`Bn5Ugrc2o#0@k1xQeV$eol&@zyK_`xOuAr_1R zO$CWkF#z$ALZCk-aYG;wWw_+90Pz$FFjwXe7zBeQ0NN=9sxtt3z`^ARh{-8f2*5od z$FVFH0*M604#WU->J5l@XG35XnzfWlGC6#_vJpg<@v2vclh5FmMFi(|1VjRFZo z2Z)3~12i>^kU%mF4k8H$;0uTzSc*WHWEltq67M+R1Io!1h|)9=C_n^YnCL&?9&&(q z*su_c6e3V>8TdT7hq1&_Gy(C5$|oKN=!4DxtPqgNA`%~`{vHo3B?>_iqF6G3L6G=A z@GyCz#vz2H90ns0uz$_x1O*HR6F|sHBvmT~@qTba*ubz92!sX&5VJGV2zeP6BY^^- Iq{he(P&v{|ZU6uP literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Cry_128x64/meta.txt b/assets/resources/dolphin/L1_Cry_128x64/meta.txt new file mode 100644 index 00000000000..1b7d13dd88e --- /dev/null +++ b/assets/resources/dolphin/L1_Cry_128x64/meta.txt @@ -0,0 +1,41 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 8 +Active frames: 4 +Frames order: 0 1 2 3 4 2 3 4 5 6 7 6 +Active cycles: 2 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 2 + +Slot: 0 +X: 22 +Y: 40 +Text: I miss you +AlignH: Right +AlignV: Bottom +StartFrame: 9 +EndFrame: 11 + +Slot: 0 +X: 17 +Y: 40 +Text: my friend... +AlignH: Right +AlignV: Bottom +StartFrame: 12 +EndFrame: 15 + +Slot: 1 +X: 2 +Y: 29 +Text: Why are you\nalways away? +AlignH: Right +AlignV: Bottom +StartFrame: 9 +EndFrame: 15 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm new file mode 100644 index 0000000000000000000000000000000000000000..1169e42d690a46a34ea8a8e1910843a4d46556f7 GIT binary patch literal 653 zcmV;80&@KU0Eq&sfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yb{}+u*N)JgmeE-+*c^7!R>aI*NP#nfa zF(wC$mqm)zTV-@8L^=f!%SJo^@q+8H!=d$4Cm0AF!a)K5K>PvoMU6v|e}P~mei{5T n@fo3FwjuGSOfUt3o`2wf3;BI$91Hjmd=v5T$N$Im3ZMYM>|;S8 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm new file mode 100644 index 0000000000000000000000000000000000000000..80e2f39bb69255c5c17ab52708c536680e2ad7dd GIT binary patch literal 653 zcmV;80&@KU0Eq&sfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yb{}+u*N)JgmeE-+*c^7!R>aI*NP#nfa zF(wC$mqm)zTV-@8L^=f!%SJo^@q+8H!=d$4Cm0AF!a)K5K>PvoMU6v|e}P~mei{5T n@fo3FwjuGSOfUt3o`2wf3;BI$91Hjmd=v5T$N$Im3ZMYMr(i() literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm new file mode 100644 index 0000000000000000000000000000000000000000..959b02556d64ec11a58378b58d291bb1843394bc GIT binary patch literal 650 zcmV;50(JcX0EPmpfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9I8tTOt4|-EOM`51*@hD*gwD)E^yosFHfQ?UsAw z4vf@3HD0M)>au+-zsMgMczi$u0GJ2LOJ$V#D*aiYeIdZ{tIvG8pC+?aJP)4$fLh-v z`4{3J8m#|-VH(IigXaPM2nSRjtRMVdH7zJTB;oV_U%}&~-m{WSA1mxP{|A6sG@$hl zmm3cQk3cyM<&%fUs~@v?Bk&4D9cpq9z zTd5g>ScBRwA1rvhLGeEilzS)mg@eSLU;*HN|70=9Mk3&WjMK2%nMbO*FvUP~85qQv z9x`1PD^+ck(4i3M6hkc-@B_vRuEP$8)l8gVAa@A_2mJ%^2hA2W4n_V2fRXrT@Xy3% kg^JjR#+@+076y6$f&4G!^`LMs;6dfevkzfcae_rL=k*0*)>*ndAk^n*Y(dar-io8a%~B(-0te4sE2rB+|<=li$$$v(C; z8XZ&u0jSkWzPz7T!#O0X!)n3t2aP~JnPB-swVJ6=Jahr`@MHt$c&g1o<3Os*zr2w7 zNeAPE)y9G2zz>g1u%NAwcpEpQt{2SOsDm0KNy*9$Ma116V&C z5`ahV{eV@YqVa$S%pQ8(Fla;L|A6ZM|KIok>b2YrR6aNO3XE8S06k{QgR1R7FQNIb z*k8ggDz2&WEs_G3%T#|NAiasQKmo1R>abJzm&et;m45@n>JN^)RRRy3_RBr;2S#ci z5FnOLt0&US{DJY8hr|FTbJfXgvY#bit27UnRsk>vjb3}@)cG}p3LB^1iZ04*;@hLFykaHXa8a zfN~qlCl8c<&ESu~DG+rj$Ugz{@x~*`0mw-N2QP%x1LFjKjldo(&ipZfTGpwzy^3J*XBuiQR1t92tW1Dpfe zE*~DQI9?^O2ekbDQT(6bUNi!6fCqv9{gB5Y7>9xeGfu;5Wge>J!xaI{WMdOxc*%5F ztyQ*HLWD!0Q4F+Wzz-NMy9_!XRWfmaf!rhzAM_8vA2e9hIT!dA0!QJW!#@$47As;O g8g#<|SQ+R32k^g_)`7skfd|1q9}Iu|e^9Cb3>pbCT>t<8 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm new file mode 100644 index 0000000000000000000000000000000000000000..389d2a8ef52e63d603a3d5c6357d14886acd4518 GIT binary patch literal 650 zcmV;50(JcX0EPmpfICzG;QxpGKjHrm_fevkzfcae_rL=k*0*)>*ndAk^n*Y(dar-io8a%~B(-0te4sE2rB+|<=li$$$v(C; z8XZ&u0jSkWzPz7T!#O0X!)n3t2aP~JnPB-swVJ6=Jahr`@MHt$c&g1o<3Os*zr2w7 zNeAPE)y9G2zz>g1u%NAwcpEpQt{2SOsDm0KNy*9$Ma116V&C z5`ahV{eV@Yun7DFK4A0K>4QQa8Tw_B>gPvZcp`nR&L;COvO@z-jgL~_;7Y_s1W zbY`LP0tsa5vVAPS$R8Pad_V$cJzSQ{De_hNvq1TEU=sj%)#tulPm@`yo(Iw#4;^om z{EP7qmjHlOkb4Ktf53sjH9`3=kMKY>Ehs%C;q$?N#p9&jvyx07E9)d+@CzoC9-;E% zVc>D-2O+$&aQR2<-U$2xkq1(ogYX|O9AZ3>9E6ZSa`;V9J}^)G2H+1FJCr~JDEx=U zRS1$yJ_3=Fhtje?8Lgs8a6k-ze!GDAVJRPv6b?XMPyKv8P-WpHj6=Z#8K+^jGLKbqVTyp}GBJs; zJY>2oR;t@8p+X_hD27@w;0KHsU4|VGs+l;zK<*L<5Bdk-51K4$9Ep%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m>7Kj0WfvJYVSXkb6V0P2JB)$m~d;_<0zLFp$CmV^I)2ac0^&PgzQr;zXb z9sy+1gVa7;d@K$<0OU88PG2bfyTKoTQXuP7kbVQ@;*3X>1CWvk4qpkXH;exPxC6#d z1rL;?@*gNPl41N5jFdi=k@;Czb>0qge< zm8y}LC5Szu;qu0d#2*vz`9`vTh*&&H#sD4%|Mo*1gkmlT7|lBksg!!FlMGY`A(4zp zf#W67VzpM;T?!EnfkZOVj{rPiy6iCMeN@TD0taxAK!4Cb0DSdQs)r)~0>DW8Gx%rX mGeX5|L*q`EU<(60|G@qi^7_y?7w{nXC*$Fd|BvbwKmmYdgFQk3 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm new file mode 100644 index 0000000000000000000000000000000000000000..451c80a26af36c0bfe1cdbfa307f6e6cbb98b292 GIT binary patch literal 646 zcmV;10(t!b0D=OlfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yat{4SNN)JgmeE-lec?1niA#9RT5*kRE6sgsNZ4&fkx|Db*V`Rb)r4n_V2fRXrT@Xy3%g^JjR g#+@+076y6$f&4G!^`LMs;6d%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRaqeZ4{R+MdKdPyqS^%UjA#h*vf#{r`Xtt6jj=d$0~X2CD#S2mtk)E(`czAMpIw>?$FE ze~06$e2Zj20tFqGsto|X5e4iuR>*(~w_B>f%jfFe%D;i(^#{jYswAGSdu5*a1EVz$ zjaRByI;@{dFY*V*ULOzuz$O9mlG$ZGO21ZUA4qUKYV+SNr^&3j(cAjY~=oNjQA}*YJ4hH>~872g>^m|H0rEO(;D><;KIn zc{Nf2>b$(2U?ti@E;pjk})1o4njyEIeaFmTbjZ@@Ed?UWbjb=N5PVO=IkGrcvsyOfgU##zrwF2aK0R ziq%_XbSOkR1rW#)P2^;0Jp2pz&f0slb!0rN$TLy>=hU?hGS{4?%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRaqeZ4{R+MdKdPyqS^%UjA#h*vf#{r`Xtt6ji|i;n@Sz#4)8J!Z>;s*k`w;rXxFTgCn# zj;Zo3kpKu3c1_~G5e4iuR>*(~w_6AKa@I`*nv(c0QLKy%H2rJ62u|N9}1LNONv3}&5%)XF_o$%ZNen8?N?!10pkv0AHa zu7wDPK%yCF$ABI%U3M6BKB{En0Ry;5AV26IfIeuksB$mxECi3kKZbrIG%QxcJ~Ziu d0I)O9{14%OFRcTCe*zDJem)rg`2L|(02rJOIJW=* literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/meta.txt b/assets/resources/dolphin/L1_Read_books_128x64/meta.txt new file mode 100644 index 00000000000..7432507ce29 --- /dev/null +++ b/assets/resources/dolphin/L1_Read_books_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 13 +Active frames: 2 +Frames order: 0 1 0 2 3 3 4 0 1 5 6 1 1 7 8 +Active cycles: 2 +Frame rate: 2 +Duration: 3600 +Active cooldown: 5 + +Bubble slots: 1 + +Slot: 0 +X: 5 +Y: 28 +Text: Predictable twist +AlignH: Right +AlignV: Bottom +StartFrame: 14 +EndFrame: 16 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm new file mode 100644 index 0000000000000000000000000000000000000000..3ff70a91699daba73b1e15ad953597404f21fd73 GIT binary patch literal 543 zcmV+)0^t1t02>1TfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_FVDqCCi~Yd<1JYxS;(zF%kh%paaHZP5cASF948q4a5Hr#4N}<5C6Ua`VWl;A`dt!&`zW8hy%nv!0~`!f5G&n zWP#@b2aE++H9-6W$_4okq)X%vEBp)|gZKxW6XYLBC-@&f|JZ%E;Prrm#$y!ZgW3_| z2bg_v;Pdkb*T@eqyaXQM_5Fj?o!c9`#aq|Apyq2d!Ju$TE2Y z(jhqNLO%fV_)uUL0C{jf^9sN`RpSeXBSap5&;5e&4ERJ~5FvPj&j3dr2R;wr4~z_6 zzW{iR4gwb-0E5U}Ao2K5@DLxxRKm7gw!T;ge!Qv$-0kBDfIJLkx z_$%1iFc1$N{sDOZ4TcAPFBp8ImR@_1@VA>6y~llIsgt14`B1)1DF5+ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm new file mode 100644 index 0000000000000000000000000000000000000000..ed11583f8f8149118f44b8da3aa791c579bffa72 GIT binary patch literal 545 zcmV++0^a=r038DVfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_Fa~y9I|3)bn`+@uipnT|{AI4rM3b1N{@`-$b?E?IV=Kli+p#0>Y;Cn$nLH!4p|Mnjkrz9R*@Or>O;vZ0X!aP9p z508)@SnzrIgY)-*gWNtpka>Q=>Q5m0&q3u#2Z_9g=S2sIIA9u%H@{W-{fEwt|BnO8 zQOom(#2$k2jRVv69}>a9A0rk5lw+0afPK945PDOA>D$$MzhLo|KnLSK2b4ppyMTJV zV)ZYWJcHxgClhpk_ptl!|MU0<`#=A?gU_)$fcmdt^WTHss!t#Ay)EGNt9n@mPat|k zCml#f;2vKJ3250rBO1N>HKun6_R8bk9J3=_fg1CN3p4_|}^4G7=@#fE+W jd6S9=SST1i2jn~unFHUzUl<4D{~rgExRvGrM(KmhGyew= literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm new file mode 100644 index 0000000000000000000000000000000000000000..41850505b54db86c1a371b051d9d4d1a5eee03da GIT binary patch literal 548 zcmV+<0^9uo03ZVYfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_FVDqCCi~Yd<1JYwn{D0`6kh%paaHZL5PFSF948q4a5Hr#4N}<5C6Ua`VWmOBo8<$&`zW8hy%nv!0~`!f5G&L ze1Ycz2aE++H9-6W$_4okq?7y)EBp)|gZKxW6XYLArz9Ug|JZ%E;Prrm#6CVidqO-w z^AD~(9)4i@{z2vUfP>sVzp#3f$bPfXc~Sx5Zy@>6LE;V=2BVGd)qcNw5P4C*@!)w% zIeu{X{>SSY2dC^lC4+!IMl1y=$1B$W`+4Rd^rr*Ux2pAi!Q(1`55{~CD2G#b0QGvs z>R&K<2gkNfCg}g~VfWqt=kO2qfB$y}pJI0b^09*q9fII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;Q#o6ijF!s2lyU;uzAsdWyk-5NFGxhZxjDT2bAUk&Ih0LUhr?^{AM%& zc+6?PfO+5WFg^kBf8qGw&+%42{=M-4c!%2G5C@OYK9sDGJo@mE zc=|o{K>R}C!13q@(k1c-oEH%f2ax_`w+Vy;Kp#mW+(G3Mh*81i|NV#C2NLAw0p$S) zjK(R+2bK3FD9}BG9%1#tVEmF82t5Pi9${8@q z2*e&H@(-OCe-PjU%K`7ze!pPzqi@@zuiwBh9&q@B(4I6H2k;(Wu=xGpEO9&y{{sNv zSHa`!fPK945PARLL1B}F$G58We!=4^fKi*n0)R(>Kwd=sMpFN#Ee^9sN`RpSeXQ-Omt0T1*4V7x;<5f}m}XaK=BksNp&_&&aA;IDQrw8%>hrtZOCMWe~5Kj4f5%sw&91P}4qAq=a*2S$ke#lr;fe8!`HkEck! ua2PZrfe#7~;P5^Gd6S6{KljlC++%42{=M-4c!%2G5C@OYK9sDGJo@mE zc=|o{K>R}C!13q@(k1c-oEH%f2ax_`w+Vy;Kp#mW+(G3Mh*81i|NV#C2NLAw0p$S) zjK(R+2bK3FD9}BG9%1#tVEmF82t5Pi|pA|Cm+*;;$H7I-Cp{;0S-8`vu||@QA=5Lh%L(w20%t=fV5| z@qvrR;14f=JV<50dWnFs;zAb)Jbn}W1PAd|tJPtUFnFE{0S*rj|3Clyf8p>$Fo}u) z1rWH-HGU0LVlXyL1Ovx^fL=erp^x|@0P_!wa{&YVc1S}i@Ild{KQVB@JRdQr-{a}h vFPsJq2;f7)gZMlTfF5MxL=XLTK=&V%0zC)^&w#!n!JmW8TuSo*9{?WUP=735 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt b/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt new file mode 100644 index 00000000000..8ad8d42a345 --- /dev/null +++ b/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt @@ -0,0 +1,32 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 3 +Active frames: 2 +Frames order: 0 1 2 3 4 +Active cycles: 4 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 22 +Y: 25 +Text: Mess with\nthe best, +AlignH: Right +AlignV: Center +StartFrame: 4 +EndFrame: 7 + +Slot: 0 +X: 31 +Y: 25 +Text: die like\nthe rest. +AlignH: Right +AlignV: Center +StartFrame: 8 +EndFrame: 10 diff --git a/assets/resources/dolphin/manifest.txt b/assets/resources/dolphin/manifest.txt index 8be65c11ea0..d6e16dd13c1 100644 --- a/assets/resources/dolphin/manifest.txt +++ b/assets/resources/dolphin/manifest.txt @@ -49,3 +49,31 @@ Max butthurt: 6 Min level: 3 Max level: 3 Weight: 3 + +Name: L1_Read_books_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 1 +Max level: 1 +Weight: 3 + +Name: L2_Hacking_pc_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L1_Cry_128x64 +Min butthurt: 8 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Boxing_128x64 +Min butthurt: 10 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 3