diff --git a/applications/applications.h b/applications/applications.h index c2615166..8395d52b 100644 --- a/applications/applications.h +++ b/applications/applications.h @@ -30,7 +30,7 @@ void app_loader(void* p); void cc1101_workaround(void* p); void lf_rfid_workaround(void* p); void nfc_task(void* p); -void irukagotchi_task(void* p); +void dolphin_task(void* p); void power_task(void* p); void bt_task(void* p); void sd_card_test(void* p); @@ -88,9 +88,9 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { .icon = A_Plugins_14}, #endif -#ifdef APP_IRUKAGOTCHI - {.app = irukagotchi_task, - .name = "irukagotchi_task", +#ifdef APP_DOLPHIN + {.app = dolphin_task, + .name = "dolphin_task", .libs = {1, FURI_LIB{"menu_task"}}, .icon = A_Plugins_14}, #endif diff --git a/applications/applications.mk b/applications/applications.mk index 851f2099..83ce746c 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -14,7 +14,7 @@ APP_POWER = 1 APP_BT = 1 APP_CLI = 1 BUILD_IRDA = 1 -APP_IRUKAGOTCHI = 1 +APP_DOLPHIN = 1 BUILD_EXAMPLE_BLINK = 1 BUILD_EXAMPLE_UART_WRITE = 1 BUILD_EXAMPLE_INPUT_DUMP = 1 @@ -36,11 +36,11 @@ CFLAGS += -DAPP_NFC C_SOURCES += $(wildcard $(APP_DIR)/nfc/*.c) endif -APP_IRUKAGOTCHI ?= 0 -ifeq ($(APP_IRUKAGOTCHI), 1) +APP_DOLPHIN ?= 0 +ifeq ($(APP_DOLPHIN), 1) APP_MENU = 1 -CFLAGS += -DAPP_IRUKAGOTCHI -C_SOURCES += $(wildcard $(APP_DIR)/irukagotchi/*.c) +CFLAGS += -DAPP_DOLPHIN +C_SOURCES += $(wildcard $(APP_DIR)/dolphin/*.c) endif APP_POWER ?= 0 diff --git a/applications/dolphin/dolphin.c b/applications/dolphin/dolphin.c new file mode 100644 index 00000000..8edcd826 --- /dev/null +++ b/applications/dolphin/dolphin.c @@ -0,0 +1,124 @@ +#include "dolphin_i.h" + +void dolphin_draw_callback(Canvas* canvas, void* context) { + Dolphin* dolphin = context; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + if(dolphin->screen == DolphinScreenIdle) { + dolphin_draw_idle(canvas, dolphin); + } else if(dolphin->screen == DolphinScreenDebug) { + dolphin_draw_debug(canvas, dolphin); + } else if(dolphin->screen == DolphinScreenStats) { + dolphin_draw_stats(canvas, dolphin); + } +} + +void dolphin_draw_idle(Canvas* canvas, Dolphin* dolphin) { + canvas_draw_icon(canvas, 128 - 80, 0, dolphin->icon); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 10, "/\\: Stats"); + canvas_draw_str(canvas, 5, 32, "OK: Menu"); + canvas_draw_str(canvas, 2, 52, "\\/: Version"); +} + +void dolphin_draw_debug(Canvas* canvas, Dolphin* dolphin) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Version info:"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 5, 22, TARGET " " BUILD_DATE); + canvas_draw_str(canvas, 5, 32, GIT_BRANCH); + canvas_draw_str(canvas, 5, 42, GIT_BRANCH_NUM); + canvas_draw_str(canvas, 5, 52, GIT_COMMIT); +} + +void dolphin_draw_stats(Canvas* canvas, Dolphin* dolphin) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Dolphin stats:"); + + char buffer[64]; + canvas_set_font(canvas, FontSecondary); + snprintf(buffer, 64, "Icounter: %ld", dolphin_state_get_icounter(dolphin->state)); + canvas_draw_str(canvas, 5, 22, buffer); + snprintf(buffer, 64, "Butthurt: %ld", dolphin_state_get_butthurt(dolphin->state)); + canvas_draw_str(canvas, 5, 32, buffer); + canvas_draw_str(canvas, 5, 40, "< > change icounter"); +} + +void dolphin_input_callback(InputEvent* event, void* context) { + Dolphin* dolphin = context; + + if(!event->state) return; + + if(event->input == InputOk) { + with_value_mutex( + dolphin->menu_vm, (Menu * menu) { menu_ok(menu); }); + } else if(event->input == InputUp) { + if(dolphin->screen != DolphinScreenStats) { + dolphin->screen++; + } + } else if(event->input == InputDown) { + if(dolphin->screen != DolphinScreenDebug) { + dolphin->screen--; + } + } else if(event->input == InputBack) { + dolphin->screen = DolphinScreenIdle; + } else if(event->input == InputLeft) { + dolphin_deed(dolphin, DolphinDeedIButtonEmulate); + } else if(event->input == InputRight) { + dolphin_deed(dolphin, DolphinDeedWrong); + } + + widget_update(dolphin->widget); +} + +Dolphin* dolphin_alloc() { + Dolphin* dolphin = furi_alloc(sizeof(Dolphin)); + + dolphin->icon = assets_icons_get(I_Flipper_young_80x60); + icon_start_animation(dolphin->icon); + + dolphin->widget = widget_alloc(); + widget_draw_callback_set(dolphin->widget, dolphin_draw_callback, dolphin); + widget_input_callback_set(dolphin->widget, dolphin_input_callback, dolphin); + + dolphin->menu_vm = furi_open("menu"); + furi_check(dolphin->menu_vm); + + dolphin->state = dolphin_state_alloc(); + + dolphin->screen = DolphinScreenIdle; + + dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL); + furi_check(dolphin->event_queue); + return dolphin; +} + +void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { + DolphinEvent event; + event.type = DolphinEventTypeDeed; + event.deed = deed; + furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK); +} + +void dolphin_task() { + Dolphin* dolphin = dolphin_alloc(); + + Gui* gui = furi_open("gui"); + gui_add_widget(gui, dolphin->widget, GuiLayerNone); + + if(!furi_create("dolphin", dolphin)) { + printf("[dolphin_task] cannot create the dolphin record\n"); + furiac_exit(NULL); + } + + furiac_ready(); + + DolphinEvent event; + while(1) { + furi_check(osMessageQueueGet(dolphin->event_queue, &event, NULL, osWaitForever) == osOK); + if(event.type == DolphinEventTypeDeed) { + dolphin_state_on_deed(dolphin->state, event.deed); + } + } +} diff --git a/applications/dolphin/dolphin.h b/applications/dolphin/dolphin.h new file mode 100644 index 00000000..38ca1e90 --- /dev/null +++ b/applications/dolphin/dolphin.h @@ -0,0 +1,11 @@ +#pragma once + +#include "dolphin_deed.h" + +typedef struct Dolphin Dolphin; + +/* + * Deed complete notification. Call it on deed completion. + * See dolphin_deed.h for available deeds. In futures it will become part of assets. + */ +void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); diff --git a/applications/dolphin/dolphin_deed.c b/applications/dolphin/dolphin_deed.c new file mode 100644 index 00000000..d589ac23 --- /dev/null +++ b/applications/dolphin/dolphin_deed.c @@ -0,0 +1,12 @@ +#include "dolphin_deed.h" + +static const DolphinDeedWeight dolphin_deed_weights[DolphinDeedMax] = { + {1, 2, 60}, + {1, 2, 60}, + {1, 2, 60}, + {-1, 2, 60}, +}; + +const DolphinDeedWeight* dolphin_deed_weight(DolphinDeed deed) { + return &dolphin_deed_weights[deed]; +} diff --git a/applications/dolphin/dolphin_deed.h b/applications/dolphin/dolphin_deed.h new file mode 100644 index 00000000..2af1829a --- /dev/null +++ b/applications/dolphin/dolphin_deed.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +/* Countable deed that affects icounter*/ +typedef enum { + // iButton + DolphinDeedIButtonRead, + DolphinDeedIButtonWrite, + DolphinDeedIButtonEmulate, + // for debug + DolphinDeedWrong, + // Special value, do not use + DolphinDeedMax +} DolphinDeed; + +typedef struct { + int32_t icounter; // how many icounter get by Deed + uint32_t limit_value; // how many deeds in limit interval + uint32_t limit_interval; // interval, in minutes +} DolphinDeedWeight; + +const DolphinDeedWeight* dolphin_deed_weight(DolphinDeed deed); diff --git a/applications/dolphin/dolphin_i.h b/applications/dolphin/dolphin_i.h new file mode 100644 index 00000000..ab7e0365 --- /dev/null +++ b/applications/dolphin/dolphin_i.h @@ -0,0 +1,51 @@ +#pragma once + +#include "dolphin.h" +#include "dolphin_state.h" + +#include + +#include +#include +#include +#include + +#include + +#include + +typedef enum { + DolphinEventTypeDeed, +} DolphinEventType; + +typedef struct { + DolphinEventType type; + union { + DolphinDeed deed; + }; +} DolphinEvent; + +typedef enum { + DolphinScreenDebug, + DolphinScreenIdle, + DolphinScreenStats, +} DolphinScreen; + +struct Dolphin { + Icon* icon; + Widget* widget; + ValueMutex* menu_vm; + // State + DolphinState* state; + DolphinScreen screen; + // Internal message queue + osMessageQueueId_t event_queue; +}; + +void dolphin_draw_callback(Canvas* canvas, void* context); +void dolphin_draw_idle(Canvas* canvas, Dolphin* dolphin); +void dolphin_draw_debug(Canvas* canvas, Dolphin* dolphin); +void dolphin_draw_stats(Canvas* canvas, Dolphin* dolphin); +void dolphin_input_callback(InputEvent* event, void* context); + +Dolphin* dolphin_alloc(); diff --git a/applications/dolphin/dolphin_state.c b/applications/dolphin/dolphin_state.c new file mode 100644 index 00000000..dcb9fddc --- /dev/null +++ b/applications/dolphin/dolphin_state.c @@ -0,0 +1,52 @@ +#include "dolphin_state.h" +#include + +typedef struct { + uint32_t ibutton; + uint32_t nfc; + uint32_t ir; + uint32_t rfid; +} DolphinLimit; + +struct DolphinState { + uint32_t icounter; + uint32_t butthurt; + + DolphinLimit limit; +}; + +DolphinState* dolphin_state_alloc() { + DolphinState* dolphin_state = furi_alloc(sizeof(DolphinState)); + return dolphin_state; +} + +void dolphin_state_release(DolphinState* dolphin_state) { + free(dolphin_state); +} + +void dolphin_state_save(DolphinState* dolphin_state) { +} + +void dolphin_state_load(DolphinState* dolphin_state) { +} + +void dolphin_state_clear(DolphinState* dolphin_state) { + memset(dolphin_state, 0, sizeof(DolphinState)); +} + +void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { + const DolphinDeedWeight* deed_weight = dolphin_deed_weight(deed); + int32_t icounter = dolphin_state->icounter + deed_weight->icounter; + + if(icounter >= 0) { + dolphin_state->icounter = icounter; + } +} + +uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state) { + return dolphin_state->icounter; +} + +uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state) { + return dolphin_state->butthurt; +} diff --git a/applications/dolphin/dolphin_state.h b/applications/dolphin/dolphin_state.h new file mode 100644 index 00000000..c3320b84 --- /dev/null +++ b/applications/dolphin/dolphin_state.h @@ -0,0 +1,22 @@ +#pragma once + +#include "dolphin_deed.h" +#include + +typedef struct DolphinState DolphinState; + +DolphinState* dolphin_state_alloc(); + +void dolphin_state_release(DolphinState* dolphin_state); + +void dolphin_state_save(DolphinState* dolphin_state); + +void dolphin_state_load(DolphinState* dolphin_state); + +void dolphin_state_clear(DolphinState* dolphin_state); + +void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed); + +uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state); + +uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state); diff --git a/applications/irukagotchi/irukagotchi.c b/applications/irukagotchi/irukagotchi.c deleted file mode 100644 index bafa9049..00000000 --- a/applications/irukagotchi/irukagotchi.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "irukagotchi.h" - -#include - -#include -#include -#include -#include - -#include - -struct Irukagotchi { - Icon* icon; - Widget* widget; - ValueMutex* menu_vm; -}; - -void irukagotchi_draw_callback(Canvas* canvas, void* context) { - Irukagotchi* irukagotchi = context; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_draw_icon(canvas, 128 - 80, 0, irukagotchi->icon); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 10, TARGET " " BUILD_DATE); - canvas_draw_str(canvas, 2, 22, GIT_BRANCH); - canvas_draw_str(canvas, 2, 34, GIT_BRANCH_NUM); - canvas_draw_str(canvas, 2, 46, GIT_COMMIT); -} - -void irukagotchi_input_callback(InputEvent* event, void* context) { - Irukagotchi* irukagotchi = context; - - if(!event->state || event->input != InputOk) return; - - with_value_mutex( - irukagotchi->menu_vm, (Menu * menu) { menu_ok(menu); }); -} - -Irukagotchi* irukagotchi_alloc() { - Irukagotchi* irukagotchi = furi_alloc(sizeof(Irukagotchi)); - - irukagotchi->icon = assets_icons_get(I_Flipper_young_80x60); - icon_start_animation(irukagotchi->icon); - - irukagotchi->widget = widget_alloc(); - widget_draw_callback_set(irukagotchi->widget, irukagotchi_draw_callback, irukagotchi); - widget_input_callback_set(irukagotchi->widget, irukagotchi_input_callback, irukagotchi); - - irukagotchi->menu_vm = furi_open("menu"); - furi_check(irukagotchi->menu_vm); - - return irukagotchi; -} - -void irukagotchi_task() { - Irukagotchi* irukagotchi = irukagotchi_alloc(); - - Gui* gui = furi_open("gui"); - gui_add_widget(gui, irukagotchi->widget, GuiLayerNone); - - furiac_ready(); - - while(1) { - osDelay(osWaitForever); - } -} \ No newline at end of file diff --git a/applications/irukagotchi/irukagotchi.h b/applications/irukagotchi/irukagotchi.h deleted file mode 100644 index 2a1f6eb6..00000000 --- a/applications/irukagotchi/irukagotchi.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -typedef struct Irukagotchi Irukagotchi; diff --git a/assets/icons/IrukaGotchi/Flipper_young_80x60.png b/assets/icons/Dolphin/Flipper_young_80x60.png similarity index 100% rename from assets/icons/IrukaGotchi/Flipper_young_80x60.png rename to assets/icons/Dolphin/Flipper_young_80x60.png diff --git a/firmware/targets/api-hal-include/api-hal-bt.h b/firmware/targets/api-hal-include/api-hal-bt.h index d89a3282..47d487bf 100644 --- a/firmware/targets/api-hal-include/api-hal-bt.h +++ b/firmware/targets/api-hal-include/api-hal-bt.h @@ -16,6 +16,12 @@ void api_hal_bt_dump_state(string_t buffer); /* Get BT/BLE system component state */ bool api_hal_bt_is_alive(); +/* Lock shared access to flash controller */ +void api_hal_bt_lock_flash(); + +/* Unlock shared access to flash controller */ +void api_hal_bt_unlock_flash(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/api-hal-include/api-hal.h b/firmware/targets/api-hal-include/api-hal.h index c482a07a..0fb87811 100644 --- a/firmware/targets/api-hal-include/api-hal.h +++ b/firmware/targets/api-hal-include/api-hal.h @@ -14,3 +14,4 @@ template struct STOP_EXTERNING_ME {}; #include "api-hal-vcp.h" #include "api-hal-uid.h" #include "api-hal-bt.h" +#include "api-hal-flash.h" diff --git a/firmware/targets/f4/api-hal/api-hal-bt.c b/firmware/targets/f4/api-hal/api-hal-bt.c index de0886cc..bb95bcd6 100644 --- a/firmware/targets/f4/api-hal/api-hal-bt.c +++ b/firmware/targets/f4/api-hal/api-hal-bt.c @@ -1,6 +1,9 @@ #include #include #include +#include +#include +#include void api_hal_bt_init() { // Explicitly tell that we are in charge of CLK48 domain @@ -31,7 +34,39 @@ void api_hal_bt_dump_state(string_t buffer) { } } - bool api_hal_bt_is_alive() { return APPE_Status() == BleGlueStatusStarted; -} \ No newline at end of file +} + +bool api_hal_bt_wait_transition() { + if (APPE_Status() == BleGlueStatusUninitialized) { + return false; + } + while (APPE_Status() != BleGlueStatusStarted) { + osDelay(1); + } + return true; +} + +void api_hal_bt_lock_flash() { + if (!api_hal_bt_wait_transition()) { + HAL_FLASH_Unlock(); + return; + } + while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) { + osDelay(1); + } + SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); + HAL_FLASH_Unlock(); + while(LL_FLASH_IsOperationSuspended()) {}; +} + +void api_hal_bt_unlock_flash() { + if (!api_hal_bt_wait_transition()) { + HAL_FLASH_Lock(); + return; + } + SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); + HAL_FLASH_Lock(); + HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID); +} diff --git a/firmware/targets/f4/api-hal/api-hal-flash.c b/firmware/targets/f4/api-hal/api-hal-flash.c new file mode 100644 index 00000000..ef8f0b97 --- /dev/null +++ b/firmware/targets/f4/api-hal/api-hal-flash.c @@ -0,0 +1,15 @@ +#include +#include +#include + +void api_hal_flash_write_dword(size_t address, uint64_t data) { + api_hal_bt_lock_flash(); + HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data); + api_hal_bt_unlock_flash(); +} + +void api_hal_flash_write_row(size_t address, size_t source_address) { + api_hal_bt_lock_flash(); + HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, source_address); + api_hal_bt_unlock_flash(); +} diff --git a/firmware/targets/f4/api-hal/api-hal-flash.h b/firmware/targets/f4/api-hal/api-hal-flash.h new file mode 100644 index 00000000..ce7247c6 --- /dev/null +++ b/firmware/targets/f4/api-hal/api-hal-flash.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +/* + * Write double word (64 bits) + * Locking operation, uses HSEM to manage shared access. + * @param address - destination address, must be double word aligned. + * @param data - data to write + */ +void api_hal_flash_write_dword(size_t address, uint64_t data); + +/* + * Write page (4096 bytes or 64 rows of double words). + * Locking operation, uses HSEM to manage shared access. + * @param address - destination address, must be page aligned + * @param source_address - source address + */ +void api_hal_flash_write_page(size_t address, size_t source_address);