Merge branch 'release-candidate' into release
This commit is contained in:
commit
d3c0618699
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@ -121,11 +121,11 @@ jobs:
|
|||||||
|
|
||||||
- name: 'Bundle core2 firmware'
|
- name: 'Bundle core2 firmware'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
uses: ./.github/actions/docker
|
||||||
|
with:
|
||||||
run: |
|
run: |
|
||||||
test -d core2_firmware && rm -rf core2_firmware || true
|
make -C assets copro_bundle
|
||||||
mkdir core2_firmware
|
tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware
|
||||||
./scripts/assets.py copro lib/STM32CubeWB core2_firmware STM32WB5x
|
|
||||||
tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz core2_firmware
|
|
||||||
|
|
||||||
- name: 'Upload artifacts to update server'
|
- name: 'Upload artifacts to update server'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
@ -161,7 +161,7 @@ jobs:
|
|||||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
body: |
|
body: |
|
||||||
[Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}&target=${{steps.names.outputs.default-target}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB.
|
[Click here](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu) for the DFU file to flash the `${{steps.names.outputs.short-hash}}` version of this branch with the [`Install from file` option in qFlipper](https://docs.flipperzero.one/basics/firmware-update).
|
||||||
edit-mode: replace
|
edit-mode: replace
|
||||||
|
|
||||||
compact:
|
compact:
|
||||||
@ -213,8 +213,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
make -C assets clean
|
make assets_rebuild assets_manifest
|
||||||
make -C assets
|
git diff --quiet || ( echo "Assets recompilation required."; exit 255 )
|
||||||
|
|
||||||
- name: 'Build the firmware in docker'
|
- name: 'Build the firmware in docker'
|
||||||
uses: ./.github/actions/docker
|
uses: ./.github/actions/docker
|
||||||
|
|||||||
17
Makefile
17
Makefile
@ -1,8 +1,7 @@
|
|||||||
PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
|
PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
|
||||||
|
|
||||||
include $(PROJECT_ROOT)/make/git.mk
|
include $(PROJECT_ROOT)/make/git.mk
|
||||||
|
include $(PROJECT_ROOT)/assets/copro.mk
|
||||||
COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x
|
|
||||||
|
|
||||||
PROJECT_SOURCE_DIRECTORIES := \
|
PROJECT_SOURCE_DIRECTORIES := \
|
||||||
$(PROJECT_ROOT)/applications \
|
$(PROJECT_ROOT)/applications \
|
||||||
@ -97,7 +96,13 @@ updater_package_bin: firmware_all updater
|
|||||||
|
|
||||||
.PHONY: updater_package
|
.PHONY: updater_package
|
||||||
updater_package: firmware_all updater assets_manifest
|
updater_package: firmware_all updater assets_manifest
|
||||||
@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) -r $(PROJECT_ROOT)/assets/resources --bundlever "$(VERSION_STRING)"
|
@$(PROJECT_ROOT)/scripts/dist.py copy \
|
||||||
|
-t $(TARGET) -p firmware updater \
|
||||||
|
-s $(DIST_SUFFIX) -r $(PROJECT_ROOT)/assets/resources \
|
||||||
|
--bundlever "$(VERSION_STRING)" \
|
||||||
|
--radio $(COPRO_STACK_BIN_PATH) \
|
||||||
|
--radiotype $(COPRO_STACK_TYPE) \
|
||||||
|
--obdata $(PROJECT_ROOT)/scripts/ob.data
|
||||||
|
|
||||||
.PHONY: assets_manifest
|
.PHONY: assets_manifest
|
||||||
assets_manifest:
|
assets_manifest:
|
||||||
@ -109,7 +114,7 @@ assets_rebuild:
|
|||||||
|
|
||||||
.PHONY: flash_radio
|
.PHONY: flash_radio
|
||||||
flash_radio:
|
flash_radio:
|
||||||
@$(PROJECT_ROOT)/scripts/flash.py core2radio 0x080D7000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_light_fw.bin
|
@$(PROJECT_ROOT)/scripts/flash.py core2radio $(COPRO_STACK_BIN_PATH) --addr=$(COPRO_STACK_ADDR)
|
||||||
@$(PROJECT_ROOT)/scripts/ob.py set
|
@$(PROJECT_ROOT)/scripts/ob.py set
|
||||||
|
|
||||||
.PHONY: flash_radio_fus
|
.PHONY: flash_radio_fus
|
||||||
@ -125,8 +130,8 @@ flash_radio_fus:
|
|||||||
|
|
||||||
.PHONY: flash_radio_fus_please_i_m_not_going_to_complain
|
.PHONY: flash_radio_fus_please_i_m_not_going_to_complain
|
||||||
flash_radio_fus_please_i_m_not_going_to_complain:
|
flash_radio_fus_please_i_m_not_going_to_complain:
|
||||||
@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin
|
@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_FIRMWARE_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin
|
||||||
@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin
|
@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_FIRMWARE_DIR)/stm32wb5x_FUS_fw.bin
|
||||||
@$(PROJECT_ROOT)/scripts/ob.py set
|
@$(PROJECT_ROOT)/scripts/ob.py set
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
|
|||||||
@ -32,7 +32,6 @@ void AccessorApp::run(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AccessorApp::AccessorApp() {
|
AccessorApp::AccessorApp() {
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
notification = static_cast<NotificationApp*>(furi_record_open("notification"));
|
notification = static_cast<NotificationApp*>(furi_record_open("notification"));
|
||||||
onewire_host = onewire_host_alloc();
|
onewire_host = onewire_host_alloc();
|
||||||
furi_hal_power_enable_otg();
|
furi_hal_power_enable_otg();
|
||||||
@ -42,7 +41,6 @@ AccessorApp::~AccessorApp() {
|
|||||||
furi_hal_power_disable_otg();
|
furi_hal_power_disable_otg();
|
||||||
furi_record_close("notification");
|
furi_record_close("notification");
|
||||||
onewire_host_free(onewire_host);
|
onewire_host_free(onewire_host);
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AccessorAppViewManager* AccessorApp::get_view_manager() {
|
AccessorAppViewManager* AccessorApp::get_view_manager() {
|
||||||
|
|||||||
@ -74,61 +74,113 @@ extern int32_t system_settings_app(void* p);
|
|||||||
const FlipperApplication FLIPPER_SERVICES[] = {
|
const FlipperApplication FLIPPER_SERVICES[] = {
|
||||||
/* Services */
|
/* Services */
|
||||||
#ifdef SRV_RPC
|
#ifdef SRV_RPC
|
||||||
{.app = rpc_srv, .name = "RpcSrv", .stack_size = 1024 * 4, .icon = NULL},
|
{.app = rpc_srv,
|
||||||
|
.name = "RpcSrv",
|
||||||
|
.stack_size = 1024 * 4,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_BT
|
#ifdef SRV_BT
|
||||||
{.app = bt_srv, .name = "BtSrv", .stack_size = 1024, .icon = NULL},
|
{.app = bt_srv,
|
||||||
|
.name = "BtSrv",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
{.app = cli_srv, .name = "CliSrv", .stack_size = 4096, .icon = NULL},
|
{.app = cli_srv,
|
||||||
|
.name = "CliSrv",
|
||||||
|
.stack_size = 4096,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_DIALOGS
|
#ifdef SRV_DIALOGS
|
||||||
{.app = dialogs_srv, .name = "DialogsSrv", .stack_size = 1024, .icon = NULL},
|
{.app = dialogs_srv,
|
||||||
|
.name = "DialogsSrv",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_DOLPHIN
|
#ifdef SRV_DOLPHIN
|
||||||
{.app = dolphin_srv, .name = "DolphinSrv", .stack_size = 1024, .icon = NULL},
|
{.app = dolphin_srv,
|
||||||
|
.name = "DolphinSrv",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_DESKTOP
|
#ifdef SRV_DESKTOP
|
||||||
#ifdef SRV_UPDATER
|
#ifdef SRV_UPDATER
|
||||||
#error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
|
#error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
|
||||||
#endif
|
#endif
|
||||||
{.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL},
|
{.app = desktop_srv,
|
||||||
|
.name = "DesktopSrv",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_GUI
|
#ifdef SRV_GUI
|
||||||
{.app = gui_srv, .name = "GuiSrv", .stack_size = 2048, .icon = NULL},
|
{.app = gui_srv,
|
||||||
|
.name = "GuiSrv",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_INPUT
|
#ifdef SRV_INPUT
|
||||||
{.app = input_srv, .name = "InputSrv", .stack_size = 1024, .icon = NULL},
|
{.app = input_srv,
|
||||||
|
.name = "InputSrv",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_LOADER
|
#ifdef SRV_LOADER
|
||||||
{.app = loader_srv, .name = "LoaderSrv", .stack_size = 1024, .icon = NULL},
|
{.app = loader_srv,
|
||||||
|
.name = "LoaderSrv",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_NOTIFICATION
|
#ifdef SRV_NOTIFICATION
|
||||||
{.app = notification_srv, .name = "NotificationSrv", .stack_size = 1536, .icon = NULL},
|
{.app = notification_srv,
|
||||||
|
.name = "NotificationSrv",
|
||||||
|
.stack_size = 1536,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_POWER
|
#ifdef SRV_POWER
|
||||||
{.app = power_srv, .name = "PowerSrv", .stack_size = 1024, .icon = NULL},
|
{.app = power_srv,
|
||||||
|
.name = "PowerSrv",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_STORAGE
|
#ifdef SRV_STORAGE
|
||||||
{.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL},
|
{.app = storage_srv,
|
||||||
|
.name = "StorageSrv",
|
||||||
|
.stack_size = 3072,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_UPDATER
|
#ifdef SRV_UPDATER
|
||||||
#ifdef SRV_DESKTOP
|
#ifdef SRV_DESKTOP
|
||||||
#error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
|
#error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
|
||||||
#endif
|
#endif
|
||||||
{.app = updater_srv, .name = "UpdaterSrv", .stack_size = 2048, .icon = NULL},
|
{.app = updater_srv,
|
||||||
|
.name = "UpdaterSrv",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -139,7 +191,11 @@ const FlipperApplication FLIPPER_SYSTEM_APPS[] = {
|
|||||||
#ifdef SRV_UPDATER
|
#ifdef SRV_UPDATER
|
||||||
#error APP_UPDATER and SRV_UPDATER are mutually exclusive!
|
#error APP_UPDATER and SRV_UPDATER are mutually exclusive!
|
||||||
#endif
|
#endif
|
||||||
{.app = updater_srv, .name = "UpdaterApp", .stack_size = 2048, .icon = NULL},
|
{.app = updater_srv,
|
||||||
|
.name = "UpdaterApp",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -149,35 +205,67 @@ const size_t FLIPPER_SYSTEM_APPS_COUNT = COUNT_OF(FLIPPER_SYSTEM_APPS);
|
|||||||
const FlipperApplication FLIPPER_APPS[] = {
|
const FlipperApplication FLIPPER_APPS[] = {
|
||||||
|
|
||||||
#ifdef APP_SUBGHZ
|
#ifdef APP_SUBGHZ
|
||||||
{.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14},
|
{.app = subghz_app,
|
||||||
|
.name = "Sub-GHz",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = &A_Sub1ghz_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_LF_RFID
|
#ifdef APP_LF_RFID
|
||||||
{.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14},
|
{.app = lfrfid_app,
|
||||||
|
.name = "125 kHz RFID",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = &A_125khz_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_NFC
|
#ifdef APP_NFC
|
||||||
{.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14},
|
{.app = nfc_app,
|
||||||
|
.name = "NFC",
|
||||||
|
.stack_size = 4096,
|
||||||
|
.icon = &A_NFC_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_INFRARED
|
#ifdef APP_INFRARED
|
||||||
{.app = infrared_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14},
|
{.app = infrared_app,
|
||||||
|
.name = "Infrared",
|
||||||
|
.stack_size = 1024 * 3,
|
||||||
|
.icon = &A_Infrared_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_GPIO
|
#ifdef APP_GPIO
|
||||||
{.app = gpio_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14},
|
{.app = gpio_app,
|
||||||
|
.name = "GPIO",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = &A_GPIO_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_IBUTTON
|
#ifdef APP_IBUTTON
|
||||||
{.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
|
{.app = ibutton_app,
|
||||||
|
.name = "iButton",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = &A_iButton_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_BAD_USB
|
#ifdef APP_BAD_USB
|
||||||
{.app = bad_usb_app, .name = "Bad USB", .stack_size = 2048, .icon = &A_BadUsb_14},
|
{.app = bad_usb_app,
|
||||||
|
.name = "Bad USB",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = &A_BadUsb_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_U2F
|
#ifdef APP_U2F
|
||||||
{.app = u2f_app, .name = "U2F", .stack_size = 2048, .icon = &A_U2F_14},
|
{.app = u2f_app,
|
||||||
|
.name = "U2F",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = &A_U2F_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -234,15 +322,27 @@ const size_t FLIPPER_ON_SYSTEM_START_COUNT = COUNT_OF(FLIPPER_ON_SYSTEM_START);
|
|||||||
// Plugin menu
|
// Plugin menu
|
||||||
const FlipperApplication FLIPPER_PLUGINS[] = {
|
const FlipperApplication FLIPPER_PLUGINS[] = {
|
||||||
#ifdef APP_BLE_HID
|
#ifdef APP_BLE_HID
|
||||||
{.app = bt_hid_app, .name = "Bluetooth Remote", .stack_size = 1024, .icon = NULL},
|
{.app = bt_hid_app,
|
||||||
|
.name = "Bluetooth Remote",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_MUSIC_PLAYER
|
#ifdef APP_MUSIC_PLAYER
|
||||||
{.app = music_player_app, .name = "Music Player", .stack_size = 1024, .icon = &A_Plugins_14},
|
{.app = music_player_app,
|
||||||
|
.name = "Music Player",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = &A_Plugins_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_SNAKE_GAME
|
#ifdef APP_SNAKE_GAME
|
||||||
{.app = snake_game_app, .name = "Snake Game", .stack_size = 1024, .icon = &A_Plugins_14},
|
{.app = snake_game_app,
|
||||||
|
.name = "Snake Game",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = &A_Plugins_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -251,108 +351,201 @@ const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS);
|
|||||||
// Plugin menu
|
// Plugin menu
|
||||||
const FlipperApplication FLIPPER_DEBUG_APPS[] = {
|
const FlipperApplication FLIPPER_DEBUG_APPS[] = {
|
||||||
#ifdef APP_BLINK
|
#ifdef APP_BLINK
|
||||||
{.app = blink_test_app, .name = "Blink Test", .stack_size = 1024, .icon = NULL},
|
{.app = blink_test_app,
|
||||||
|
.name = "Blink Test",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_VIBRO_TEST
|
#ifdef APP_VIBRO_TEST
|
||||||
{.app = vibro_test_app, .name = "Vibro Test", .stack_size = 1024, .icon = NULL},
|
{.app = vibro_test_app,
|
||||||
|
.name = "Vibro Test",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_KEYPAD_TEST
|
#ifdef APP_KEYPAD_TEST
|
||||||
{.app = keypad_test_app, .name = "Keypad Test", .stack_size = 1024, .icon = NULL},
|
{.app = keypad_test_app,
|
||||||
|
.name = "Keypad Test",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_ACCESSOR
|
#ifdef APP_ACCESSOR
|
||||||
{.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = NULL},
|
{.app = accessor_app,
|
||||||
|
.name = "Accessor",
|
||||||
|
.stack_size = 4096,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_USB_TEST
|
#ifdef APP_USB_TEST
|
||||||
{.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = NULL},
|
{.app = usb_test_app,
|
||||||
|
.name = "USB Test",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_USB_MOUSE
|
#ifdef APP_USB_MOUSE
|
||||||
{.app = usb_mouse_app, .name = "USB Mouse Demo", .stack_size = 1024, .icon = NULL},
|
{.app = usb_mouse_app,
|
||||||
|
.name = "USB Mouse Demo",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_UART_ECHO
|
#ifdef APP_UART_ECHO
|
||||||
{.app = uart_echo_app, .name = "Uart Echo", .stack_size = 2048, .icon = NULL},
|
{.app = uart_echo_app,
|
||||||
|
.name = "Uart Echo",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_INFRARED_MONITOR
|
#ifdef APP_INFRARED_MONITOR
|
||||||
{.app = infrared_monitor_app, .name = "Infrared Monitor", .stack_size = 1024, .icon = NULL},
|
{.app = infrared_monitor_app,
|
||||||
|
.name = "Infrared Monitor",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_SCENED
|
#ifdef APP_SCENED
|
||||||
{.app = scened_app, .name = "Templated Scene", .stack_size = 1024, .icon = NULL},
|
{.app = scened_app,
|
||||||
|
.name = "Templated Scene",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_LF_RFID
|
#ifdef APP_LF_RFID
|
||||||
{.app = lfrfid_debug_app, .name = "LF-RFID Debug", .stack_size = 1024, .icon = NULL},
|
{.app = lfrfid_debug_app,
|
||||||
|
.name = "LF-RFID Debug",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_BT
|
#ifdef SRV_BT
|
||||||
{.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL},
|
{.app = bt_debug_app,
|
||||||
|
.name = "Bluetooth Debug",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_UNIT_TESTS
|
#ifdef APP_UNIT_TESTS
|
||||||
{.app = delay_test_app, .name = "Delay Test", .stack_size = 1024, .icon = NULL},
|
{.app = delay_test_app,
|
||||||
|
.name = "Delay Test",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_DISPLAY_TEST
|
#ifdef APP_DISPLAY_TEST
|
||||||
{.app = display_test_app, .name = "Display Test", .stack_size = 1024, .icon = NULL},
|
{.app = display_test_app,
|
||||||
|
.name = "Display Test",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_BATTERY_TEST
|
#ifdef APP_BATTERY_TEST
|
||||||
{.app = battery_test_app, .name = "Battery Test", .stack_size = 1024, .icon = NULL},
|
{.app = battery_test_app,
|
||||||
|
.name = "Battery Test",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_TEXT_BOX_TEST
|
#ifdef APP_TEXT_BOX_TEST
|
||||||
{.app = text_box_test_app, .name = "Text Box Test", .stack_size = 1024, .icon = NULL},
|
{.app = text_box_test_app,
|
||||||
|
.name = "Text Box Test",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS);
|
const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS);
|
||||||
|
|
||||||
#ifdef APP_ARCHIVE
|
#ifdef APP_ARCHIVE
|
||||||
const FlipperApplication FLIPPER_ARCHIVE =
|
const FlipperApplication FLIPPER_ARCHIVE = {
|
||||||
{.app = archive_app, .name = "Archive", .stack_size = 4096, .icon = &A_FileManager_14};
|
.app = archive_app,
|
||||||
|
.name = "Archive",
|
||||||
|
.stack_size = 4096,
|
||||||
|
.icon = &A_FileManager_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Settings menu
|
// Settings menu
|
||||||
const FlipperApplication FLIPPER_SETTINGS_APPS[] = {
|
const FlipperApplication FLIPPER_SETTINGS_APPS[] = {
|
||||||
#ifdef SRV_BT
|
#ifdef SRV_BT
|
||||||
{.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL},
|
{.app = bt_settings_app,
|
||||||
|
.name = "Bluetooth",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_NOTIFICATION
|
#ifdef SRV_NOTIFICATION
|
||||||
{.app = notification_settings_app,
|
{.app = notification_settings_app,
|
||||||
.name = "LCD and Notifications",
|
.name = "LCD and Notifications",
|
||||||
.stack_size = 1024,
|
.stack_size = 1024,
|
||||||
.icon = NULL},
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_STORAGE
|
#ifdef SRV_STORAGE
|
||||||
{.app = storage_settings_app, .name = "Storage", .stack_size = 2048, .icon = NULL},
|
{.app = storage_settings_app,
|
||||||
|
.name = "Storage",
|
||||||
|
.stack_size = 2048,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_POWER
|
#ifdef SRV_POWER
|
||||||
{.app = power_settings_app, .name = "Power", .stack_size = 1024, .icon = NULL},
|
{.app = power_settings_app,
|
||||||
|
.name = "Power",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagInsomniaSafe},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_DESKTOP
|
#ifdef SRV_DESKTOP
|
||||||
{.app = desktop_settings_app, .name = "Desktop", .stack_size = 1024, .icon = NULL},
|
{.app = desktop_settings_app,
|
||||||
|
.name = "Desktop",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_PASSPORT
|
#ifdef APP_PASSPORT
|
||||||
{.app = passport_app, .name = "Passport", .stack_size = 1024, .icon = NULL},
|
{.app = passport_app,
|
||||||
|
.name = "Passport",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_GUI
|
#ifdef SRV_GUI
|
||||||
{.app = system_settings_app, .name = "System", .stack_size = 1024, .icon = NULL},
|
{.app = system_settings_app,
|
||||||
|
.name = "System",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef APP_ABOUT
|
#ifdef APP_ABOUT
|
||||||
{.app = about_settings_app, .name = "About", .stack_size = 1024, .icon = NULL},
|
{.app = about_settings_app,
|
||||||
|
.name = "About",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = NULL,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,11 +3,17 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <gui/icon.h>
|
#include <gui/icon.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FlipperApplicationFlagDefault = 0,
|
||||||
|
FlipperApplicationFlagInsomniaSafe = (1 << 0),
|
||||||
|
} FlipperApplicationFlag;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const FuriThreadCallback app;
|
const FuriThreadCallback app;
|
||||||
const char* name;
|
const char* name;
|
||||||
const size_t stack_size;
|
const size_t stack_size;
|
||||||
const Icon* icon;
|
const Icon* icon;
|
||||||
|
const FlipperApplicationFlag flags;
|
||||||
} FlipperApplication;
|
} FlipperApplication;
|
||||||
|
|
||||||
typedef void (*FlipperOnStartHook)(void);
|
typedef void (*FlipperOnStartHook)(void);
|
||||||
|
|||||||
@ -27,6 +27,7 @@ struct ArchiveApp {
|
|||||||
ArchiveBrowserView* browser;
|
ArchiveBrowserView* browser;
|
||||||
TextInput* text_input;
|
TextInput* text_input;
|
||||||
Widget* widget;
|
Widget* widget;
|
||||||
|
FuriPubSubSubscription* loader_stop_subscription;
|
||||||
char text_store[MAX_NAME_LEN];
|
char text_store[MAX_NAME_LEN];
|
||||||
char file_extension[MAX_EXT_LEN + 1];
|
char file_extension[MAX_EXT_LEN + 1];
|
||||||
};
|
};
|
||||||
|
|||||||
@ -77,6 +77,7 @@ void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) {
|
|||||||
with_view_model(
|
with_view_model(
|
||||||
browser->view, (ArchiveBrowserViewModel * model) {
|
browser->view, (ArchiveBrowserViewModel * model) {
|
||||||
model->item_cnt = count;
|
model->item_cnt = count;
|
||||||
|
model->item_idx = CLAMP(model->item_idx, model->item_cnt - 1, 0);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -397,6 +398,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) {
|
|||||||
|
|
||||||
archive_dir_count_items(browser, string_get_cstr(name));
|
archive_dir_count_items(browser, string_get_cstr(name));
|
||||||
|
|
||||||
|
if(string_cmp(browser->path, name) != 0) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
browser->view, (ArchiveBrowserViewModel * model) {
|
browser->view, (ArchiveBrowserViewModel * model) {
|
||||||
idx_last_array_push_back(model->idx_last, model->item_idx);
|
idx_last_array_push_back(model->idx_last, model->item_idx);
|
||||||
@ -406,6 +408,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
string_set(browser->path, name);
|
string_set(browser->path, name);
|
||||||
|
}
|
||||||
|
|
||||||
archive_switch_dir(browser, string_get_cstr(browser->path));
|
archive_switch_dir(browser, string_get_cstr(browser->path));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,18 @@ static const char* flipper_app_name[] = {
|
|||||||
[ArchiveFileTypeUpdateManifest] = "UpdaterApp",
|
[ArchiveFileTypeUpdateManifest] = "UpdaterApp",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void archive_loader_callback(const void* message, void* context) {
|
||||||
|
furi_assert(message);
|
||||||
|
furi_assert(context);
|
||||||
|
const LoaderEvent* event = message;
|
||||||
|
ArchiveApp* archive = (ArchiveApp*)context;
|
||||||
|
|
||||||
|
if(event->type == LoaderEventTypeApplicationStopped) {
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
archive->view_dispatcher, ArchiveBrowserEventLoaderAppExit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) {
|
static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) {
|
||||||
Loader* loader = furi_record_open("loader");
|
Loader* loader = furi_record_open("loader");
|
||||||
|
|
||||||
@ -52,6 +64,11 @@ void archive_scene_browser_on_enter(void* context) {
|
|||||||
archive_browser_set_callback(browser, archive_scene_browser_callback, archive);
|
archive_browser_set_callback(browser, archive_scene_browser_callback, archive);
|
||||||
archive_update_focus(browser, archive->text_store);
|
archive_update_focus(browser, archive->text_store);
|
||||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser);
|
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser);
|
||||||
|
|
||||||
|
Loader* loader = furi_record_open("loader");
|
||||||
|
archive->loader_stop_subscription =
|
||||||
|
furi_pubsub_subscribe(loader_get_pubsub(loader), archive_loader_callback, archive);
|
||||||
|
furi_record_close("loader");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
||||||
@ -147,11 +164,25 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
|||||||
archive_file_array_load(archive->browser, 1);
|
archive_file_array_load(archive->browser, 1);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
|
case ArchiveBrowserEventLoaderAppExit:
|
||||||
|
if(!favorites) {
|
||||||
|
archive_enter_dir(browser, browser->path);
|
||||||
|
} else {
|
||||||
|
archive_favorites_read(browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case ArchiveBrowserEventExit:
|
case ArchiveBrowserEventExit:
|
||||||
if(archive_get_depth(browser)) {
|
if(archive_get_depth(browser)) {
|
||||||
archive_leave_dir(browser);
|
archive_leave_dir(browser);
|
||||||
} else {
|
} else {
|
||||||
|
Loader* loader = furi_record_open("loader");
|
||||||
|
furi_pubsub_unsubscribe(
|
||||||
|
loader_get_pubsub(loader), archive->loader_stop_subscription);
|
||||||
|
furi_record_close("loader");
|
||||||
|
|
||||||
view_dispatcher_stop(archive->view_dispatcher);
|
view_dispatcher_stop(archive->view_dispatcher);
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
@ -165,5 +196,9 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void archive_scene_browser_on_exit(void* context) {
|
void archive_scene_browser_on_exit(void* context) {
|
||||||
// ArchiveApp* archive = (ArchiveApp*)context;
|
ArchiveApp* archive = (ArchiveApp*)context;
|
||||||
|
|
||||||
|
Loader* loader = furi_record_open("loader");
|
||||||
|
furi_pubsub_unsubscribe(loader_get_pubsub(loader), archive->loader_stop_subscription);
|
||||||
|
furi_record_close("loader");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,8 @@ typedef enum {
|
|||||||
ArchiveBrowserEventLoadPrevItems,
|
ArchiveBrowserEventLoadPrevItems,
|
||||||
ArchiveBrowserEventLoadNextItems,
|
ArchiveBrowserEventLoadNextItems,
|
||||||
|
|
||||||
|
ArchiveBrowserEventLoaderAppExit,
|
||||||
|
|
||||||
ArchiveBrowserEventExit,
|
ArchiveBrowserEventExit,
|
||||||
} ArchiveBrowserEvent;
|
} ArchiveBrowserEvent;
|
||||||
|
|
||||||
@ -72,7 +74,6 @@ struct ArchiveBrowserView {
|
|||||||
View* view;
|
View* view;
|
||||||
ArchiveBrowserViewCallback callback;
|
ArchiveBrowserViewCallback callback;
|
||||||
void* context;
|
void* context;
|
||||||
|
|
||||||
string_t path;
|
string_t path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -148,7 +148,7 @@ static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) {
|
|||||||
static void bt_cli_scan_callback(GapAddress address, void* context) {
|
static void bt_cli_scan_callback(GapAddress address, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
osMessageQueueId_t queue = context;
|
osMessageQueueId_t queue = context;
|
||||||
osMessageQueuePut(queue, &address, NULL, 250);
|
osMessageQueuePut(queue, &address, 0, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_cli_command_scan(Cli* cli, string_t args, void* context) {
|
static void bt_cli_command_scan(Cli* cli, string_t args, void* context) {
|
||||||
|
|||||||
@ -60,7 +60,7 @@ static ViewPort* bt_pin_code_view_port_alloc(Bt* bt) {
|
|||||||
|
|
||||||
static void bt_pin_code_show(Bt* bt, uint32_t pin_code) {
|
static void bt_pin_code_show(Bt* bt, uint32_t pin_code) {
|
||||||
bt->pin_code = pin_code;
|
bt->pin_code = pin_code;
|
||||||
notification_message(bt->notification, &sequence_display_on);
|
notification_message(bt->notification, &sequence_display_backlight_on);
|
||||||
gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port);
|
gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port);
|
||||||
view_port_enabled_set(bt->pin_code_view_port, true);
|
view_port_enabled_set(bt->pin_code_view_port, true);
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ static void bt_pin_code_hide(Bt* bt) {
|
|||||||
|
|
||||||
static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) {
|
static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) {
|
||||||
furi_assert(bt);
|
furi_assert(bt);
|
||||||
notification_message(bt->notification, &sequence_display_on);
|
notification_message(bt->notification, &sequence_display_backlight_on);
|
||||||
string_t pin_str;
|
string_t pin_str;
|
||||||
dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0);
|
dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0);
|
||||||
string_init_printf(pin_str, "Verify code\n%06d", pin);
|
string_init_printf(pin_str, "Verify code\n%06d", pin);
|
||||||
@ -319,6 +319,13 @@ static void bt_change_profile(Bt* bt, BtMessage* message) {
|
|||||||
int32_t bt_srv() {
|
int32_t bt_srv() {
|
||||||
Bt* bt = bt_alloc();
|
Bt* bt = bt_alloc();
|
||||||
|
|
||||||
|
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
|
||||||
|
FURI_LOG_W(TAG, "Skipped BT init: device in special startup mode");
|
||||||
|
ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT);
|
||||||
|
furi_record_create("bt", bt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Read keys
|
// Read keys
|
||||||
if(!bt_keys_storage_load(bt)) {
|
if(!bt_keys_storage_load(bt)) {
|
||||||
FURI_LOG_W(TAG, "Failed to load bonding keys");
|
FURI_LOG_W(TAG, "Failed to load bonding keys");
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
#include "cli_i.h"
|
#include "cli_i.h"
|
||||||
#include "cli_commands.h"
|
#include "cli_commands.h"
|
||||||
|
#include "cli_vcp.h"
|
||||||
#include <furi_hal_version.h>
|
#include <furi_hal_version.h>
|
||||||
#include <loader/loader.h>
|
#include <loader/loader.h>
|
||||||
|
|
||||||
|
#define TAG "CliSrv"
|
||||||
|
|
||||||
Cli* cli_alloc() {
|
Cli* cli_alloc() {
|
||||||
Cli* cli = malloc(sizeof(Cli));
|
Cli* cli = malloc(sizeof(Cli));
|
||||||
|
|
||||||
@ -12,55 +14,78 @@ Cli* cli_alloc() {
|
|||||||
string_init(cli->last_line);
|
string_init(cli->last_line);
|
||||||
string_init(cli->line);
|
string_init(cli->line);
|
||||||
|
|
||||||
|
cli->session = NULL;
|
||||||
|
|
||||||
cli->mutex = osMutexNew(NULL);
|
cli->mutex = osMutexNew(NULL);
|
||||||
furi_check(cli->mutex);
|
furi_check(cli->mutex);
|
||||||
|
|
||||||
|
cli->idle_sem = osSemaphoreNew(1, 0, NULL);
|
||||||
|
|
||||||
return cli;
|
return cli;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_free(Cli* cli) {
|
void cli_putc(Cli* cli, char c) {
|
||||||
furi_assert(cli);
|
furi_assert(cli);
|
||||||
|
if(cli->session != NULL) {
|
||||||
string_clear(cli->last_line);
|
cli->session->tx((uint8_t*)&c, 1);
|
||||||
string_clear(cli->line);
|
}
|
||||||
|
|
||||||
CliCommandTree_clear(cli->commands);
|
|
||||||
|
|
||||||
free(cli);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_putc(char c) {
|
|
||||||
furi_hal_vcp_tx((uint8_t*)&c, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char cli_getc(Cli* cli) {
|
char cli_getc(Cli* cli) {
|
||||||
furi_assert(cli);
|
furi_assert(cli);
|
||||||
char c;
|
char c = 0;
|
||||||
if(furi_hal_vcp_rx((uint8_t*)&c, 1) == 0) {
|
if(cli->session != NULL) {
|
||||||
|
if(cli->session->rx((uint8_t*)&c, 1, osWaitForever) == 0) {
|
||||||
|
cli_reset(cli);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
cli_reset(cli);
|
cli_reset(cli);
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_stdout_callback(void* _cookie, const char* data, size_t size) {
|
|
||||||
furi_hal_vcp_tx((const uint8_t*)data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_write(Cli* cli, const uint8_t* buffer, size_t size) {
|
void cli_write(Cli* cli, const uint8_t* buffer, size_t size) {
|
||||||
return furi_hal_vcp_tx(buffer, size);
|
furi_assert(cli);
|
||||||
|
if(cli->session != NULL) {
|
||||||
|
cli->session->tx(buffer, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) {
|
size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) {
|
||||||
return furi_hal_vcp_rx(buffer, size);
|
furi_assert(cli);
|
||||||
|
if(cli->session != NULL) {
|
||||||
|
return cli->session->rx(buffer, size, osWaitForever);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout) {
|
||||||
|
furi_assert(cli);
|
||||||
|
if(cli->session != NULL) {
|
||||||
|
return cli->session->rx(buffer, size, timeout);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cli_cmd_interrupt_received(Cli* cli) {
|
bool cli_cmd_interrupt_received(Cli* cli) {
|
||||||
|
furi_assert(cli);
|
||||||
char c = '\0';
|
char c = '\0';
|
||||||
if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 0) == 1) {
|
if(cli->session != NULL) {
|
||||||
|
if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) {
|
||||||
return c == CliSymbolAsciiETX;
|
return c == CliSymbolAsciiETX;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cli_is_connected(Cli* cli) {
|
||||||
|
furi_assert(cli);
|
||||||
|
if(cli->session != NULL) {
|
||||||
|
return (cli->session->is_connected());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_print_usage(const char* cmd, const char* usage, const char* arg) {
|
void cli_print_usage(const char* cmd, const char* usage, const char* arg) {
|
||||||
@ -139,7 +164,7 @@ static void cli_handle_backspace(Cli* cli) {
|
|||||||
|
|
||||||
cli->cursor_position--;
|
cli->cursor_position--;
|
||||||
} else {
|
} else {
|
||||||
cli_putc(CliSymbolAsciiBell);
|
cli_putc(cli, CliSymbolAsciiBell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +235,7 @@ static void cli_handle_enter(Cli* cli) {
|
|||||||
printf(
|
printf(
|
||||||
"`%s` command not found, use `help` or `?` to list all available commands",
|
"`%s` command not found, use `help` or `?` to list all available commands",
|
||||||
string_get_cstr(command));
|
string_get_cstr(command));
|
||||||
cli_putc(CliSymbolAsciiBell);
|
cli_putc(cli, CliSymbolAsciiBell);
|
||||||
}
|
}
|
||||||
furi_check(osMutexRelease(cli->mutex) == osOK);
|
furi_check(osMutexRelease(cli->mutex) == osOK);
|
||||||
|
|
||||||
@ -301,43 +326,43 @@ static void cli_handle_escape(Cli* cli, char c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cli_process_input(Cli* cli) {
|
void cli_process_input(Cli* cli) {
|
||||||
char c = cli_getc(cli);
|
char in_chr = cli_getc(cli);
|
||||||
size_t r;
|
size_t rx_len;
|
||||||
|
|
||||||
if(c == CliSymbolAsciiTab) {
|
if(in_chr == CliSymbolAsciiTab) {
|
||||||
cli_handle_autocomplete(cli);
|
cli_handle_autocomplete(cli);
|
||||||
} else if(c == CliSymbolAsciiSOH) {
|
} else if(in_chr == CliSymbolAsciiSOH) {
|
||||||
osDelay(33); // We are too fast, Minicom is not ready yet
|
osDelay(33); // We are too fast, Minicom is not ready yet
|
||||||
cli_motd();
|
cli_motd();
|
||||||
cli_prompt(cli);
|
cli_prompt(cli);
|
||||||
} else if(c == CliSymbolAsciiETX) {
|
} else if(in_chr == CliSymbolAsciiETX) {
|
||||||
cli_reset(cli);
|
cli_reset(cli);
|
||||||
cli_prompt(cli);
|
cli_prompt(cli);
|
||||||
} else if(c == CliSymbolAsciiEOT) {
|
} else if(in_chr == CliSymbolAsciiEOT) {
|
||||||
cli_reset(cli);
|
cli_reset(cli);
|
||||||
} else if(c == CliSymbolAsciiEsc) {
|
} else if(in_chr == CliSymbolAsciiEsc) {
|
||||||
r = furi_hal_vcp_rx((uint8_t*)&c, 1);
|
rx_len = cli_read(cli, (uint8_t*)&in_chr, 1);
|
||||||
if(r && c == '[') {
|
if((rx_len > 0) && (in_chr == '[')) {
|
||||||
furi_hal_vcp_rx((uint8_t*)&c, 1);
|
cli_read(cli, (uint8_t*)&in_chr, 1);
|
||||||
cli_handle_escape(cli, c);
|
cli_handle_escape(cli, in_chr);
|
||||||
} else {
|
} else {
|
||||||
cli_putc(CliSymbolAsciiBell);
|
cli_putc(cli, CliSymbolAsciiBell);
|
||||||
}
|
}
|
||||||
} else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) {
|
} else if(in_chr == CliSymbolAsciiBackspace || in_chr == CliSymbolAsciiDel) {
|
||||||
cli_handle_backspace(cli);
|
cli_handle_backspace(cli);
|
||||||
} else if(c == CliSymbolAsciiCR) {
|
} else if(in_chr == CliSymbolAsciiCR) {
|
||||||
cli_handle_enter(cli);
|
cli_handle_enter(cli);
|
||||||
} else if(c >= 0x20 && c < 0x7F) {
|
} else if(in_chr >= 0x20 && in_chr < 0x7F) {
|
||||||
if(cli->cursor_position == string_size(cli->line)) {
|
if(cli->cursor_position == string_size(cli->line)) {
|
||||||
string_push_back(cli->line, c);
|
string_push_back(cli->line, in_chr);
|
||||||
cli_putc(c);
|
cli_putc(cli, in_chr);
|
||||||
} else {
|
} else {
|
||||||
// ToDo: better way?
|
// ToDo: better way?
|
||||||
string_t temp;
|
string_t temp;
|
||||||
string_init(temp);
|
string_init(temp);
|
||||||
string_reserve(temp, string_size(cli->line) + 1);
|
string_reserve(temp, string_size(cli->line) + 1);
|
||||||
string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position);
|
string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position);
|
||||||
string_push_back(temp, c);
|
string_push_back(temp, in_chr);
|
||||||
string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position);
|
string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position);
|
||||||
|
|
||||||
// cli->line is cleared and temp's buffer moved to cli->line
|
// cli->line is cleared and temp's buffer moved to cli->line
|
||||||
@ -345,12 +370,12 @@ void cli_process_input(Cli* cli) {
|
|||||||
// NO MEMORY LEAK, STOP REPORTING IT
|
// NO MEMORY LEAK, STOP REPORTING IT
|
||||||
|
|
||||||
// Print character in replace mode
|
// Print character in replace mode
|
||||||
printf("\e[4h%c\e[4l", c);
|
printf("\e[4h%c\e[4l", in_chr);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
cli->cursor_position++;
|
cli->cursor_position++;
|
||||||
} else {
|
} else {
|
||||||
cli_putc(CliSymbolAsciiBell);
|
cli_putc(cli, CliSymbolAsciiBell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,19 +423,59 @@ void cli_delete_command(Cli* cli, const char* name) {
|
|||||||
string_clear(name_str);
|
string_clear(name_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cli_session_open(Cli* cli, void* session) {
|
||||||
|
furi_assert(cli);
|
||||||
|
|
||||||
|
furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
|
||||||
|
cli->session = session;
|
||||||
|
if(cli->session != NULL) {
|
||||||
|
cli->session->init();
|
||||||
|
furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout);
|
||||||
|
} else {
|
||||||
|
furi_stdglue_set_thread_stdout_callback(NULL);
|
||||||
|
}
|
||||||
|
osSemaphoreRelease(cli->idle_sem);
|
||||||
|
furi_check(osMutexRelease(cli->mutex) == osOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_session_close(Cli* cli) {
|
||||||
|
furi_assert(cli);
|
||||||
|
|
||||||
|
furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
|
||||||
|
if(cli->session != NULL) {
|
||||||
|
cli->session->deinit();
|
||||||
|
}
|
||||||
|
cli->session = NULL;
|
||||||
|
furi_stdglue_set_thread_stdout_callback(NULL);
|
||||||
|
furi_check(osMutexRelease(cli->mutex) == osOK);
|
||||||
|
}
|
||||||
|
|
||||||
int32_t cli_srv(void* p) {
|
int32_t cli_srv(void* p) {
|
||||||
Cli* cli = cli_alloc();
|
Cli* cli = cli_alloc();
|
||||||
|
|
||||||
furi_hal_vcp_init();
|
|
||||||
|
|
||||||
// Init basic cli commands
|
// Init basic cli commands
|
||||||
cli_commands_init(cli);
|
cli_commands_init(cli);
|
||||||
|
|
||||||
furi_record_create("cli", cli);
|
furi_record_create("cli", cli);
|
||||||
|
|
||||||
furi_stdglue_set_thread_stdout_callback(cli_stdout_callback);
|
if(cli->session != NULL) {
|
||||||
|
furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout);
|
||||||
|
} else {
|
||||||
|
furi_stdglue_set_thread_stdout_callback(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) {
|
||||||
|
cli_session_open(cli, &cli_vcp);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_W(TAG, "Skipped CLI session open: device in special startup mode");
|
||||||
|
}
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
|
if(cli->session != NULL) {
|
||||||
cli_process_input(cli);
|
cli_process_input(cli);
|
||||||
|
} else {
|
||||||
|
furi_check(osSemaphoreAcquire(cli->idle_sem, osWaitForever) == osOK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -73,17 +73,28 @@ void cli_print_usage(const char* cmd, const char* usage, const char* arg);
|
|||||||
*/
|
*/
|
||||||
void cli_delete_command(Cli* cli, const char* name);
|
void cli_delete_command(Cli* cli, const char* name);
|
||||||
|
|
||||||
/** Read from terminal Do it only from inside of cli call.
|
/** Read from terminal
|
||||||
*
|
*
|
||||||
* @param cli Cli instance
|
* @param cli Cli instance
|
||||||
* @param buffer pointer to buffer
|
* @param buffer pointer to buffer
|
||||||
* @param size size of buffer in bytes
|
* @param size size of buffer in bytes
|
||||||
*
|
*
|
||||||
* @return bytes written
|
* @return bytes read
|
||||||
*/
|
*/
|
||||||
size_t cli_read(Cli* cli, uint8_t* buffer, size_t size);
|
size_t cli_read(Cli* cli, uint8_t* buffer, size_t size);
|
||||||
|
|
||||||
/** Not blocking check for interrupt command received
|
/** Non-blocking read from terminal
|
||||||
|
*
|
||||||
|
* @param cli Cli instance
|
||||||
|
* @param buffer pointer to buffer
|
||||||
|
* @param size size of buffer in bytes
|
||||||
|
* @param timeout timeout value in ms
|
||||||
|
*
|
||||||
|
* @return bytes read
|
||||||
|
*/
|
||||||
|
size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout);
|
||||||
|
|
||||||
|
/** Non-blocking check for interrupt command received
|
||||||
*
|
*
|
||||||
* @param cli Cli instance
|
* @param cli Cli instance
|
||||||
*
|
*
|
||||||
@ -111,6 +122,12 @@ char cli_getc(Cli* cli);
|
|||||||
*/
|
*/
|
||||||
void cli_nl();
|
void cli_nl();
|
||||||
|
|
||||||
|
void cli_session_open(Cli* cli, void* session);
|
||||||
|
|
||||||
|
void cli_session_close(Cli* cli);
|
||||||
|
|
||||||
|
bool cli_is_connected(Cli* cli);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -202,7 +202,7 @@ void cli_command_led(Cli* cli, string_t args, void* context) {
|
|||||||
} else if(!string_cmp(light_name, "b")) {
|
} else if(!string_cmp(light_name, "b")) {
|
||||||
notification_led_message.type = NotificationMessageTypeLedBlue;
|
notification_led_message.type = NotificationMessageTypeLedBlue;
|
||||||
} else if(!string_cmp(light_name, "bl")) {
|
} else if(!string_cmp(light_name, "bl")) {
|
||||||
notification_led_message.type = NotificationMessageTypeLedDisplay;
|
notification_led_message.type = NotificationMessageTypeLedDisplayBacklight;
|
||||||
} else {
|
} else {
|
||||||
cli_print_usage("led", "<r|g|b|bl> <0-255>", string_get_cstr(args));
|
cli_print_usage("led", "<r|g|b|bl> <0-255>", string_get_cstr(args));
|
||||||
string_clear(light_name);
|
string_clear(light_name);
|
||||||
|
|||||||
@ -18,6 +18,17 @@ typedef struct {
|
|||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
} CliCommand;
|
} CliCommand;
|
||||||
|
|
||||||
|
typedef struct CliSession CliSession;
|
||||||
|
|
||||||
|
struct CliSession {
|
||||||
|
void (*init)(void);
|
||||||
|
void (*deinit)(void);
|
||||||
|
size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout);
|
||||||
|
void (*tx)(const uint8_t* buffer, size_t size);
|
||||||
|
void (*tx_stdout)(void* _cookie, const char* data, size_t size);
|
||||||
|
bool (*is_connected)(void);
|
||||||
|
};
|
||||||
|
|
||||||
BPTREE_DEF2(
|
BPTREE_DEF2(
|
||||||
CliCommandTree,
|
CliCommandTree,
|
||||||
CLI_COMMANDS_TREE_RANK,
|
CLI_COMMANDS_TREE_RANK,
|
||||||
@ -31,18 +42,18 @@ BPTREE_DEF2(
|
|||||||
struct Cli {
|
struct Cli {
|
||||||
CliCommandTree_t commands;
|
CliCommandTree_t commands;
|
||||||
osMutexId_t mutex;
|
osMutexId_t mutex;
|
||||||
|
osSemaphoreId_t idle_sem;
|
||||||
string_t last_line;
|
string_t last_line;
|
||||||
string_t line;
|
string_t line;
|
||||||
|
CliSession* session;
|
||||||
|
|
||||||
size_t cursor_position;
|
size_t cursor_position;
|
||||||
};
|
};
|
||||||
|
|
||||||
Cli* cli_alloc();
|
Cli* cli_alloc();
|
||||||
|
|
||||||
void cli_free(Cli* cli);
|
|
||||||
|
|
||||||
void cli_reset(Cli* cli);
|
void cli_reset(Cli* cli);
|
||||||
|
|
||||||
void cli_putc(char c);
|
void cli_putc(Cli* cli, char c);
|
||||||
|
|
||||||
void cli_stdout_callback(void* _cookie, const char* data, size_t size);
|
void cli_stdout_callback(void* _cookie, const char* data, size_t size);
|
||||||
|
|||||||
@ -2,8 +2,9 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <stream_buffer.h>
|
#include <stream_buffer.h>
|
||||||
|
#include "cli_i.h"
|
||||||
|
|
||||||
#define TAG "FuriHalVcp"
|
#define TAG "CliVcp"
|
||||||
|
|
||||||
#define USB_CDC_PKT_LEN CDC_DATA_SZ
|
#define USB_CDC_PKT_LEN CDC_DATA_SZ
|
||||||
#define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3)
|
#define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3)
|
||||||
@ -12,19 +13,18 @@
|
|||||||
#define VCP_IF_NUM 0
|
#define VCP_IF_NUM 0
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VcpEvtEnable = (1 << 0),
|
VcpEvtStop = (1 << 0),
|
||||||
VcpEvtDisable = (1 << 1),
|
VcpEvtConnect = (1 << 1),
|
||||||
VcpEvtConnect = (1 << 2),
|
VcpEvtDisconnect = (1 << 2),
|
||||||
VcpEvtDisconnect = (1 << 3),
|
VcpEvtStreamRx = (1 << 3),
|
||||||
VcpEvtStreamRx = (1 << 4),
|
VcpEvtRx = (1 << 4),
|
||||||
VcpEvtRx = (1 << 5),
|
VcpEvtStreamTx = (1 << 5),
|
||||||
VcpEvtStreamTx = (1 << 6),
|
VcpEvtTx = (1 << 6),
|
||||||
VcpEvtTx = (1 << 7),
|
|
||||||
} WorkerEvtFlags;
|
} WorkerEvtFlags;
|
||||||
|
|
||||||
#define VCP_THREAD_FLAG_ALL \
|
#define VCP_THREAD_FLAG_ALL \
|
||||||
(VcpEvtEnable | VcpEvtDisable | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | \
|
(VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \
|
||||||
VcpEvtStreamRx | VcpEvtStreamTx)
|
VcpEvtStreamTx)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FuriThread* thread;
|
FuriThread* thread;
|
||||||
@ -33,9 +33,12 @@ typedef struct {
|
|||||||
StreamBufferHandle_t rx_stream;
|
StreamBufferHandle_t rx_stream;
|
||||||
|
|
||||||
volatile bool connected;
|
volatile bool connected;
|
||||||
|
volatile bool running;
|
||||||
|
|
||||||
|
FuriHalUsbInterface* usb_if_prev;
|
||||||
|
|
||||||
uint8_t data_buffer[USB_CDC_PKT_LEN];
|
uint8_t data_buffer[USB_CDC_PKT_LEN];
|
||||||
} FuriHalVcp;
|
} CliVcp;
|
||||||
|
|
||||||
static int32_t vcp_worker(void* context);
|
static int32_t vcp_worker(void* context);
|
||||||
static void vcp_on_cdc_tx_complete(void* context);
|
static void vcp_on_cdc_tx_complete(void* context);
|
||||||
@ -51,25 +54,23 @@ static CdcCallbacks cdc_cb = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static FuriHalVcp* vcp = NULL;
|
static CliVcp* vcp = NULL;
|
||||||
|
|
||||||
static const uint8_t ascii_soh = 0x01;
|
static const uint8_t ascii_soh = 0x01;
|
||||||
static const uint8_t ascii_eot = 0x04;
|
static const uint8_t ascii_eot = 0x04;
|
||||||
|
|
||||||
void furi_hal_vcp_init() {
|
static void cli_vcp_init() {
|
||||||
vcp = malloc(sizeof(FuriHalVcp));
|
if(vcp == NULL) {
|
||||||
vcp->connected = false;
|
vcp = malloc(sizeof(CliVcp));
|
||||||
|
|
||||||
vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1);
|
vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1);
|
||||||
vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1);
|
vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1);
|
||||||
|
|
||||||
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
|
|
||||||
FURI_LOG_W(TAG, "Skipped worker init: device in special startup mode=");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
furi_assert(vcp->thread == NULL);
|
||||||
|
|
||||||
|
vcp->connected = false;
|
||||||
|
|
||||||
vcp->thread = furi_thread_alloc();
|
vcp->thread = furi_thread_alloc();
|
||||||
furi_thread_set_name(vcp->thread, "VcpDriver");
|
furi_thread_set_name(vcp->thread, "CliVcpWorker");
|
||||||
furi_thread_set_stack_size(vcp->thread, 1024);
|
furi_thread_set_stack_size(vcp->thread, 1024);
|
||||||
furi_thread_set_callback(vcp->thread, vcp_worker);
|
furi_thread_set_callback(vcp->thread, vcp_worker);
|
||||||
furi_thread_start(vcp->thread);
|
furi_thread_start(vcp->thread);
|
||||||
@ -77,48 +78,35 @@ void furi_hal_vcp_init() {
|
|||||||
FURI_LOG_I(TAG, "Init OK");
|
FURI_LOG_I(TAG, "Init OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cli_vcp_deinit() {
|
||||||
|
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStop);
|
||||||
|
furi_thread_join(vcp->thread);
|
||||||
|
furi_thread_free(vcp->thread);
|
||||||
|
vcp->thread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int32_t vcp_worker(void* context) {
|
static int32_t vcp_worker(void* context) {
|
||||||
bool enabled = true;
|
bool tx_idle = true;
|
||||||
bool tx_idle = false;
|
|
||||||
size_t missed_rx = 0;
|
size_t missed_rx = 0;
|
||||||
uint8_t last_tx_pkt_len = 0;
|
uint8_t last_tx_pkt_len = 0;
|
||||||
|
|
||||||
|
// Switch USB to VCP mode (if it is not set yet)
|
||||||
|
vcp->usb_if_prev = furi_hal_usb_get_config();
|
||||||
|
if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) {
|
||||||
furi_hal_usb_set_config(&usb_cdc_single, NULL);
|
furi_hal_usb_set_config(&usb_cdc_single, NULL);
|
||||||
|
}
|
||||||
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
|
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
|
||||||
|
|
||||||
|
FURI_LOG_D(TAG, "Start");
|
||||||
|
vcp->running = true;
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever);
|
uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever);
|
||||||
furi_assert((flags & osFlagsError) == 0);
|
furi_assert((flags & osFlagsError) == 0);
|
||||||
|
|
||||||
// VCP enabled
|
|
||||||
if((flags & VcpEvtEnable) && !enabled) {
|
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
|
||||||
FURI_LOG_D(TAG, "Enable");
|
|
||||||
#endif
|
|
||||||
flags |= VcpEvtTx;
|
|
||||||
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
|
|
||||||
enabled = true;
|
|
||||||
furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); // flush Rx buffer
|
|
||||||
if(furi_hal_cdc_get_ctrl_line_state(VCP_IF_NUM) & (1 << 0)) {
|
|
||||||
vcp->connected = true;
|
|
||||||
xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VCP disabled
|
|
||||||
if((flags & VcpEvtDisable) && enabled) {
|
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
|
||||||
FURI_LOG_D(TAG, "Disable");
|
|
||||||
#endif
|
|
||||||
enabled = false;
|
|
||||||
vcp->connected = false;
|
|
||||||
xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
|
|
||||||
xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
|
|
||||||
}
|
|
||||||
|
|
||||||
// VCP session opened
|
// VCP session opened
|
||||||
if((flags & VcpEvtConnect) && enabled) {
|
if(flags & VcpEvtConnect) {
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "Connect");
|
FURI_LOG_D(TAG, "Connect");
|
||||||
#endif
|
#endif
|
||||||
if(vcp->connected == false) {
|
if(vcp->connected == false) {
|
||||||
@ -128,8 +116,8 @@ static int32_t vcp_worker(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VCP session closed
|
// VCP session closed
|
||||||
if((flags & VcpEvtDisconnect) && enabled) {
|
if(flags & VcpEvtDisconnect) {
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "Disconnect");
|
FURI_LOG_D(TAG, "Disconnect");
|
||||||
#endif
|
#endif
|
||||||
if(vcp->connected == true) {
|
if(vcp->connected == true) {
|
||||||
@ -140,8 +128,8 @@ static int32_t vcp_worker(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rx buffer was read, maybe there is enough space for new data?
|
// Rx buffer was read, maybe there is enough space for new data?
|
||||||
if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) {
|
if((flags & VcpEvtStreamRx) && (missed_rx > 0)) {
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "StreamRx");
|
FURI_LOG_D(TAG, "StreamRx");
|
||||||
#endif
|
#endif
|
||||||
if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
|
if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
|
||||||
@ -151,10 +139,10 @@ static int32_t vcp_worker(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New data received
|
// New data received
|
||||||
if((flags & VcpEvtRx)) {
|
if(flags & VcpEvtRx) {
|
||||||
if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
|
if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
|
||||||
int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN);
|
int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN);
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "Rx %d", len);
|
FURI_LOG_D(TAG, "Rx %d", len);
|
||||||
#endif
|
#endif
|
||||||
if(len > 0) {
|
if(len > 0) {
|
||||||
@ -163,7 +151,7 @@ static int32_t vcp_worker(void* context) {
|
|||||||
len);
|
len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "Rx missed");
|
FURI_LOG_D(TAG, "Rx missed");
|
||||||
#endif
|
#endif
|
||||||
missed_rx++;
|
missed_rx++;
|
||||||
@ -171,8 +159,8 @@ static int32_t vcp_worker(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New data in Tx buffer
|
// New data in Tx buffer
|
||||||
if((flags & VcpEvtStreamTx) && enabled) {
|
if(flags & VcpEvtStreamTx) {
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "StreamTx");
|
FURI_LOG_D(TAG, "StreamTx");
|
||||||
#endif
|
#endif
|
||||||
if(tx_idle) {
|
if(tx_idle) {
|
||||||
@ -181,10 +169,10 @@ static int32_t vcp_worker(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CDC write transfer done
|
// CDC write transfer done
|
||||||
if((flags & VcpEvtTx) && enabled) {
|
if(flags & VcpEvtTx) {
|
||||||
size_t len =
|
size_t len =
|
||||||
xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
|
xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "Tx %d", len);
|
FURI_LOG_D(TAG, "Tx %d", len);
|
||||||
#endif
|
#endif
|
||||||
if(len > 0) { // Some data left in Tx buffer. Sending it now
|
if(len > 0) { // Some data left in Tx buffer. Sending it now
|
||||||
@ -202,23 +190,33 @@ static int32_t vcp_worker(void* context) {
|
|||||||
last_tx_pkt_len = 0;
|
last_tx_pkt_len = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(flags & VcpEvtStop) {
|
||||||
|
vcp->connected = false;
|
||||||
|
vcp->running = false;
|
||||||
|
furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL);
|
||||||
|
// Restore previous USB mode (if it was set during init)
|
||||||
|
if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) {
|
||||||
|
furi_hal_usb_set_config(vcp->usb_if_prev, NULL);
|
||||||
}
|
}
|
||||||
|
xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0);
|
||||||
|
xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FURI_LOG_D(TAG, "End");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void furi_hal_vcp_enable() {
|
static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) {
|
||||||
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtEnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void furi_hal_vcp_disable() {
|
|
||||||
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisable);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout) {
|
|
||||||
furi_assert(vcp);
|
furi_assert(vcp);
|
||||||
furi_assert(buffer);
|
furi_assert(buffer);
|
||||||
|
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
if(vcp->running == false) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "rx %u start", size);
|
FURI_LOG_D(TAG, "rx %u start", size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -229,7 +227,7 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo
|
|||||||
if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE;
|
if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE;
|
||||||
|
|
||||||
size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout);
|
size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout);
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "rx %u ", batch_size);
|
FURI_LOG_D(TAG, "rx %u ", batch_size);
|
||||||
#endif
|
#endif
|
||||||
if(len == 0) break;
|
if(len == 0) break;
|
||||||
@ -239,22 +237,21 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo
|
|||||||
rx_cnt += len;
|
rx_cnt += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "rx %u end", size);
|
FURI_LOG_D(TAG, "rx %u end", size);
|
||||||
#endif
|
#endif
|
||||||
return rx_cnt;
|
return rx_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) {
|
static void cli_vcp_tx(const uint8_t* buffer, size_t size) {
|
||||||
furi_assert(vcp);
|
|
||||||
return furi_hal_vcp_rx_with_timeout(buffer, size, osWaitForever);
|
|
||||||
}
|
|
||||||
|
|
||||||
void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
|
|
||||||
furi_assert(vcp);
|
furi_assert(vcp);
|
||||||
furi_assert(buffer);
|
furi_assert(buffer);
|
||||||
|
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
if(vcp->running == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "tx %u start", size);
|
FURI_LOG_D(TAG, "tx %u start", size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -264,7 +261,7 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
|
|||||||
|
|
||||||
xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever);
|
xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever);
|
||||||
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx);
|
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx);
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "tx %u", batch_size);
|
FURI_LOG_D(TAG, "tx %u", batch_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -272,11 +269,15 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
|
|||||||
buffer += batch_size;
|
buffer += batch_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FURI_HAL_USB_VCP_DEBUG
|
#ifdef CLI_VCP_DEBUG
|
||||||
FURI_LOG_D(TAG, "tx %u end", size);
|
FURI_LOG_D(TAG, "tx %u end", size);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cli_vcp_tx_stdout(void* _cookie, const char* data, size_t size) {
|
||||||
|
cli_vcp_tx((const uint8_t*)data, size);
|
||||||
|
}
|
||||||
|
|
||||||
static void vcp_state_callback(void* context, uint8_t state) {
|
static void vcp_state_callback(void* context, uint8_t state) {
|
||||||
if(state == 0) {
|
if(state == 0) {
|
||||||
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect);
|
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect);
|
||||||
@ -303,7 +304,16 @@ static void vcp_on_cdc_tx_complete(void* context) {
|
|||||||
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx);
|
osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool furi_hal_vcp_is_connected(void) {
|
static bool cli_vcp_is_connected(void) {
|
||||||
furi_assert(vcp);
|
furi_assert(vcp);
|
||||||
return vcp->connected;
|
return vcp->connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CliSession cli_vcp = {
|
||||||
|
cli_vcp_init,
|
||||||
|
cli_vcp_deinit,
|
||||||
|
cli_vcp_rx,
|
||||||
|
cli_vcp_tx,
|
||||||
|
cli_vcp_tx_stdout,
|
||||||
|
cli_vcp_is_connected,
|
||||||
|
};
|
||||||
18
applications/cli/cli_vcp.h
Normal file
18
applications/cli/cli_vcp.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @file cli_vcp.h
|
||||||
|
* VCP HAL API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cli_i.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern CliSession cli_vcp;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -45,7 +45,7 @@ typedef enum {
|
|||||||
#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
|
#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
|
||||||
|
|
||||||
const NotificationSequence sequence_notification = {
|
const NotificationSequence sequence_notification = {
|
||||||
&message_display_on,
|
&message_display_backlight_on,
|
||||||
&message_green_255,
|
&message_green_255,
|
||||||
&message_delay_10,
|
&message_delay_10,
|
||||||
NULL,
|
NULL,
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
#include "desktop/views/desktop_view_pin_input.h"
|
#include "desktop/views/desktop_view_pin_input.h"
|
||||||
#include "desktop/views/desktop_view_pin_timeout.h"
|
#include "desktop/views/desktop_view_pin_timeout.h"
|
||||||
#include "desktop_i.h"
|
#include "desktop_i.h"
|
||||||
#include "desktop_helpers.h"
|
#include "helpers/pin_lock.h"
|
||||||
|
|
||||||
static void desktop_auto_lock_arm(Desktop*);
|
static void desktop_auto_lock_arm(Desktop*);
|
||||||
static void desktop_auto_lock_inhibit(Desktop*);
|
static void desktop_auto_lock_inhibit(Desktop*);
|
||||||
@ -117,17 +117,18 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void desktop_lock(Desktop* desktop) {
|
void desktop_lock(Desktop* desktop) {
|
||||||
furi_hal_rtc_set_pin_fails(0);
|
|
||||||
desktop_auto_lock_inhibit(desktop);
|
desktop_auto_lock_inhibit(desktop);
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
|
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||||
notification_message(desktop->notification, &sequence_display_off_delay_1000);
|
notification_message(desktop->notification, &sequence_display_backlight_off_delay_1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void desktop_unlock(Desktop* desktop) {
|
void desktop_unlock(Desktop* desktop) {
|
||||||
furi_hal_rtc_set_pin_fails(0);
|
view_port_enabled_set(desktop->lock_viewport, false);
|
||||||
desktop_helpers_unlock_system(desktop);
|
Gui* gui = furi_record_open("gui");
|
||||||
|
gui_set_lockdown(gui, false);
|
||||||
|
furi_record_close("gui");
|
||||||
desktop_view_locked_unlock(desktop->locked_view);
|
desktop_view_locked_unlock(desktop->locked_view);
|
||||||
scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain);
|
scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain);
|
||||||
desktop_auto_lock_arm(desktop);
|
desktop_auto_lock_arm(desktop);
|
||||||
@ -301,18 +302,15 @@ int32_t desktop_srv(void* p) {
|
|||||||
|
|
||||||
bool loaded = LOAD_DESKTOP_SETTINGS(&desktop->settings);
|
bool loaded = LOAD_DESKTOP_SETTINGS(&desktop->settings);
|
||||||
if(!loaded) {
|
if(!loaded) {
|
||||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
|
||||||
memset(&desktop->settings, 0, sizeof(desktop->settings));
|
memset(&desktop->settings, 0, sizeof(desktop->settings));
|
||||||
SAVE_DESKTOP_SETTINGS(&desktop->settings);
|
SAVE_DESKTOP_SETTINGS(&desktop->settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||||
|
|
||||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) && !desktop->settings.pin_code.length) {
|
desktop_pin_lock_init(&desktop->settings);
|
||||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
|
if(!desktop_pin_lock_is_locked()) {
|
||||||
if(!loader_is_locked(desktop->loader)) {
|
if(!loader_is_locked(desktop->loader)) {
|
||||||
desktop_auto_lock_arm(desktop);
|
desktop_auto_lock_arm(desktop);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,82 +0,0 @@
|
|||||||
#include <notification/notification.h>
|
|
||||||
#include <notification/notification_messages.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#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);
|
|
||||||
@ -5,7 +5,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <toolbox/saved_struct.h>
|
#include <toolbox/saved_struct.h>
|
||||||
|
|
||||||
#define DESKTOP_SETTINGS_VER (2)
|
#define DESKTOP_SETTINGS_VER (3)
|
||||||
#define DESKTOP_SETTINGS_PATH "/int/desktop.settings"
|
#define DESKTOP_SETTINGS_PATH "/int/desktop.settings"
|
||||||
#define DESKTOP_SETTINGS_MAGIC (0x17)
|
#define DESKTOP_SETTINGS_MAGIC (0x17)
|
||||||
#define PIN_MAX_LENGTH 12
|
#define PIN_MAX_LENGTH 12
|
||||||
@ -39,17 +39,6 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t favorite;
|
uint16_t favorite;
|
||||||
PinCode pin_code;
|
PinCode pin_code;
|
||||||
|
uint8_t is_locked;
|
||||||
uint32_t auto_lock_delay_ms;
|
uint32_t auto_lock_delay_ms;
|
||||||
} DesktopSettings;
|
} 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;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <furi/check.h>
|
#include <furi/check.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
|
#include "../../helpers/pin_lock.h"
|
||||||
#include "../desktop_settings_app.h"
|
#include "../desktop_settings_app.h"
|
||||||
#include "desktop/desktop_settings/desktop_settings.h"
|
#include "desktop/desktop_settings/desktop_settings.h"
|
||||||
#include "desktop/views/desktop_view_pin_input.h"
|
#include "desktop/views/desktop_view_pin_input.h"
|
||||||
@ -18,7 +18,7 @@ static void pin_auth_done_callback(const PinCode* pin_code, void* context) {
|
|||||||
DesktopSettingsApp* app = context;
|
DesktopSettingsApp* app = context;
|
||||||
|
|
||||||
app->pincode_buffer = *pin_code;
|
app->pincode_buffer = *pin_code;
|
||||||
if(pins_are_equal(&app->settings.pin_code, pin_code)) {
|
if(desktop_pins_are_equal(&app->settings.pin_code, pin_code)) {
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
|
||||||
} else {
|
} else {
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
#include "desktop/views/desktop_view_pin_input.h"
|
#include "desktop/views/desktop_view_pin_input.h"
|
||||||
#include "desktop_settings_scene.h"
|
#include "desktop_settings_scene.h"
|
||||||
#include "desktop_settings_scene_i.h"
|
#include "desktop_settings_scene_i.h"
|
||||||
#include "../../desktop_helpers.h"
|
#include "../../helpers/pin_lock.h"
|
||||||
#include "../desktop_settings_app.h"
|
#include "../desktop_settings_app.h"
|
||||||
|
|
||||||
#define SCENE_EVENT_EXIT (0U)
|
#define SCENE_EVENT_EXIT (0U)
|
||||||
@ -25,7 +25,7 @@ static void pin_error_done_callback(const PinCode* pin_code, void* context) {
|
|||||||
|
|
||||||
void desktop_settings_scene_pin_error_on_enter(void* context) {
|
void desktop_settings_scene_pin_error_on_enter(void* context) {
|
||||||
DesktopSettingsApp* app = context;
|
DesktopSettingsApp* app = context;
|
||||||
desktop_helpers_emit_error_notification();
|
desktop_pin_lock_error_notify();
|
||||||
|
|
||||||
desktop_view_pin_input_set_context(app->pin_input_view, app);
|
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_back_callback(app->pin_input_view, pin_error_back_callback);
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include "desktop/views/desktop_view_pin_input.h"
|
#include "desktop/views/desktop_view_pin_input.h"
|
||||||
#include "desktop_settings_scene.h"
|
#include "desktop_settings_scene.h"
|
||||||
#include "desktop_settings_scene_i.h"
|
#include "desktop_settings_scene_i.h"
|
||||||
|
#include "../../helpers/pin_lock.h"
|
||||||
|
|
||||||
#define SCENE_EVENT_EXIT (0U)
|
#define SCENE_EVENT_EXIT (0U)
|
||||||
#define SCENE_EVENT_1ST_PIN_ENTERED (1U)
|
#define SCENE_EVENT_1ST_PIN_ENTERED (1U)
|
||||||
@ -24,7 +25,7 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
|
|||||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED);
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED);
|
||||||
} else {
|
} else {
|
||||||
app->pincode_buffer_filled = false;
|
app->pincode_buffer_filled = false;
|
||||||
if(pins_are_equal(&app->pincode_buffer, pin_code)) {
|
if(desktop_pins_are_equal(&app->pincode_buffer, pin_code)) {
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
|
||||||
} else {
|
} else {
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
|
||||||
|
|||||||
138
applications/desktop/helpers/pin_lock.c
Normal file
138
applications/desktop/helpers/pin_lock.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
|
||||||
|
#include <notification/notification.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
|
||||||
|
#include "../helpers/pin_lock.h"
|
||||||
|
#include "../desktop_i.h"
|
||||||
|
#include <cli/cli_vcp.h>
|
||||||
|
|
||||||
|
static const NotificationSequence sequence_pin_fail = {
|
||||||
|
&message_display_backlight_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_pin_lock_error_notify() {
|
||||||
|
NotificationApp* notification = furi_record_open("notification");
|
||||||
|
notification_message(notification, &sequence_pin_fail);
|
||||||
|
furi_record_close("notification");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t desktop_pin_lock_get_fail_timeout() {
|
||||||
|
uint32_t pin_fails = furi_hal_rtc_get_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_pin_lock(DesktopSettings* settings) {
|
||||||
|
furi_assert(settings);
|
||||||
|
|
||||||
|
furi_hal_rtc_set_pin_fails(0);
|
||||||
|
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||||
|
Cli* cli = furi_record_open("cli");
|
||||||
|
cli_session_close(cli);
|
||||||
|
furi_record_close("cli");
|
||||||
|
settings->is_locked = 1;
|
||||||
|
SAVE_DESKTOP_SETTINGS(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_pin_unlock(DesktopSettings* settings) {
|
||||||
|
furi_assert(settings);
|
||||||
|
|
||||||
|
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
||||||
|
Cli* cli = furi_record_open("cli");
|
||||||
|
cli_session_open(cli, &cli_vcp);
|
||||||
|
furi_record_close("cli");
|
||||||
|
settings->is_locked = 0;
|
||||||
|
SAVE_DESKTOP_SETTINGS(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_pin_lock_init(DesktopSettings* settings) {
|
||||||
|
furi_assert(settings);
|
||||||
|
|
||||||
|
if(settings->pin_code.length > 0) {
|
||||||
|
if(settings->is_locked == 1) {
|
||||||
|
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||||
|
} else {
|
||||||
|
if(desktop_pin_lock_is_locked()) {
|
||||||
|
settings->is_locked = 1;
|
||||||
|
SAVE_DESKTOP_SETTINGS(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
furi_hal_rtc_set_pin_fails(0);
|
||||||
|
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
||||||
|
furi_hal_usb_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(desktop_pin_lock_is_locked()) {
|
||||||
|
furi_hal_usb_disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) {
|
||||||
|
bool result = false;
|
||||||
|
if(desktop_pins_are_equal(pin_set, pin_entered)) {
|
||||||
|
furi_hal_rtc_set_pin_fails(0);
|
||||||
|
result = true;
|
||||||
|
} else {
|
||||||
|
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
||||||
|
furi_hal_rtc_set_pin_fails(pin_fails + 1);
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_pin_lock_is_locked() {
|
||||||
|
return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_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;
|
||||||
|
}
|
||||||
21
applications/desktop/helpers/pin_lock.h
Normal file
21
applications/desktop/helpers/pin_lock.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../desktop.h"
|
||||||
|
#include "../desktop_settings/desktop_settings.h"
|
||||||
|
|
||||||
|
void desktop_pin_lock_error_notify();
|
||||||
|
|
||||||
|
uint32_t desktop_pin_lock_get_fail_timeout();
|
||||||
|
|
||||||
|
void desktop_pin_lock(DesktopSettings* settings);
|
||||||
|
|
||||||
|
void desktop_pin_unlock(DesktopSettings* settings);
|
||||||
|
|
||||||
|
bool desktop_pin_lock_is_locked();
|
||||||
|
|
||||||
|
void desktop_pin_lock_init(DesktopSettings* settings);
|
||||||
|
|
||||||
|
bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered);
|
||||||
|
|
||||||
|
bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2);
|
||||||
@ -10,6 +10,7 @@
|
|||||||
#include "../views/desktop_view_lock_menu.h"
|
#include "../views/desktop_view_lock_menu.h"
|
||||||
#include "desktop_scene_i.h"
|
#include "desktop_scene_i.h"
|
||||||
#include "desktop_scene.h"
|
#include "desktop_scene.h"
|
||||||
|
#include "../helpers/pin_lock.h"
|
||||||
|
|
||||||
#define TAG "DesktopSceneLock"
|
#define TAG "DesktopSceneLock"
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
break;
|
break;
|
||||||
case DesktopLockMenuEventPinLock:
|
case DesktopLockMenuEventPinLock:
|
||||||
if(desktop->settings.pin_code.length > 0) {
|
if(desktop->settings.pin_code.length > 0) {
|
||||||
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
desktop_pin_lock(&desktop->settings);
|
||||||
desktop_lock(desktop);
|
desktop_lock(desktop);
|
||||||
} else {
|
} else {
|
||||||
LoaderStatus status =
|
LoaderStatus status =
|
||||||
@ -64,7 +65,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
FURI_LOG_E(TAG, "Unable to start desktop settings");
|
FURI_LOG_E(TAG, "Unable to start desktop settings");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case DesktopLockMenuEventExit:
|
case DesktopLockMenuEventExit:
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include "../desktop.h"
|
#include "../desktop.h"
|
||||||
#include "../desktop_i.h"
|
#include "../desktop_i.h"
|
||||||
#include "../desktop_helpers.h"
|
#include "../helpers/pin_lock.h"
|
||||||
#include "../animations/animation_manager.h"
|
#include "../animations/animation_manager.h"
|
||||||
#include "../views/desktop_events.h"
|
#include "../views/desktop_events.h"
|
||||||
#include "../views/desktop_view_pin_input.h"
|
#include "../views/desktop_view_pin_input.h"
|
||||||
@ -45,14 +45,17 @@ void desktop_scene_locked_on_enter(void* context) {
|
|||||||
bool switch_to_timeout_scene = false;
|
bool switch_to_timeout_scene = false;
|
||||||
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked);
|
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked);
|
||||||
if(state == SCENE_LOCKED_FIRST_ENTER) {
|
if(state == SCENE_LOCKED_FIRST_ENTER) {
|
||||||
bool pin_locked = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
|
bool pin_locked = desktop_pin_lock_is_locked();
|
||||||
desktop_helpers_lock_system(desktop, pin_locked);
|
view_port_enabled_set(desktop->lock_viewport, true);
|
||||||
|
Gui* gui = furi_record_open("gui");
|
||||||
|
gui_set_lockdown(gui, true);
|
||||||
|
furi_record_close("gui");
|
||||||
|
|
||||||
if(pin_locked) {
|
if(pin_locked) {
|
||||||
LOAD_DESKTOP_SETTINGS(&desktop->settings);
|
LOAD_DESKTOP_SETTINGS(&desktop->settings);
|
||||||
desktop_view_locked_lock(desktop->locked_view, true);
|
desktop_view_locked_lock(desktop->locked_view, true);
|
||||||
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
uint32_t pin_timeout = desktop_pin_lock_get_fail_timeout();
|
||||||
uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails);
|
if(pin_timeout > 0) {
|
||||||
if(pin_timeout) {
|
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
desktop->scene_manager, DesktopScenePinTimeout, pin_timeout);
|
desktop->scene_manager, DesktopScenePinTimeout, pin_timeout);
|
||||||
switch_to_timeout_scene = true;
|
switch_to_timeout_scene = true;
|
||||||
@ -86,7 +89,7 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
|||||||
break;
|
break;
|
||||||
case DesktopLockedEventUpdate:
|
case DesktopLockedEventUpdate:
|
||||||
if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) {
|
if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) {
|
||||||
notification_message(desktop->notification, &sequence_display_off);
|
notification_message(desktop->notification, &sequence_display_backlight_off);
|
||||||
}
|
}
|
||||||
desktop_view_locked_update(desktop->locked_view);
|
desktop_view_locked_update(desktop->locked_view);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
#include "../animations/animation_manager.h"
|
#include "../animations/animation_manager.h"
|
||||||
#include "../views/desktop_events.h"
|
#include "../views/desktop_events.h"
|
||||||
#include "../views/desktop_view_pin_input.h"
|
#include "../views/desktop_view_pin_input.h"
|
||||||
#include "../desktop_helpers.h"
|
#include "../helpers/pin_lock.h"
|
||||||
#include "desktop_scene.h"
|
#include "desktop_scene.h"
|
||||||
#include "desktop_scene_i.h"
|
#include "desktop_scene_i.h"
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ static void desktop_scene_pin_input_back_callback(void* context) {
|
|||||||
|
|
||||||
static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) {
|
static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
if(pins_are_equal(&desktop->settings.pin_code, pin_code)) {
|
if(desktop_pin_lock_verify(&desktop->settings.pin_code, pin_code)) {
|
||||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked);
|
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked);
|
||||||
} else {
|
} else {
|
||||||
view_dispatcher_send_custom_event(
|
view_dispatcher_send_custom_event(
|
||||||
@ -97,17 +97,14 @@ void desktop_scene_pin_input_on_enter(void* context) {
|
|||||||
bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
|
bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
uint32_t pin_fails = 0;
|
uint32_t pin_timeout = 0;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case DesktopPinInputEventUnlockFailed:
|
case DesktopPinInputEventUnlockFailed:
|
||||||
pin_fails = furi_hal_rtc_get_pin_fails();
|
pin_timeout = desktop_pin_lock_get_fail_timeout();
|
||||||
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) {
|
if(pin_timeout > 0) {
|
||||||
desktop_helpers_emit_error_notification();
|
desktop_pin_lock_error_notify();
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
desktop->scene_manager, DesktopScenePinTimeout, pin_timeout);
|
desktop->scene_manager, DesktopScenePinTimeout, pin_timeout);
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout);
|
scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout);
|
||||||
@ -129,13 +126,14 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
|
|||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case DesktopPinInputEventUnlocked:
|
case DesktopPinInputEventUnlocked:
|
||||||
|
desktop_pin_unlock(&desktop->settings);
|
||||||
desktop_unlock(desktop);
|
desktop_unlock(desktop);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
case DesktopPinInputEventBack:
|
case DesktopPinInputEventBack:
|
||||||
scene_manager_search_and_switch_to_previous_scene(
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
desktop->scene_manager, DesktopSceneLocked);
|
desktop->scene_manager, DesktopSceneLocked);
|
||||||
notification_message(desktop->notification, &sequence_display_off);
|
notification_message(desktop->notification, &sequence_display_backlight_off);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -148,7 +148,7 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) {
|
|||||||
dolphin_deed_get_app_limit(app) - dolphin_state->data.icounter_daily_limit[app];
|
dolphin_deed_get_app_limit(app) - dolphin_state->data.icounter_daily_limit[app];
|
||||||
uint8_t deed_weight = CLAMP(dolphin_deed_get_weight(deed), weight_limit, 0);
|
uint8_t deed_weight = CLAMP(dolphin_deed_get_weight(deed), weight_limit, 0);
|
||||||
|
|
||||||
uint8_t xp_to_levelup = dolphin_state_xp_to_levelup(dolphin_state->data.icounter);
|
uint32_t xp_to_levelup = dolphin_state_xp_to_levelup(dolphin_state->data.icounter);
|
||||||
if(xp_to_levelup) {
|
if(xp_to_levelup) {
|
||||||
deed_weight = MIN(xp_to_levelup, deed_weight);
|
deed_weight = MIN(xp_to_levelup, deed_weight);
|
||||||
dolphin_state->data.icounter += deed_weight;
|
dolphin_state->data.icounter += deed_weight;
|
||||||
|
|||||||
@ -33,7 +33,7 @@ void gpio_scene_usb_uart_on_enter(void* context) {
|
|||||||
gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app);
|
gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app);
|
||||||
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0);
|
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0);
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
|
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
|
||||||
notification_message(app->notifications, &sequence_display_lock);
|
notification_message(app->notifications, &sequence_display_backlight_enforce_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
|
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
|
||||||
@ -63,5 +63,5 @@ void gpio_scene_usb_uart_on_exit(void* context) {
|
|||||||
usb_uart_disable(app->usb_uart_bridge);
|
usb_uart_disable(app->usb_uart_bridge);
|
||||||
free(scene_usb_uart);
|
free(scene_usb_uart);
|
||||||
}
|
}
|
||||||
notification_message(app->notifications, &sequence_display_unlock);
|
notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
#include <stream_buffer.h>
|
#include <stream_buffer.h>
|
||||||
#include <furi_hal_usb_cdc_i.h>
|
#include <furi_hal_usb_cdc_i.h>
|
||||||
#include "usb_cdc.h"
|
#include "usb_cdc.h"
|
||||||
|
#include "cli/cli_vcp.h"
|
||||||
|
#include "cli/cli.h"
|
||||||
|
|
||||||
#define USB_CDC_PKT_LEN CDC_DATA_SZ
|
#define USB_CDC_PKT_LEN CDC_DATA_SZ
|
||||||
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
|
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
|
||||||
@ -16,17 +18,16 @@ static const GpioPin* flow_pins[][2] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WorkerEvtReserved = (1 << 0), // Reserved for StreamBuffer internal event
|
WorkerEvtStop = (1 << 0),
|
||||||
WorkerEvtStop = (1 << 1),
|
WorkerEvtRxDone = (1 << 1),
|
||||||
WorkerEvtRxDone = (1 << 2),
|
|
||||||
|
|
||||||
WorkerEvtTxStop = (1 << 3),
|
WorkerEvtTxStop = (1 << 2),
|
||||||
WorkerEvtCdcRx = (1 << 4),
|
WorkerEvtCdcRx = (1 << 3),
|
||||||
|
|
||||||
WorkerEvtCfgChange = (1 << 5),
|
WorkerEvtCfgChange = (1 << 4),
|
||||||
|
|
||||||
WorkerEvtLineCfgSet = (1 << 6),
|
WorkerEvtLineCfgSet = (1 << 5),
|
||||||
WorkerEvtCtrlLineSet = (1 << 7),
|
WorkerEvtCtrlLineSet = (1 << 6),
|
||||||
|
|
||||||
} WorkerEvtFlags;
|
} WorkerEvtFlags;
|
||||||
|
|
||||||
@ -84,18 +85,29 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
|
|||||||
|
|
||||||
static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
|
static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
|
||||||
furi_hal_usb_unlock();
|
furi_hal_usb_unlock();
|
||||||
|
FURI_LOG_I("", "Init %d", vcp_ch);
|
||||||
if(vcp_ch == 0) {
|
if(vcp_ch == 0) {
|
||||||
|
Cli* cli = furi_record_open("cli");
|
||||||
|
cli_session_close(cli);
|
||||||
|
furi_record_close("cli");
|
||||||
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
|
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
|
||||||
furi_hal_vcp_disable();
|
|
||||||
} else {
|
} else {
|
||||||
furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);
|
furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);
|
||||||
|
Cli* cli = furi_record_open("cli");
|
||||||
|
cli_session_open(cli, &cli_vcp);
|
||||||
|
furi_record_close("cli");
|
||||||
}
|
}
|
||||||
furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart);
|
furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
|
static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
|
||||||
furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL);
|
furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL);
|
||||||
if(vcp_ch == 0) furi_hal_vcp_enable();
|
FURI_LOG_I("", "Deinit %d", vcp_ch);
|
||||||
|
if(vcp_ch != 0) {
|
||||||
|
Cli* cli = furi_record_open("cli");
|
||||||
|
cli_session_close(cli);
|
||||||
|
furi_record_close("cli");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) {
|
static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) {
|
||||||
@ -155,7 +167,6 @@ static int32_t usb_uart_worker(void* context) {
|
|||||||
furi_thread_set_context(usb_uart->tx_thread, usb_uart);
|
furi_thread_set_context(usb_uart->tx_thread, usb_uart);
|
||||||
furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread);
|
furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread);
|
||||||
|
|
||||||
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
|
|
||||||
usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
|
usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
|
||||||
usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
|
usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
|
||||||
usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);
|
usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);
|
||||||
@ -247,11 +258,9 @@ static int32_t usb_uart_worker(void* context) {
|
|||||||
usb_uart_update_ctrl_lines(usb_uart);
|
usb_uart_update_ctrl_lines(usb_uart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
|
usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
|
||||||
usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
|
usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
|
||||||
furi_hal_usb_unlock();
|
|
||||||
furi_hal_usb_set_config(usb_mode_prev, NULL);
|
|
||||||
if(usb_uart->cfg.flow_pins != 0) {
|
if(usb_uart->cfg.flow_pins != 0) {
|
||||||
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);
|
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);
|
||||||
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);
|
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);
|
||||||
@ -265,6 +274,12 @@ static int32_t usb_uart_worker(void* context) {
|
|||||||
osMutexDelete(usb_uart->usb_mutex);
|
osMutexDelete(usb_uart->usb_mutex);
|
||||||
osSemaphoreDelete(usb_uart->tx_sem);
|
osSemaphoreDelete(usb_uart->tx_sem);
|
||||||
|
|
||||||
|
furi_hal_usb_unlock();
|
||||||
|
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
|
||||||
|
Cli* cli = furi_record_open("cli");
|
||||||
|
cli_session_open(cli, &cli_vcp);
|
||||||
|
furi_record_close("cli");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,8 +17,6 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
|
|||||||
Canvas* canvas_init() {
|
Canvas* canvas_init() {
|
||||||
Canvas* canvas = malloc(sizeof(Canvas));
|
Canvas* canvas = malloc(sizeof(Canvas));
|
||||||
|
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
|
|
||||||
// Setup u8g2
|
// Setup u8g2
|
||||||
u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||||
canvas->orientation = CanvasOrientationHorizontal;
|
canvas->orientation = CanvasOrientationHorizontal;
|
||||||
@ -31,8 +29,6 @@ Canvas* canvas_init() {
|
|||||||
canvas_clear(canvas);
|
canvas_clear(canvas);
|
||||||
canvas_commit(canvas);
|
canvas_commit(canvas);
|
||||||
|
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,6 @@ iButtonApp::iButtonApp()
|
|||||||
: notification{"notification"}
|
: notification{"notification"}
|
||||||
, storage{"storage"}
|
, storage{"storage"}
|
||||||
, dialogs{"dialogs"} {
|
, dialogs{"dialogs"} {
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
key = ibutton_key_alloc();
|
key = ibutton_key_alloc();
|
||||||
key_worker = ibutton_worker_alloc();
|
key_worker = ibutton_worker_alloc();
|
||||||
ibutton_worker_start_thread(key_worker);
|
ibutton_worker_start_thread(key_worker);
|
||||||
@ -56,8 +55,6 @@ iButtonApp::~iButtonApp() {
|
|||||||
ibutton_worker_stop_thread(key_worker);
|
ibutton_worker_stop_thread(key_worker);
|
||||||
ibutton_worker_free(key_worker);
|
ibutton_worker_free(key_worker);
|
||||||
ibutton_key_free(key);
|
ibutton_key_free(key);
|
||||||
|
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iButtonAppViewManager* iButtonApp::get_view_manager() {
|
iButtonAppViewManager* iButtonApp::get_view_manager() {
|
||||||
@ -133,18 +130,18 @@ uint8_t iButtonApp::get_file_name_size() {
|
|||||||
return file_name_size;
|
return file_name_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void iButtonApp::notify_green_blink() {
|
void iButtonApp::notify_read() {
|
||||||
notification_message(notification, &sequence_blink_green_10);
|
notification_message(notification, &sequence_blink_cyan_10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void iButtonApp::notify_emulate() {
|
||||||
|
notification_message(notification, &sequence_blink_magenta_10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void iButtonApp::notify_yellow_blink() {
|
void iButtonApp::notify_yellow_blink() {
|
||||||
notification_message(notification, &sequence_blink_yellow_10);
|
notification_message(notification, &sequence_blink_yellow_10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void iButtonApp::notify_red_blink() {
|
|
||||||
notification_message(notification, &sequence_blink_red_10);
|
|
||||||
}
|
|
||||||
|
|
||||||
void iButtonApp::notify_error() {
|
void iButtonApp::notify_error() {
|
||||||
notification_message(notification, &sequence_error);
|
notification_message(notification, &sequence_error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,9 +75,9 @@ public:
|
|||||||
iButtonWorker* get_key_worker();
|
iButtonWorker* get_key_worker();
|
||||||
iButtonKey* get_key();
|
iButtonKey* get_key();
|
||||||
|
|
||||||
void notify_green_blink();
|
void notify_read();
|
||||||
void notify_yellow_blink();
|
void notify_yellow_blink();
|
||||||
void notify_red_blink();
|
void notify_emulate();
|
||||||
|
|
||||||
void notify_error();
|
void notify_error();
|
||||||
void notify_success();
|
void notify_success();
|
||||||
|
|||||||
@ -83,7 +83,7 @@ bool iButtonSceneEmulate::on_event(iButtonApp* app, iButtonEvent* event) {
|
|||||||
app->notify_yellow_blink();
|
app->notify_yellow_blink();
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event->type == iButtonEvent::Type::EventTypeTick) {
|
} else if(event->type == iButtonEvent::Type::EventTypeTick) {
|
||||||
app->notify_red_blink();
|
app->notify_emulate();
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,7 @@ bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) {
|
|||||||
}
|
}
|
||||||
} else if(event->type == iButtonEvent::Type::EventTypeTick) {
|
} else if(event->type == iButtonEvent::Type::EventTypeTick) {
|
||||||
consumed = true;
|
consumed = true;
|
||||||
app->notify_red_blink();
|
app->notify_read();
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
|
|||||||
@ -95,7 +95,7 @@ bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) {
|
|||||||
if(blink_yellow) {
|
if(blink_yellow) {
|
||||||
app->notify_yellow_blink();
|
app->notify_yellow_blink();
|
||||||
} else {
|
} else {
|
||||||
app->notify_red_blink();
|
app->notify_emulate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -211,46 +211,12 @@ void InfraredApp::notify_success() {
|
|||||||
notification_message(notification, &sequence_success);
|
notification_message(notification, &sequence_success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfraredApp::notify_red_blink() {
|
void InfraredApp::notify_blink_read() {
|
||||||
notification_message(notification, &sequence_blink_red_10);
|
notification_message(notification, &sequence_blink_cyan_10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfraredApp::notify_click() {
|
void InfraredApp::notify_blink_send() {
|
||||||
static const NotificationSequence sequence = {
|
notification_message(notification, &sequence_blink_magenta_10);
|
||||||
&message_click,
|
|
||||||
&message_delay_1,
|
|
||||||
&message_sound_off,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
notification_message_block(notification, &sequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InfraredApp::notify_click_and_green_blink() {
|
|
||||||
static const NotificationSequence sequence = {
|
|
||||||
&message_click,
|
|
||||||
&message_delay_1,
|
|
||||||
&message_sound_off,
|
|
||||||
&message_green_255,
|
|
||||||
&message_delay_10,
|
|
||||||
&message_green_0,
|
|
||||||
&message_do_not_reset,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
notification_message_block(notification, &sequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InfraredApp::notify_blink_green() {
|
|
||||||
static const NotificationSequence sequence = {
|
|
||||||
&message_green_255,
|
|
||||||
&message_delay_10,
|
|
||||||
&message_green_0,
|
|
||||||
&message_do_not_reset,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
notification_message(notification, &sequence);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogsApp* InfraredApp::get_dialogs() {
|
DialogsApp* InfraredApp::get_dialogs() {
|
||||||
@ -279,5 +245,5 @@ void InfraredApp::set_received_signal(const InfraredAppSignal& signal) {
|
|||||||
|
|
||||||
void InfraredApp::signal_sent_callback(void* context) {
|
void InfraredApp::signal_sent_callback(void* context) {
|
||||||
InfraredApp* app = static_cast<InfraredApp*>(context);
|
InfraredApp* app = static_cast<InfraredApp*>(context);
|
||||||
app->notify_blink_green();
|
app->notify_blink_send();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -217,17 +217,13 @@ public:
|
|||||||
/** Play success notification */
|
/** Play success notification */
|
||||||
void notify_success();
|
void notify_success();
|
||||||
/** Play red blink notification */
|
/** Play red blink notification */
|
||||||
void notify_red_blink();
|
void notify_blink_read();
|
||||||
/** Light green */
|
/** Light green */
|
||||||
void notify_green_on();
|
void notify_green_on();
|
||||||
/** Disable green light */
|
/** Disable green light */
|
||||||
void notify_green_off();
|
void notify_green_off();
|
||||||
/** Play click sound */
|
/** Blink on send */
|
||||||
void notify_click();
|
void notify_blink_send();
|
||||||
/** Play click and green notification */
|
|
||||||
void notify_click_and_green_blink();
|
|
||||||
/** Blink green light */
|
|
||||||
void notify_blink_green();
|
|
||||||
|
|
||||||
/** Get Dialogs instance */
|
/** Get Dialogs instance */
|
||||||
DialogsApp* get_dialogs();
|
DialogsApp* get_dialogs();
|
||||||
|
|||||||
@ -51,7 +51,7 @@ bool InfraredAppSceneLearn::on_event(InfraredApp* app, InfraredAppEvent* event)
|
|||||||
switch(event->type) {
|
switch(event->type) {
|
||||||
case InfraredAppEvent::Type::Tick:
|
case InfraredAppEvent::Type::Tick:
|
||||||
consumed = true;
|
consumed = true;
|
||||||
app->notify_red_blink();
|
app->notify_blink_read();
|
||||||
break;
|
break;
|
||||||
case InfraredAppEvent::Type::InfraredMessageReceived:
|
case InfraredAppEvent::Type::InfraredMessageReceived:
|
||||||
app->notify_success();
|
app->notify_success();
|
||||||
|
|||||||
@ -95,7 +95,6 @@ bool InfraredAppSceneLearnSuccess::on_event(InfraredApp* app, InfraredAppEvent*
|
|||||||
case DialogExPressCenter:
|
case DialogExPressCenter:
|
||||||
if(!button_pressed) {
|
if(!button_pressed) {
|
||||||
button_pressed = true;
|
button_pressed = true;
|
||||||
app->notify_click_and_green_blink();
|
|
||||||
|
|
||||||
auto signal = app->get_received_signal();
|
auto signal = app->get_received_signal();
|
||||||
if(signal.is_raw()) {
|
if(signal.is_raw()) {
|
||||||
|
|||||||
@ -72,14 +72,12 @@ bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event)
|
|||||||
switch(event->payload.menu_index) {
|
switch(event->payload.menu_index) {
|
||||||
case ButtonIndexPlus:
|
case ButtonIndexPlus:
|
||||||
furi_assert(event->type == InfraredAppEvent::Type::MenuSelected);
|
furi_assert(event->type == InfraredAppEvent::Type::MenuSelected);
|
||||||
app->notify_click();
|
|
||||||
buttonmenu_item_selected = event->payload.menu_index;
|
buttonmenu_item_selected = event->payload.menu_index;
|
||||||
app->set_learn_new_remote(false);
|
app->set_learn_new_remote(false);
|
||||||
app->switch_to_next_scene(InfraredApp::Scene::Learn);
|
app->switch_to_next_scene(InfraredApp::Scene::Learn);
|
||||||
break;
|
break;
|
||||||
case ButtonIndexEdit:
|
case ButtonIndexEdit:
|
||||||
furi_assert(event->type == InfraredAppEvent::Type::MenuSelected);
|
furi_assert(event->type == InfraredAppEvent::Type::MenuSelected);
|
||||||
app->notify_click();
|
|
||||||
buttonmenu_item_selected = event->payload.menu_index;
|
buttonmenu_item_selected = event->payload.menu_index;
|
||||||
app->switch_to_next_scene(InfraredApp::Scene::Edit);
|
app->switch_to_next_scene(InfraredApp::Scene::Edit);
|
||||||
break;
|
break;
|
||||||
@ -89,7 +87,6 @@ bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event)
|
|||||||
|
|
||||||
if(pressed && !button_pressed) {
|
if(pressed && !button_pressed) {
|
||||||
button_pressed = true;
|
button_pressed = true;
|
||||||
app->notify_click_and_green_blink();
|
|
||||||
|
|
||||||
auto button_signal =
|
auto button_signal =
|
||||||
app->get_remote_manager()->get_button_data(event->payload.menu_index);
|
app->get_remote_manager()->get_button_data(event->payload.menu_index);
|
||||||
|
|||||||
@ -56,6 +56,7 @@ bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEven
|
|||||||
if(brute_force_started) {
|
if(brute_force_started) {
|
||||||
if(event->type == InfraredAppEvent::Type::Tick) {
|
if(event->type == InfraredAppEvent::Type::Tick) {
|
||||||
auto view_manager = app->get_view_manager();
|
auto view_manager = app->get_view_manager();
|
||||||
|
app->notify_blink_send();
|
||||||
InfraredAppEvent tick_event = {.type = InfraredAppEvent::Type::Tick};
|
InfraredAppEvent tick_event = {.type = InfraredAppEvent::Type::Tick};
|
||||||
view_manager->send_event(&tick_event);
|
view_manager->send_event(&tick_event);
|
||||||
bool result = brute_force.send_next_bruteforce();
|
bool result = brute_force.send_next_bruteforce();
|
||||||
@ -81,6 +82,7 @@ bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEven
|
|||||||
DOLPHIN_DEED(DolphinDeedIrBruteForce);
|
DOLPHIN_DEED(DolphinDeedIrBruteForce);
|
||||||
brute_force_started = true;
|
brute_force_started = true;
|
||||||
show_popup(app, record_amount);
|
show_popup(app, record_amount);
|
||||||
|
app->notify_blink_send();
|
||||||
} else {
|
} else {
|
||||||
app->switch_to_previous_scene();
|
app->switch_to_previous_scene();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,11 +31,9 @@ LfRfidApp::LfRfidApp()
|
|||||||
, storage{"storage"}
|
, storage{"storage"}
|
||||||
, dialogs{"dialogs"}
|
, dialogs{"dialogs"}
|
||||||
, text_store(40) {
|
, text_store(40) {
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LfRfidApp::~LfRfidApp() {
|
LfRfidApp::~LfRfidApp() {
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LfRfidApp::run(void* _args) {
|
void LfRfidApp::run(void* _args) {
|
||||||
|
|||||||
@ -29,7 +29,7 @@ bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
|||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event->type == LfRfidApp::EventType::Tick) {
|
if(event->type == LfRfidApp::EventType::Tick) {
|
||||||
notification_message(app->notification, &sequence_blink_cyan_10);
|
notification_message(app->notification, &sequence_blink_magenta_10);
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
|
|||||||
@ -24,9 +24,9 @@ bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
|||||||
if(app->worker.any_read()) {
|
if(app->worker.any_read()) {
|
||||||
notification_message(app->notification, &sequence_blink_green_10);
|
notification_message(app->notification, &sequence_blink_green_10);
|
||||||
} else if(app->worker.detect()) {
|
} else if(app->worker.detect()) {
|
||||||
notification_message(app->notification, &sequence_blink_blue_10);
|
notification_message(app->notification, &sequence_blink_cyan_10);
|
||||||
} else {
|
} else {
|
||||||
notification_message(app->notification, &sequence_blink_red_10);
|
notification_message(app->notification, &sequence_blink_cyan_10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
|||||||
|
|
||||||
switch(result) {
|
switch(result) {
|
||||||
case RfidWorker::WriteResult::Nothing:
|
case RfidWorker::WriteResult::Nothing:
|
||||||
notification_message(app->notification, &sequence_blink_yellow_10);
|
notification_message(app->notification, &sequence_blink_magenta_10);
|
||||||
break;
|
break;
|
||||||
case RfidWorker::WriteResult::Ok:
|
case RfidWorker::WriteResult::Ok:
|
||||||
notification_message(app->notification, &sequence_success);
|
notification_message(app->notification, &sequence_success);
|
||||||
@ -51,7 +51,7 @@ bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
|||||||
AlignTop);
|
AlignTop);
|
||||||
card_not_supported = true;
|
card_not_supported = true;
|
||||||
}
|
}
|
||||||
notification_message(app->notification, &sequence_blink_red_10);
|
notification_message(app->notification, &sequence_blink_yellow_10);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -238,7 +238,10 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
|||||||
if(thread_state == FuriThreadStateRunning) {
|
if(thread_state == FuriThreadStateRunning) {
|
||||||
event.type = LoaderEventTypeApplicationStarted;
|
event.type = LoaderEventTypeApplicationStarted;
|
||||||
furi_pubsub_publish(loader_instance->pubsub, &event);
|
furi_pubsub_publish(loader_instance->pubsub, &event);
|
||||||
|
|
||||||
|
if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) {
|
||||||
furi_hal_power_insomnia_enter();
|
furi_hal_power_insomnia_enter();
|
||||||
|
}
|
||||||
} else if(thread_state == FuriThreadStateStopped) {
|
} else if(thread_state == FuriThreadStateStopped) {
|
||||||
FURI_LOG_I(
|
FURI_LOG_I(
|
||||||
TAG,
|
TAG,
|
||||||
@ -251,7 +254,9 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
|||||||
loader_instance->application_arguments = NULL;
|
loader_instance->application_arguments = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) {
|
||||||
furi_hal_power_insomnia_exit();
|
furi_hal_power_insomnia_exit();
|
||||||
|
}
|
||||||
loader_unlock(instance);
|
loader_unlock(instance);
|
||||||
|
|
||||||
event.type = LoaderEventTypeApplicationStopped;
|
event.type = LoaderEventTypeApplicationStopped;
|
||||||
|
|||||||
@ -262,6 +262,7 @@ static bool nfc_device_save_mifare_df_app(FlipperFormat* file, MifareDesfireAppl
|
|||||||
file, app->key_settings, string_get_cstr(prefix)))
|
file, app->key_settings, string_get_cstr(prefix)))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(!app->file_head) break;
|
||||||
uint32_t n_files = 0;
|
uint32_t n_files = 0;
|
||||||
for(MifareDesfireFile* f = app->file_head; f; f = f->next) {
|
for(MifareDesfireFile* f = app->file_head; f; f = f->next) {
|
||||||
n_files++;
|
n_files++;
|
||||||
@ -477,6 +478,7 @@ static bool nfc_device_save_mifare_df_data(FlipperFormat* file, NfcDevice* dev)
|
|||||||
n_apps++;
|
n_apps++;
|
||||||
}
|
}
|
||||||
if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break;
|
if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break;
|
||||||
|
if(n_apps == 0) break;
|
||||||
tmp = malloc(n_apps * 3);
|
tmp = malloc(n_apps * 3);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
|
for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
|
||||||
|
|||||||
@ -86,7 +86,6 @@ void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
|
|||||||
int32_t nfc_worker_task(void* context) {
|
int32_t nfc_worker_task(void* context) {
|
||||||
NfcWorker* nfc_worker = context;
|
NfcWorker* nfc_worker = context;
|
||||||
|
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
furi_hal_nfc_exit_sleep();
|
furi_hal_nfc_exit_sleep();
|
||||||
|
|
||||||
if(nfc_worker->state == NfcWorkerStateDetect) {
|
if(nfc_worker->state == NfcWorkerStateDetect) {
|
||||||
@ -110,7 +109,6 @@ int32_t nfc_worker_task(void* context) {
|
|||||||
}
|
}
|
||||||
furi_hal_nfc_sleep();
|
furi_hal_nfc_sleep();
|
||||||
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
|
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -540,6 +538,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
|
|||||||
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
|
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
|
||||||
free(data->master_key_settings);
|
free(data->master_key_settings);
|
||||||
data->master_key_settings = NULL;
|
data->master_key_settings = NULL;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MifareDesfireKeyVersion** key_version_head =
|
MifareDesfireKeyVersion** key_version_head =
|
||||||
@ -593,6 +592,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
|
|||||||
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
|
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
|
||||||
free(app->key_settings);
|
free(app->key_settings);
|
||||||
app->key_settings = NULL;
|
app->key_settings = NULL;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head;
|
MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head;
|
||||||
|
|||||||
@ -50,9 +50,9 @@ typedef enum {
|
|||||||
|
|
||||||
NotificationMessageTypeDelay,
|
NotificationMessageTypeDelay,
|
||||||
|
|
||||||
NotificationMessageTypeLedDisplay,
|
NotificationMessageTypeLedDisplayBacklight,
|
||||||
NotificationMessageTypeLedDisplayLock,
|
NotificationMessageTypeLedDisplayBacklightEnforceOn,
|
||||||
NotificationMessageTypeLedDisplayUnlock,
|
NotificationMessageTypeLedDisplayBacklightEnforceAuto,
|
||||||
|
|
||||||
NotificationMessageTypeDoNotReset,
|
NotificationMessageTypeDoNotReset,
|
||||||
|
|
||||||
|
|||||||
@ -152,7 +152,7 @@ void notification_sound_off() {
|
|||||||
static void notification_display_timer(void* ctx) {
|
static void notification_display_timer(void* ctx) {
|
||||||
furi_assert(ctx);
|
furi_assert(ctx);
|
||||||
NotificationApp* app = ctx;
|
NotificationApp* app = ctx;
|
||||||
notification_message(app, &sequence_display_off);
|
notification_message(app, &sequence_display_backlight_off);
|
||||||
}
|
}
|
||||||
|
|
||||||
// message processing
|
// message processing
|
||||||
@ -174,7 +174,7 @@ void notification_process_notification_message(
|
|||||||
|
|
||||||
while(notification_message != NULL) {
|
while(notification_message != NULL) {
|
||||||
switch(notification_message->type) {
|
switch(notification_message->type) {
|
||||||
case NotificationMessageTypeLedDisplay:
|
case NotificationMessageTypeLedDisplayBacklight:
|
||||||
// if on - switch on and start timer
|
// if on - switch on and start timer
|
||||||
// if off - switch off and stop timer
|
// if off - switch off and stop timer
|
||||||
// on timer - switch off
|
// on timer - switch off
|
||||||
@ -190,7 +190,7 @@ void notification_process_notification_message(
|
|||||||
}
|
}
|
||||||
reset_mask |= reset_display_mask;
|
reset_mask |= reset_display_mask;
|
||||||
break;
|
break;
|
||||||
case NotificationMessageTypeLedDisplayLock:
|
case NotificationMessageTypeLedDisplayBacklightEnforceOn:
|
||||||
furi_assert(app->display_led_lock < UINT8_MAX);
|
furi_assert(app->display_led_lock < UINT8_MAX);
|
||||||
app->display_led_lock++;
|
app->display_led_lock++;
|
||||||
if(app->display_led_lock == 1) {
|
if(app->display_led_lock == 1) {
|
||||||
@ -199,7 +199,7 @@ void notification_process_notification_message(
|
|||||||
notification_message->data.led.value * display_brightness_setting);
|
notification_message->data.led.value * display_brightness_setting);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotificationMessageTypeLedDisplayUnlock:
|
case NotificationMessageTypeLedDisplayBacklightEnforceAuto:
|
||||||
furi_assert(app->display_led_lock > 0);
|
furi_assert(app->display_led_lock > 0);
|
||||||
app->display_led_lock--;
|
app->display_led_lock--;
|
||||||
if(app->display_led_lock == 0) {
|
if(app->display_led_lock == 0) {
|
||||||
@ -322,7 +322,7 @@ void notification_process_internal_message(NotificationApp* app, NotificationApp
|
|||||||
|
|
||||||
while(notification_message != NULL) {
|
while(notification_message != NULL) {
|
||||||
switch(notification_message->type) {
|
switch(notification_message->type) {
|
||||||
case NotificationMessageTypeLedDisplay:
|
case NotificationMessageTypeLedDisplayBacklight:
|
||||||
notification_apply_internal_led_layer(
|
notification_apply_internal_led_layer(
|
||||||
&app->display,
|
&app->display,
|
||||||
notification_settings_get_display_brightness(
|
notification_settings_get_display_brightness(
|
||||||
@ -442,7 +442,7 @@ static void input_event_callback(const void* value, void* context) {
|
|||||||
furi_assert(value);
|
furi_assert(value);
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
NotificationApp* app = context;
|
NotificationApp* app = context;
|
||||||
notification_message(app, &sequence_display_on);
|
notification_message(app, &sequence_display_backlight_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
// App alloc
|
// App alloc
|
||||||
@ -482,7 +482,7 @@ static NotificationApp* notification_app_alloc() {
|
|||||||
// display backlight control
|
// display backlight control
|
||||||
app->event_record = furi_record_open("input_events");
|
app->event_record = furi_record_open("input_events");
|
||||||
furi_pubsub_subscribe(app->event_record, input_event_callback, app);
|
furi_pubsub_subscribe(app->event_record, input_event_callback, app);
|
||||||
notification_message(app, &sequence_display_on);
|
notification_message(app, &sequence_display_backlight_on);
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,24 +4,27 @@
|
|||||||
|
|
||||||
/*********************************** Messages **********************************/
|
/*********************************** Messages **********************************/
|
||||||
|
|
||||||
// Display
|
/** Display: backlight wakeup */
|
||||||
const NotificationMessage message_display_on = {
|
const NotificationMessage message_display_backlight_on = {
|
||||||
.type = NotificationMessageTypeLedDisplay,
|
.type = NotificationMessageTypeLedDisplayBacklight,
|
||||||
.data.led.value = 0xFF,
|
.data.led.value = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationMessage message_display_off = {
|
/** Display: backlight force off */
|
||||||
.type = NotificationMessageTypeLedDisplay,
|
const NotificationMessage message_display_backlight_off = {
|
||||||
|
.type = NotificationMessageTypeLedDisplayBacklight,
|
||||||
.data.led.value = 0x00,
|
.data.led.value = 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationMessage message_display_lock = {
|
/** Display: backlight always on */
|
||||||
.type = NotificationMessageTypeLedDisplayLock,
|
const NotificationMessage message_display_backlight_enforce_on = {
|
||||||
|
.type = NotificationMessageTypeLedDisplayBacklightEnforceOn,
|
||||||
.data.led.value = 0xFF,
|
.data.led.value = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationMessage message_display_unlock = {
|
/** Display: automatic backlight management, with configured timeout */
|
||||||
.type = NotificationMessageTypeLedDisplayUnlock,
|
const NotificationMessage message_display_backlight_enforce_auto = {
|
||||||
|
.type = NotificationMessageTypeLedDisplayBacklightEnforceAuto,
|
||||||
.data.led.value = 0x00,
|
.data.led.value = 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -166,7 +169,7 @@ const NotificationSequence sequence_reset_rgb = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NotificationSequence sequence_reset_display = {
|
const NotificationSequence sequence_reset_display = {
|
||||||
&message_display_off,
|
&message_display_backlight_off,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -188,29 +191,31 @@ const NotificationSequence sequence_set_vibro_on = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
const NotificationSequence sequence_display_on = {
|
const NotificationSequence sequence_display_backlight_on = {
|
||||||
&message_display_on,
|
&message_display_backlight_on,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationSequence sequence_display_off = {
|
const NotificationSequence sequence_display_backlight_off = {
|
||||||
&message_display_off,
|
&message_display_backlight_off,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationSequence sequence_display_lock = {
|
/** Display: backlight always on lock */
|
||||||
&message_display_lock,
|
const NotificationSequence sequence_display_backlight_enforce_on = {
|
||||||
|
&message_display_backlight_enforce_on,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationSequence sequence_display_unlock = {
|
/** Display: backlight always on unlock */
|
||||||
&message_display_unlock,
|
const NotificationSequence sequence_display_backlight_enforce_auto = {
|
||||||
|
&message_display_backlight_enforce_auto,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationSequence sequence_display_off_delay_1000 = {
|
const NotificationSequence sequence_display_backlight_off_delay_1000 = {
|
||||||
&message_delay_1000,
|
&message_delay_1000,
|
||||||
&message_display_off,
|
&message_display_backlight_off,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -309,6 +314,13 @@ const NotificationSequence sequence_blink_cyan_10 = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const NotificationSequence sequence_blink_magenta_10 = {
|
||||||
|
&message_red_255,
|
||||||
|
&message_blue_255,
|
||||||
|
&message_delay_10,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
const NotificationSequence sequence_blink_red_100 = {
|
const NotificationSequence sequence_blink_red_100 = {
|
||||||
&message_red_255,
|
&message_red_255,
|
||||||
&message_delay_100,
|
&message_delay_100,
|
||||||
@ -376,7 +388,7 @@ const NotificationSequence sequence_double_vibro = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NotificationSequence sequence_success = {
|
const NotificationSequence sequence_success = {
|
||||||
&message_display_on,
|
&message_display_backlight_on,
|
||||||
&message_green_255,
|
&message_green_255,
|
||||||
&message_vibro_on,
|
&message_vibro_on,
|
||||||
&message_note_c5,
|
&message_note_c5,
|
||||||
@ -393,7 +405,7 @@ const NotificationSequence sequence_success = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NotificationSequence sequence_error = {
|
const NotificationSequence sequence_error = {
|
||||||
&message_display_on,
|
&message_display_backlight_on,
|
||||||
&message_red_255,
|
&message_red_255,
|
||||||
&message_vibro_on,
|
&message_vibro_on,
|
||||||
&message_note_c5,
|
&message_note_c5,
|
||||||
@ -415,27 +427,27 @@ const NotificationSequence sequence_audiovisual_alert = {
|
|||||||
&message_force_display_brightness_setting_1f,
|
&message_force_display_brightness_setting_1f,
|
||||||
&message_vibro_on,
|
&message_vibro_on,
|
||||||
|
|
||||||
&message_display_on,
|
&message_display_backlight_on,
|
||||||
&message_note_c7,
|
&message_note_c7,
|
||||||
&message_delay_250,
|
&message_delay_250,
|
||||||
|
|
||||||
&message_display_off,
|
&message_display_backlight_off,
|
||||||
&message_note_c4,
|
&message_note_c4,
|
||||||
&message_delay_250,
|
&message_delay_250,
|
||||||
|
|
||||||
&message_display_on,
|
&message_display_backlight_on,
|
||||||
&message_note_c7,
|
&message_note_c7,
|
||||||
&message_delay_250,
|
&message_delay_250,
|
||||||
|
|
||||||
&message_display_off,
|
&message_display_backlight_off,
|
||||||
&message_note_c4,
|
&message_note_c4,
|
||||||
&message_delay_250,
|
&message_delay_250,
|
||||||
|
|
||||||
&message_display_on,
|
&message_display_backlight_on,
|
||||||
&message_note_c7,
|
&message_note_c7,
|
||||||
&message_delay_250,
|
&message_delay_250,
|
||||||
|
|
||||||
&message_display_off,
|
&message_display_backlight_off,
|
||||||
&message_note_c4,
|
&message_note_c4,
|
||||||
&message_delay_250,
|
&message_delay_250,
|
||||||
|
|
||||||
|
|||||||
@ -9,15 +9,10 @@ extern "C" {
|
|||||||
/*********************************** Messages **********************************/
|
/*********************************** Messages **********************************/
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
|
extern const NotificationMessage message_display_backlight_on;
|
||||||
/** Display: backlight wakeup */
|
extern const NotificationMessage message_display_backlight_off;
|
||||||
extern const NotificationMessage message_display_on;
|
extern const NotificationMessage message_display_backlight_enforce_on;
|
||||||
/** Display: backlight force off */
|
extern const NotificationMessage message_display_backlight_enforce_auto;
|
||||||
extern const NotificationMessage message_display_off;
|
|
||||||
/** Display: backlight always on lock */
|
|
||||||
extern const NotificationMessage message_display_lock;
|
|
||||||
/** Display: backlight always on unlock */
|
|
||||||
extern const NotificationMessage message_display_unlock;
|
|
||||||
|
|
||||||
// Led ON
|
// Led ON
|
||||||
extern const NotificationMessage message_red_255;
|
extern const NotificationMessage message_red_255;
|
||||||
@ -71,15 +66,16 @@ extern const NotificationSequence sequence_set_vibro_on;
|
|||||||
|
|
||||||
// Display
|
// Display
|
||||||
/** Display: backlight wakeup */
|
/** Display: backlight wakeup */
|
||||||
extern const NotificationSequence sequence_display_on;
|
extern const NotificationSequence sequence_display_backlight_on;
|
||||||
/** Display: backlight force off */
|
/** Display: backlight force off */
|
||||||
extern const NotificationSequence sequence_display_off;
|
extern const NotificationSequence sequence_display_backlight_off;
|
||||||
/** Display: backlight always on lock */
|
|
||||||
extern const NotificationSequence sequence_display_lock;
|
|
||||||
/** Display: backlight always on unlock */
|
|
||||||
extern const NotificationSequence sequence_display_unlock;
|
|
||||||
/** Display: backlight force off after a delay of 1000ms */
|
/** Display: backlight force off after a delay of 1000ms */
|
||||||
extern const NotificationSequence sequence_display_off_delay_1000;
|
extern const NotificationSequence sequence_display_backlight_off_delay_1000;
|
||||||
|
|
||||||
|
/** Display: backlight always on lock */
|
||||||
|
extern const NotificationSequence sequence_display_backlight_enforce_on;
|
||||||
|
/** Display: backlight always on unlock */
|
||||||
|
extern const NotificationSequence sequence_display_backlight_enforce_auto;
|
||||||
|
|
||||||
// Charging
|
// Charging
|
||||||
extern const NotificationSequence sequence_charging;
|
extern const NotificationSequence sequence_charging;
|
||||||
@ -100,6 +96,7 @@ extern const NotificationSequence sequence_blink_red_10;
|
|||||||
extern const NotificationSequence sequence_blink_green_10;
|
extern const NotificationSequence sequence_blink_green_10;
|
||||||
extern const NotificationSequence sequence_blink_yellow_10;
|
extern const NotificationSequence sequence_blink_yellow_10;
|
||||||
extern const NotificationSequence sequence_blink_cyan_10;
|
extern const NotificationSequence sequence_blink_cyan_10;
|
||||||
|
extern const NotificationSequence sequence_blink_magenta_10;
|
||||||
|
|
||||||
extern const NotificationSequence sequence_blink_red_100;
|
extern const NotificationSequence sequence_blink_red_100;
|
||||||
extern const NotificationSequence sequence_blink_green_100;
|
extern const NotificationSequence sequence_blink_green_100;
|
||||||
|
|||||||
@ -70,7 +70,7 @@ static void backlight_changed(VariableItem* item) {
|
|||||||
|
|
||||||
variable_item_set_current_value_text(item, backlight_text[index]);
|
variable_item_set_current_value_text(item, backlight_text[index]);
|
||||||
app->notification->settings.display_brightness = backlight_value[index];
|
app->notification->settings.display_brightness = backlight_value[index];
|
||||||
notification_message(app->notification, &sequence_display_on);
|
notification_message(app->notification, &sequence_display_backlight_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screen_changed(VariableItem* item) {
|
static void screen_changed(VariableItem* item) {
|
||||||
@ -79,7 +79,7 @@ static void screen_changed(VariableItem* item) {
|
|||||||
|
|
||||||
variable_item_set_current_value_text(item, delay_text[index]);
|
variable_item_set_current_value_text(item, delay_text[index]);
|
||||||
app->notification->settings.display_off_delay_ms = delay_value[index];
|
app->notification->settings.display_off_delay_ms = delay_value[index];
|
||||||
notification_message(app->notification, &sequence_display_on);
|
notification_message(app->notification, &sequence_display_backlight_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
const NotificationMessage apply_message = {
|
const NotificationMessage apply_message = {
|
||||||
|
|||||||
@ -28,7 +28,7 @@ static void battery_test_battery_info_update_model(void* context) {
|
|||||||
.health = app->info.health,
|
.health = app->info.health,
|
||||||
};
|
};
|
||||||
battery_info_set_data(app->batery_info, &battery_info_data);
|
battery_info_set_data(app->batery_info, &battery_info_data);
|
||||||
notification_message(app->notifications, &sequence_display_on);
|
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
BatteryTestApp* battery_test_alloc() {
|
BatteryTestApp* battery_test_alloc() {
|
||||||
|
|||||||
@ -62,8 +62,8 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) {
|
|||||||
size_t size_received = 0;
|
size_t size_received = 0;
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
size_received = furi_hal_vcp_rx_with_timeout(buffer, CLI_READ_BUFFER_SIZE, 50);
|
size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50);
|
||||||
if(!furi_hal_vcp_is_connected() || cli_rpc.session_close_request) {
|
if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,17 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** File type */
|
||||||
|
typedef enum {
|
||||||
|
FileTypeClosed, /**< Closed file */
|
||||||
|
FileTypeOpenDir, /**< Open dir */
|
||||||
|
FileTypeOpenFile, /**< Open file */
|
||||||
|
} FileType;
|
||||||
|
|
||||||
/** Structure that hold file index and returned api errors */
|
/** Structure that hold file index and returned api errors */
|
||||||
struct File {
|
struct File {
|
||||||
uint32_t file_id; /**< File ID for internal references */
|
uint32_t file_id; /**< File ID for internal references */
|
||||||
|
FileType type;
|
||||||
FS_Error error_id; /**< Standart API error from FS_Error enum */
|
FS_Error error_id; /**< Standart API error from FS_Error enum */
|
||||||
int32_t internal_error_id; /**< Internal API error value */
|
int32_t internal_error_id; /**< Internal API error value */
|
||||||
void* storage;
|
void* storage;
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#define MAX_NAME_LENGTH 256
|
#define MAX_NAME_LENGTH 256
|
||||||
|
|
||||||
|
#define TAG "StorageAPI"
|
||||||
|
|
||||||
#define S_API_PROLOGUE \
|
#define S_API_PROLOGUE \
|
||||||
osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); \
|
osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); \
|
||||||
furi_check(semaphore != NULL);
|
furi_check(semaphore != NULL);
|
||||||
@ -47,10 +49,6 @@
|
|||||||
#define S_RETURN_ERROR (return_data.error_value);
|
#define S_RETURN_ERROR (return_data.error_value);
|
||||||
#define S_RETURN_CSTRING (return_data.cstring_value);
|
#define S_RETURN_CSTRING (return_data.cstring_value);
|
||||||
|
|
||||||
#define FILE_OPENED_FILE 1
|
|
||||||
#define FILE_OPENED_DIR 2
|
|
||||||
#define FILE_CLOSED 0
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
StorageEventFlagFileClose = (1 << 0),
|
StorageEventFlagFileClose = (1 << 0),
|
||||||
} StorageEventFlag;
|
} StorageEventFlag;
|
||||||
@ -72,7 +70,7 @@ static bool storage_file_open_internal(
|
|||||||
.open_mode = open_mode,
|
.open_mode = open_mode,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
file->file_id = FILE_OPENED_FILE;
|
file->type = FileTypeOpenFile;
|
||||||
|
|
||||||
S_API_MESSAGE(StorageCommandFileOpen);
|
S_API_MESSAGE(StorageCommandFileOpen);
|
||||||
S_API_EPILOGUE;
|
S_API_EPILOGUE;
|
||||||
@ -113,6 +111,10 @@ bool storage_file_open(
|
|||||||
|
|
||||||
furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription);
|
furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription);
|
||||||
osEventFlagsDelete(event);
|
osEventFlagsDelete(event);
|
||||||
|
|
||||||
|
FURI_LOG_T(
|
||||||
|
TAG, "File %p - %p open (%s)", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE, path);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +126,8 @@ bool storage_file_close(File* file) {
|
|||||||
S_API_MESSAGE(StorageCommandFileClose);
|
S_API_MESSAGE(StorageCommandFileClose);
|
||||||
S_API_EPILOGUE;
|
S_API_EPILOGUE;
|
||||||
|
|
||||||
file->file_id = FILE_CLOSED;
|
FURI_LOG_T(TAG, "File %p - %p closed", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE);
|
||||||
|
file->type = FileTypeClosed;
|
||||||
|
|
||||||
return S_RETURN_BOOL;
|
return S_RETURN_BOOL;
|
||||||
}
|
}
|
||||||
@ -234,7 +237,7 @@ static bool storage_dir_open_internal(File* file, const char* path) {
|
|||||||
.path = path,
|
.path = path,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
file->file_id = FILE_OPENED_DIR;
|
file->type = FileTypeOpenDir;
|
||||||
|
|
||||||
S_API_MESSAGE(StorageCommandDirOpen);
|
S_API_MESSAGE(StorageCommandDirOpen);
|
||||||
S_API_EPILOGUE;
|
S_API_EPILOGUE;
|
||||||
@ -259,6 +262,10 @@ bool storage_dir_open(File* file, const char* path) {
|
|||||||
|
|
||||||
furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription);
|
furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription);
|
||||||
osEventFlagsDelete(event);
|
osEventFlagsDelete(event);
|
||||||
|
|
||||||
|
FURI_LOG_T(
|
||||||
|
TAG, "Dir %p - %p open (%s)", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE, path);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +276,9 @@ bool storage_dir_close(File* file) {
|
|||||||
S_API_MESSAGE(StorageCommandDirClose);
|
S_API_MESSAGE(StorageCommandDirClose);
|
||||||
S_API_EPILOGUE;
|
S_API_EPILOGUE;
|
||||||
|
|
||||||
file->file_id = FILE_CLOSED;
|
FURI_LOG_T(TAG, "Dir %p - %p closed", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE);
|
||||||
|
|
||||||
|
file->type = FileTypeClosed;
|
||||||
|
|
||||||
return S_RETURN_BOOL;
|
return S_RETURN_BOOL;
|
||||||
}
|
}
|
||||||
@ -448,18 +457,20 @@ FS_Error storage_sd_status(Storage* storage) {
|
|||||||
|
|
||||||
File* storage_file_alloc(Storage* storage) {
|
File* storage_file_alloc(Storage* storage) {
|
||||||
File* file = malloc(sizeof(File));
|
File* file = malloc(sizeof(File));
|
||||||
file->file_id = FILE_CLOSED;
|
file->type = FileTypeClosed;
|
||||||
file->storage = storage;
|
file->storage = storage;
|
||||||
|
|
||||||
|
FURI_LOG_T(TAG, "File/Dir %p alloc", (uint32_t)file - SRAM_BASE);
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool storage_file_is_open(File* file) {
|
bool storage_file_is_open(File* file) {
|
||||||
return (file->file_id != FILE_CLOSED);
|
return (file->type != FileTypeClosed);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool storage_file_is_dir(File* file) {
|
bool storage_file_is_dir(File* file) {
|
||||||
return (file->file_id == FILE_OPENED_DIR);
|
return (file->type == FileTypeOpenDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void storage_file_free(File* file) {
|
void storage_file_free(File* file) {
|
||||||
@ -471,6 +482,7 @@ void storage_file_free(File* file) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FURI_LOG_T(TAG, "File/Dir %p free", (uint32_t)file - SRAM_BASE);
|
||||||
free(file);
|
free(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,12 +39,10 @@ void storage_data_init(StorageData* storage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool storage_data_lock(StorageData* storage) {
|
bool storage_data_lock(StorageData* storage) {
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
return (osMutexAcquire(storage->mutex, osWaitForever) == osOK);
|
return (osMutexAcquire(storage->mutex, osWaitForever) == osOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool storage_data_unlock(StorageData* storage) {
|
bool storage_data_unlock(StorageData* storage) {
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
return (osMutexRelease(storage->mutex) == osOK);
|
return (osMutexRelease(storage->mutex) == osOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,8 @@ struct SubGhzChatWorker {
|
|||||||
volatile bool worker_stoping;
|
volatile bool worker_stoping;
|
||||||
osMessageQueueId_t event_queue;
|
osMessageQueueId_t event_queue;
|
||||||
uint32_t last_time_rx_data;
|
uint32_t last_time_rx_data;
|
||||||
|
|
||||||
|
Cli* cli;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Worker thread
|
/** Worker thread
|
||||||
@ -27,7 +29,7 @@ static int32_t subghz_chat_worker_thread(void* context) {
|
|||||||
event.event = SubGhzChatEventUserEntrance;
|
event.event = SubGhzChatEventUserEntrance;
|
||||||
osMessageQueuePut(instance->event_queue, &event, 0, 0);
|
osMessageQueuePut(instance->event_queue, &event, 0, 0);
|
||||||
while(instance->worker_running) {
|
while(instance->worker_running) {
|
||||||
if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 1000) == 1) {
|
if(cli_read_timeout(instance->cli, (uint8_t*)&c, 1, 1000) == 1) {
|
||||||
event.event = SubGhzChatEventInputData;
|
event.event = SubGhzChatEventInputData;
|
||||||
event.c = c;
|
event.c = c;
|
||||||
osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever);
|
osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever);
|
||||||
@ -52,9 +54,11 @@ static void subghz_chat_worker_update_rx_event_chat(void* context) {
|
|||||||
osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever);
|
osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever);
|
||||||
}
|
}
|
||||||
|
|
||||||
SubGhzChatWorker* subghz_chat_worker_alloc() {
|
SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) {
|
||||||
SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker));
|
SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker));
|
||||||
|
|
||||||
|
instance->cli = cli;
|
||||||
|
|
||||||
instance->thread = furi_thread_alloc();
|
instance->thread = furi_thread_alloc();
|
||||||
furi_thread_set_name(instance->thread, "SubGhzChat");
|
furi_thread_set_name(instance->thread, "SubGhzChat");
|
||||||
furi_thread_set_stack_size(instance->thread, 2048);
|
furi_thread_set_stack_size(instance->thread, 2048);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
#include <cli/cli.h>
|
||||||
|
|
||||||
typedef struct SubGhzChatWorker SubGhzChatWorker;
|
typedef struct SubGhzChatWorker SubGhzChatWorker;
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ typedef struct {
|
|||||||
char c;
|
char c;
|
||||||
} SubGhzChatEvent;
|
} SubGhzChatEvent;
|
||||||
|
|
||||||
SubGhzChatWorker* subghz_chat_worker_alloc();
|
SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli);
|
||||||
void subghz_chat_worker_free(SubGhzChatWorker* instance);
|
void subghz_chat_worker_free(SubGhzChatWorker* instance);
|
||||||
bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency);
|
bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency);
|
||||||
void subghz_chat_worker_stop(SubGhzChatWorker* instance);
|
void subghz_chat_worker_stop(SubGhzChatWorker* instance);
|
||||||
|
|||||||
@ -19,6 +19,8 @@ typedef enum {
|
|||||||
SubGhzCustomEventSceneShowErrorOk,
|
SubGhzCustomEventSceneShowErrorOk,
|
||||||
SubGhzCustomEventSceneShowErrorSub,
|
SubGhzCustomEventSceneShowErrorSub,
|
||||||
SubGhzCustomEventSceneShowOnlyRX,
|
SubGhzCustomEventSceneShowOnlyRX,
|
||||||
|
SubGhzCustomEventSceneAnalyzerLock,
|
||||||
|
SubGhzCustomEventSceneAnalyzerUnlock,
|
||||||
|
|
||||||
SubGhzCustomEventSceneExit,
|
SubGhzCustomEventSceneExit,
|
||||||
SubGhzCustomEventSceneStay,
|
SubGhzCustomEventSceneStay,
|
||||||
@ -35,7 +37,6 @@ typedef enum {
|
|||||||
SubGhzCustomEventViewReadRAWSendStart,
|
SubGhzCustomEventViewReadRAWSendStart,
|
||||||
SubGhzCustomEventViewReadRAWSendStop,
|
SubGhzCustomEventViewReadRAWSendStop,
|
||||||
SubGhzCustomEventViewReadRAWSave,
|
SubGhzCustomEventViewReadRAWSave,
|
||||||
SubGhzCustomEventViewReadRAWVibro,
|
|
||||||
SubGhzCustomEventViewReadRAWTXRXStop,
|
SubGhzCustomEventViewReadRAWTXRXStop,
|
||||||
SubGhzCustomEventViewReadRAWMore,
|
SubGhzCustomEventViewReadRAWMore,
|
||||||
|
|
||||||
|
|||||||
@ -17,10 +17,20 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) {
|
bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) {
|
||||||
//SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubGhzCustomEventSceneAnalyzerLock) {
|
||||||
|
notification_message(subghz->notifications, &sequence_set_green_255);
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) {
|
||||||
|
notification_message(subghz->notifications, &sequence_reset_rgb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_frequency_analyzer_on_exit(void* context) {
|
void subghz_scene_frequency_analyzer_on_exit(void* context) {
|
||||||
// SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
|
notification_message(subghz->notifications, &sequence_reset_rgb);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -168,11 +168,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
|||||||
|
|
||||||
case SubGhzCustomEventViewReadRAWErase:
|
case SubGhzCustomEventViewReadRAWErase:
|
||||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||||
return true;
|
notification_message(subghz->notifications, &sequence_reset_rgb);
|
||||||
break;
|
|
||||||
|
|
||||||
case SubGhzCustomEventViewReadRAWVibro:
|
|
||||||
notification_message(subghz->notifications, &sequence_single_vibro);
|
|
||||||
return true;
|
return true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -209,7 +205,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
|||||||
(SubGhzProtocolEncoderRAW*)subghz->txrx->transmitter->protocol_instance,
|
(SubGhzProtocolEncoderRAW*)subghz->txrx->transmitter->protocol_instance,
|
||||||
subghz_scene_read_raw_callback_end_tx,
|
subghz_scene_read_raw_callback_end_tx,
|
||||||
subghz);
|
subghz);
|
||||||
subghz->state_notifications = SubGhzNotificationStateTX;
|
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,6 +227,10 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
|||||||
subghz_rx_end(subghz);
|
subghz_rx_end(subghz);
|
||||||
subghz_sleep(subghz);
|
subghz_sleep(subghz);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t spl_count = subghz_protocol_raw_get_sample_write(
|
||||||
|
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
|
||||||
|
|
||||||
subghz_protocol_raw_save_to_file_stop(
|
subghz_protocol_raw_save_to_file_stop(
|
||||||
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
|
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
|
||||||
|
|
||||||
@ -241,6 +241,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
|||||||
subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, string_get_cstr(temp_str));
|
subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, string_get_cstr(temp_str));
|
||||||
string_clear(temp_str);
|
string_clear(temp_str);
|
||||||
|
|
||||||
|
if(spl_count > 0) {
|
||||||
|
notification_message(subghz->notifications, &sequence_set_green_255);
|
||||||
|
} else {
|
||||||
|
notification_message(subghz->notifications, &sequence_reset_rgb);
|
||||||
|
}
|
||||||
|
|
||||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
|
||||||
|
|
||||||
@ -263,7 +269,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
|||||||
subghz_begin(subghz, subghz->txrx->preset);
|
subghz_begin(subghz, subghz->txrx->preset);
|
||||||
subghz_rx(subghz, subghz->txrx->frequency);
|
subghz_rx(subghz, subghz->txrx->frequency);
|
||||||
}
|
}
|
||||||
subghz->state_notifications = SubGhzNotificationStateRX;
|
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
|
||||||
} else {
|
} else {
|
||||||
string_set(subghz->error_str, "Function requires\nan SD card.");
|
string_set(subghz->error_str, "Function requires\nan SD card.");
|
||||||
@ -288,16 +294,16 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
|||||||
}
|
}
|
||||||
} else if(event.type == SceneManagerEventTypeTick) {
|
} else if(event.type == SceneManagerEventTypeTick) {
|
||||||
switch(subghz->state_notifications) {
|
switch(subghz->state_notifications) {
|
||||||
case SubGhzNotificationStateRX:
|
case SubGhzNotificationStateRx:
|
||||||
notification_message(subghz->notifications, &sequence_blink_blue_10);
|
notification_message(subghz->notifications, &sequence_blink_cyan_10);
|
||||||
subghz_read_raw_update_sample_write(
|
subghz_read_raw_update_sample_write(
|
||||||
subghz->subghz_read_raw,
|
subghz->subghz_read_raw,
|
||||||
subghz_protocol_raw_get_sample_write(
|
subghz_protocol_raw_get_sample_write(
|
||||||
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result));
|
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result));
|
||||||
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi());
|
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi());
|
||||||
break;
|
break;
|
||||||
case SubGhzNotificationStateTX:
|
case SubGhzNotificationStateTx:
|
||||||
notification_message(subghz->notifications, &sequence_blink_green_10);
|
notification_message(subghz->notifications, &sequence_blink_magenta_10);
|
||||||
subghz_read_raw_update_sin(subghz->subghz_read_raw);
|
subghz_read_raw_update_sin(subghz->subghz_read_raw);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -316,6 +322,7 @@ void subghz_scene_read_raw_on_exit(void* context) {
|
|||||||
subghz_sleep(subghz);
|
subghz_sleep(subghz);
|
||||||
};
|
};
|
||||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||||
|
notification_message(subghz->notifications, &sequence_reset_rgb);
|
||||||
|
|
||||||
//filter restoration
|
//filter restoration
|
||||||
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||||
|
|||||||
@ -1,6 +1,19 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
#include "../views/receiver.h"
|
#include "../views/receiver.h"
|
||||||
|
|
||||||
|
static const NotificationSequence subghs_sequence_rx = {
|
||||||
|
&message_green_255,
|
||||||
|
|
||||||
|
&message_vibro_on,
|
||||||
|
&message_note_c6,
|
||||||
|
&message_delay_50,
|
||||||
|
&message_sound_off,
|
||||||
|
&message_vibro_off,
|
||||||
|
|
||||||
|
&message_delay_50,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
static void subghz_scene_receiver_update_statusbar(void* context) {
|
static void subghz_scene_receiver_update_statusbar(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
string_t history_stat_str;
|
string_t history_stat_str;
|
||||||
@ -50,6 +63,8 @@ static void subghz_scene_add_to_history_callback(
|
|||||||
subghz_receiver_reset(receiver);
|
subghz_receiver_reset(receiver);
|
||||||
string_reset(str_buff);
|
string_reset(str_buff);
|
||||||
|
|
||||||
|
subghz->state_notifications = SubGhzNotificationStateRxDone;
|
||||||
|
|
||||||
subghz_history_get_text_item_menu(
|
subghz_history_get_text_item_menu(
|
||||||
subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1);
|
subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1);
|
||||||
subghz_view_receiver_add_item_to_menu(
|
subghz_view_receiver_add_item_to_menu(
|
||||||
@ -95,7 +110,7 @@ void subghz_scene_receiver_on_enter(void* context) {
|
|||||||
subghz_receiver_set_rx_callback(
|
subghz_receiver_set_rx_callback(
|
||||||
subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz);
|
subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz);
|
||||||
|
|
||||||
subghz->state_notifications = SubGhzNotificationStateRX;
|
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
subghz_rx_end(subghz);
|
subghz_rx_end(subghz);
|
||||||
};
|
};
|
||||||
@ -161,8 +176,12 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch(subghz->state_notifications) {
|
switch(subghz->state_notifications) {
|
||||||
case SubGhzNotificationStateRX:
|
case SubGhzNotificationStateRx:
|
||||||
notification_message(subghz->notifications, &sequence_blink_blue_10);
|
notification_message(subghz->notifications, &sequence_blink_cyan_10);
|
||||||
|
break;
|
||||||
|
case SubGhzNotificationStateRxDone:
|
||||||
|
notification_message(subghz->notifications, &subghs_sequence_rx);
|
||||||
|
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -126,7 +126,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
|||||||
subghz->txrx->history, subghz->txrx->idx_menu_chosen))) {
|
subghz->txrx->history, subghz->txrx->idx_menu_chosen))) {
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
||||||
} else {
|
} else {
|
||||||
subghz->state_notifications = SubGhzNotificationStateTX;
|
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -143,7 +143,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
|||||||
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
|
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
|
||||||
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
|
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
|
||||||
}
|
}
|
||||||
subghz->state_notifications = SubGhzNotificationStateRX;
|
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||||
return true;
|
return true;
|
||||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
|
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
|
||||||
//CC1101 Stop RX -> Save
|
//CC1101 Stop RX -> Save
|
||||||
@ -171,11 +171,15 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
|||||||
subghz_hopper_update(subghz);
|
subghz_hopper_update(subghz);
|
||||||
}
|
}
|
||||||
switch(subghz->state_notifications) {
|
switch(subghz->state_notifications) {
|
||||||
case SubGhzNotificationStateTX:
|
case SubGhzNotificationStateTx:
|
||||||
notification_message(subghz->notifications, &sequence_blink_red_10);
|
notification_message(subghz->notifications, &sequence_blink_magenta_10);
|
||||||
break;
|
break;
|
||||||
case SubGhzNotificationStateRX:
|
case SubGhzNotificationStateRx:
|
||||||
notification_message(subghz->notifications, &sequence_blink_blue_10);
|
notification_message(subghz->notifications, &sequence_blink_cyan_10);
|
||||||
|
break;
|
||||||
|
case SubGhzNotificationStateRxDone:
|
||||||
|
notification_message(subghz->notifications, &sequence_blink_green_100);
|
||||||
|
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
#include <flipper_format/flipper_format_i.h>
|
#include <flipper_format/flipper_format_i.h>
|
||||||
#include <lib/toolbox/stream/stream.h>
|
#include <lib/toolbox/stream/stream.h>
|
||||||
|
#include <lib/subghz/protocols/registry.h>
|
||||||
|
|
||||||
#define TAG "SubGhzSetType"
|
#define TAG "SubGhzSetType"
|
||||||
|
|
||||||
@ -19,13 +20,16 @@ enum SubmenuIndex {
|
|||||||
SubmenuIndexGateTX,
|
SubmenuIndexGateTX,
|
||||||
SubmenuIndexDoorHan_315_00,
|
SubmenuIndexDoorHan_315_00,
|
||||||
SubmenuIndexDoorHan_433_92,
|
SubmenuIndexDoorHan_433_92,
|
||||||
|
SubmenuIndexFirefly_300_00,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool subghz_scene_set_type_submenu_gen_data_protocol(
|
bool subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
void* context,
|
void* context,
|
||||||
const char* protocol_name,
|
const char* protocol_name,
|
||||||
uint64_t key,
|
uint64_t key,
|
||||||
uint32_t bit) {
|
uint32_t bit,
|
||||||
|
uint32_t frequency,
|
||||||
|
FuriHalSubGhzPreset preset) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
@ -44,10 +48,7 @@ bool subghz_scene_set_type_submenu_gen_data_protocol(
|
|||||||
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data);
|
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data);
|
||||||
stream_clean(fff_data_stream);
|
stream_clean(fff_data_stream);
|
||||||
if(!subghz_protocol_decoder_base_serialize(
|
if(!subghz_protocol_decoder_base_serialize(
|
||||||
subghz->txrx->decoder_result,
|
subghz->txrx->decoder_result, subghz->txrx->fff_data, frequency, preset)) {
|
||||||
subghz->txrx->fff_data,
|
|
||||||
subghz_setting_get_default_frequency(subghz->setting),
|
|
||||||
FuriHalSubGhzPresetOok650Async)) {
|
|
||||||
FURI_LOG_E(TAG, "Unable to serialize");
|
FURI_LOG_E(TAG, "Unable to serialize");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -107,6 +108,12 @@ void subghz_scene_set_type_on_enter(void* context) {
|
|||||||
SubmenuIndexCAME24bit,
|
SubmenuIndexCAME24bit,
|
||||||
subghz_scene_set_type_submenu_callback,
|
subghz_scene_set_type_submenu_callback,
|
||||||
subghz);
|
subghz);
|
||||||
|
submenu_add_item(
|
||||||
|
subghz->submenu,
|
||||||
|
"Firefly_300",
|
||||||
|
SubmenuIndexFirefly_300_00,
|
||||||
|
subghz_scene_set_type_submenu_callback,
|
||||||
|
subghz);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
subghz->submenu,
|
subghz->submenu,
|
||||||
"CAME TWEE",
|
"CAME TWEE",
|
||||||
@ -152,7 +159,13 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case SubmenuIndexPricenton:
|
case SubmenuIndexPricenton:
|
||||||
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
||||||
if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "Princeton", key, 24)) {
|
if(subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
|
subghz,
|
||||||
|
SUBGHZ_PROTOCOL_PRINCETON_NAME,
|
||||||
|
key,
|
||||||
|
24,
|
||||||
|
433920000,
|
||||||
|
FuriHalSubGhzPresetOok650Async)) {
|
||||||
uint32_t te = 400;
|
uint32_t te = 400;
|
||||||
flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1);
|
flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1);
|
||||||
generated_protocol = true;
|
generated_protocol = true;
|
||||||
@ -160,32 +173,74 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
break;
|
break;
|
||||||
case SubmenuIndexNiceFlo12bit:
|
case SubmenuIndexNiceFlo12bit:
|
||||||
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
|
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
|
||||||
if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "Nice FLO", key, 12)) {
|
if(subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
|
subghz,
|
||||||
|
SUBGHZ_PROTOCOL_NICE_FLO_NAME,
|
||||||
|
key,
|
||||||
|
12,
|
||||||
|
433920000,
|
||||||
|
FuriHalSubGhzPresetOok650Async)) {
|
||||||
generated_protocol = true;
|
generated_protocol = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SubmenuIndexNiceFlo24bit:
|
case SubmenuIndexNiceFlo24bit:
|
||||||
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
||||||
if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "Nice FLO", key, 24)) {
|
if(subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
|
subghz,
|
||||||
|
SUBGHZ_PROTOCOL_NICE_FLO_NAME,
|
||||||
|
key,
|
||||||
|
24,
|
||||||
|
433920000,
|
||||||
|
FuriHalSubGhzPresetOok650Async)) {
|
||||||
generated_protocol = true;
|
generated_protocol = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SubmenuIndexCAME12bit:
|
case SubmenuIndexCAME12bit:
|
||||||
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
|
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
|
||||||
if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "CAME", key, 12)) {
|
if(subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
|
subghz,
|
||||||
|
SUBGHZ_PROTOCOL_CAME_NAME,
|
||||||
|
key,
|
||||||
|
12,
|
||||||
|
433920000,
|
||||||
|
FuriHalSubGhzPresetOok650Async)) {
|
||||||
generated_protocol = true;
|
generated_protocol = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SubmenuIndexCAME24bit:
|
case SubmenuIndexCAME24bit:
|
||||||
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
||||||
if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "CAME", key, 24)) {
|
if(subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
|
subghz,
|
||||||
|
SUBGHZ_PROTOCOL_CAME_NAME,
|
||||||
|
key,
|
||||||
|
24,
|
||||||
|
433920000,
|
||||||
|
FuriHalSubGhzPresetOok650Async)) {
|
||||||
|
generated_protocol = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SubmenuIndexFirefly_300_00:
|
||||||
|
key = (key & 0x3FF);
|
||||||
|
if(subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
|
subghz,
|
||||||
|
SUBGHZ_PROTOCOL_FIREFLY_NAME,
|
||||||
|
key,
|
||||||
|
10,
|
||||||
|
300000000,
|
||||||
|
FuriHalSubGhzPresetOok650Async)) {
|
||||||
generated_protocol = true;
|
generated_protocol = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SubmenuIndexCAMETwee:
|
case SubmenuIndexCAMETwee:
|
||||||
key = (key & 0x0FFFFFF0);
|
key = (key & 0x0FFFFFF0);
|
||||||
key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE);
|
key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE);
|
||||||
if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "CAME TWEE", key, 54)) {
|
if(subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
|
subghz,
|
||||||
|
SUBGHZ_PROTOCOL_CAME_TWEE_NAME,
|
||||||
|
key,
|
||||||
|
54,
|
||||||
|
433920000,
|
||||||
|
FuriHalSubGhzPresetOok650Async)) {
|
||||||
generated_protocol = true;
|
generated_protocol = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -198,13 +253,19 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
case SubmenuIndexGateTX:
|
case SubmenuIndexGateTX:
|
||||||
key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
|
key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
|
||||||
uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24);
|
uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24);
|
||||||
if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "GateTX", rev_key, 24)) {
|
if(subghz_scene_set_type_submenu_gen_data_protocol(
|
||||||
|
subghz,
|
||||||
|
SUBGHZ_PROTOCOL_GATE_TX_NAME,
|
||||||
|
rev_key,
|
||||||
|
24,
|
||||||
|
433920000,
|
||||||
|
FuriHalSubGhzPresetOok650Async)) {
|
||||||
generated_protocol = true;
|
generated_protocol = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SubmenuIndexDoorHan_433_92:
|
case SubmenuIndexDoorHan_433_92:
|
||||||
subghz->txrx->transmitter =
|
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||||
subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq");
|
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
||||||
if(subghz->txrx->transmitter) {
|
if(subghz->txrx->transmitter) {
|
||||||
subghz_protocol_keeloq_create_data(
|
subghz_protocol_keeloq_create_data(
|
||||||
subghz->txrx->transmitter->protocol_instance,
|
subghz->txrx->transmitter->protocol_instance,
|
||||||
@ -227,8 +288,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SubmenuIndexDoorHan_315_00:
|
case SubmenuIndexDoorHan_315_00:
|
||||||
subghz->txrx->transmitter =
|
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||||
subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq");
|
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
||||||
if(subghz->txrx->transmitter) {
|
if(subghz->txrx->transmitter) {
|
||||||
subghz_protocol_keeloq_create_data(
|
subghz_protocol_keeloq_create_data(
|
||||||
subghz->txrx->transmitter->protocol_instance,
|
subghz->txrx->transmitter->protocol_instance,
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
#include "../helpers/subghz_custom_event.h"
|
#include "../helpers/subghz_custom_event.h"
|
||||||
|
|
||||||
|
static const NotificationSequence subghs_sequence_sd_error = {
|
||||||
|
&message_red_255,
|
||||||
|
&message_green_255,
|
||||||
|
&message_do_not_reset,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
void subghz_scene_show_error_callback(GuiButtonType result, InputType type, void* context) {
|
void subghz_scene_show_error_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
@ -31,6 +38,8 @@ void subghz_scene_show_error_on_enter(void* context) {
|
|||||||
SubGhzCustomEventManagerSet) {
|
SubGhzCustomEventManagerSet) {
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
subghz->widget, GuiButtonTypeRight, "Ok", subghz_scene_show_error_callback, subghz);
|
subghz->widget, GuiButtonTypeRight, "Ok", subghz_scene_show_error_callback, subghz);
|
||||||
|
} else {
|
||||||
|
notification_message(subghz->notifications, &subghs_sequence_sd_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
@ -81,4 +90,5 @@ void subghz_scene_show_error_on_exit(void* context) {
|
|||||||
subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerNoSet);
|
subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerNoSet);
|
||||||
widget_reset(subghz->widget);
|
widget_reset(subghz->widget);
|
||||||
string_reset(subghz->error_str);
|
string_reset(subghz->error_str);
|
||||||
|
notification_message(subghz->notifications, &sequence_reset_rgb);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,8 @@ void subghz_scene_show_error_sub_on_enter(void* context) {
|
|||||||
popup_set_callback(popup, subghz_scene_show_error_sub_popup_callback);
|
popup_set_callback(popup, subghz_scene_show_error_sub_popup_callback);
|
||||||
popup_enable_timeout(popup);
|
popup_enable_timeout(popup);
|
||||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup);
|
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup);
|
||||||
|
|
||||||
|
notification_message(subghz->notifications, &sequence_set_red_255);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event) {
|
bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event) {
|
||||||
@ -45,4 +47,6 @@ void subghz_scene_show_error_sub_on_exit(void* context) {
|
|||||||
popup_set_timeout(popup, 0);
|
popup_set_timeout(popup, 0);
|
||||||
popup_disable_timeout(popup);
|
popup_disable_timeout(popup);
|
||||||
string_reset(subghz->error_str);
|
string_reset(subghz->error_str);
|
||||||
|
|
||||||
|
notification_message(subghz->notifications, &sequence_reset_rgb);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
|
if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
||||||
} else {
|
} else {
|
||||||
subghz->state_notifications = SubGhzNotificationStateTX;
|
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||||
subghz_scene_transmitter_update_data_show(subghz);
|
subghz_scene_transmitter_update_data_show(subghz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,8 +98,8 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
|||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
|
||||||
}
|
}
|
||||||
} else if(event.type == SceneManagerEventTypeTick) {
|
} else if(event.type == SceneManagerEventTypeTick) {
|
||||||
if(subghz->state_notifications == SubGhzNotificationStateTX) {
|
if(subghz->state_notifications == SubGhzNotificationStateTx) {
|
||||||
notification_message(subghz->notifications, &sequence_blink_red_10);
|
notification_message(subghz->notifications, &sequence_blink_magenta_10);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -525,7 +525,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc();
|
SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli);
|
||||||
if(!subghz_chat_worker_start(subghz_chat, frequency)) {
|
if(!subghz_chat_worker_start(subghz_chat, frequency)) {
|
||||||
printf("Startup error SubGhzChatWorker\r\n");
|
printf("Startup error SubGhzChatWorker\r\n");
|
||||||
|
|
||||||
|
|||||||
@ -41,8 +41,9 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
SubGhzNotificationStateStarting,
|
SubGhzNotificationStateStarting,
|
||||||
SubGhzNotificationStateIDLE,
|
SubGhzNotificationStateIDLE,
|
||||||
SubGhzNotificationStateTX,
|
SubGhzNotificationStateTx,
|
||||||
SubGhzNotificationStateRX,
|
SubGhzNotificationStateRx,
|
||||||
|
SubGhzNotificationStateRxDone,
|
||||||
} SubGhzNotificationState;
|
} SubGhzNotificationState;
|
||||||
|
|
||||||
/** SubGhzTxRx state */
|
/** SubGhzTxRx state */
|
||||||
|
|||||||
@ -19,6 +19,7 @@ struct SubGhzFrequencyAnalyzer {
|
|||||||
SubGhzFrequencyAnalyzerWorker* worker;
|
SubGhzFrequencyAnalyzerWorker* worker;
|
||||||
SubGhzFrequencyAnalyzerCallback callback;
|
SubGhzFrequencyAnalyzerCallback callback;
|
||||||
void* context;
|
void* context;
|
||||||
|
bool locked;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -86,6 +87,17 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
|
|||||||
|
|
||||||
void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) {
|
void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) {
|
||||||
SubGhzFrequencyAnalyzer* instance = context;
|
SubGhzFrequencyAnalyzer* instance = context;
|
||||||
|
if((rssi == 0.f) && (instance->locked)) {
|
||||||
|
if(instance->callback) {
|
||||||
|
instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context);
|
||||||
|
}
|
||||||
|
} else if((rssi != 0.f) && (!instance->locked)) {
|
||||||
|
if(instance->callback) {
|
||||||
|
instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->locked = (rssi != 0.f);
|
||||||
with_view_model(
|
with_view_model(
|
||||||
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
|
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
|
||||||
model->rssi = rssi;
|
model->rssi = rssi;
|
||||||
|
|||||||
@ -293,7 +293,6 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
|
|||||||
case SubGhzReadRAWStatusIDLE:
|
case SubGhzReadRAWStatusIDLE:
|
||||||
// Start TX
|
// Start TX
|
||||||
instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);
|
instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);
|
||||||
instance->callback(SubGhzCustomEventViewReadRAWVibro, instance->context);
|
|
||||||
model->satus = SubGhzReadRAWStatusTXRepeat;
|
model->satus = SubGhzReadRAWStatusTXRepeat;
|
||||||
ret = true;
|
ret = true;
|
||||||
break;
|
break;
|
||||||
@ -304,7 +303,6 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
|
|||||||
case SubGhzReadRAWStatusLoadKeyIDLE:
|
case SubGhzReadRAWStatusLoadKeyIDLE:
|
||||||
// Start Load Key TX
|
// Start Load Key TX
|
||||||
instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);
|
instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);
|
||||||
instance->callback(SubGhzCustomEventViewReadRAWVibro, instance->context);
|
|
||||||
model->satus = SubGhzReadRAWStatusLoadKeyTXRepeat;
|
model->satus = SubGhzReadRAWStatusLoadKeyTXRepeat;
|
||||||
ret = true;
|
ret = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -59,18 +59,20 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||||||
u2f_view_set_state(app->u2f_view, U2fMsgRegister);
|
u2f_view_set_state(app->u2f_view, U2fMsgRegister);
|
||||||
else if(event.event == U2fCustomEventAuth)
|
else if(event.event == U2fCustomEventAuth)
|
||||||
u2f_view_set_state(app->u2f_view, U2fMsgAuth);
|
u2f_view_set_state(app->u2f_view, U2fMsgAuth);
|
||||||
notification_message(app->notifications, &sequence_display_on);
|
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||||
notification_message(app->notifications, &sequence_single_vibro);
|
notification_message(app->notifications, &sequence_single_vibro);
|
||||||
}
|
}
|
||||||
notification_message(app->notifications, &sequence_blink_blue_10);
|
notification_message(app->notifications, &sequence_blink_magenta_10);
|
||||||
} else if(event.event == U2fCustomEventWink) {
|
} else if(event.event == U2fCustomEventWink) {
|
||||||
notification_message(app->notifications, &sequence_blink_green_10);
|
notification_message(app->notifications, &sequence_blink_magenta_10);
|
||||||
} else if(event.event == U2fCustomEventAuthSuccess) {
|
} else if(event.event == U2fCustomEventAuthSuccess) {
|
||||||
|
notification_message_block(app->notifications, &sequence_set_green_255);
|
||||||
DOLPHIN_DEED(DolphinDeedU2fAuthorized);
|
DOLPHIN_DEED(DolphinDeedU2fAuthorized);
|
||||||
osTimerStart(app->timer, U2F_SUCCESS_TIMEOUT);
|
osTimerStart(app->timer, U2F_SUCCESS_TIMEOUT);
|
||||||
app->event_cur = U2fCustomEventNone;
|
app->event_cur = U2fCustomEventNone;
|
||||||
u2f_view_set_state(app->u2f_view, U2fMsgSuccess);
|
u2f_view_set_state(app->u2f_view, U2fMsgSuccess);
|
||||||
} else if(event.event == U2fCustomEventTimeout) {
|
} else if(event.event == U2fCustomEventTimeout) {
|
||||||
|
notification_message_block(app->notifications, &sequence_reset_rgb);
|
||||||
app->event_cur = U2fCustomEventNone;
|
app->event_cur = U2fCustomEventNone;
|
||||||
u2f_view_set_state(app->u2f_view, U2fMsgIdle);
|
u2f_view_set_state(app->u2f_view, U2fMsgIdle);
|
||||||
} else if(event.event == U2fCustomEventConfirm) {
|
} else if(event.event == U2fCustomEventConfirm) {
|
||||||
@ -78,6 +80,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||||||
u2f_confirm_user_present(app->u2f_instance);
|
u2f_confirm_user_present(app->u2f_instance);
|
||||||
}
|
}
|
||||||
} else if(event.event == U2fCustomEventDataError) {
|
} else if(event.event == U2fCustomEventDataError) {
|
||||||
|
notification_message(app->notifications, &sequence_set_red_255);
|
||||||
osTimerStop(app->timer);
|
osTimerStop(app->timer);
|
||||||
u2f_view_set_state(app->u2f_view, U2fMsgError);
|
u2f_view_set_state(app->u2f_view, U2fMsgError);
|
||||||
}
|
}
|
||||||
@ -108,6 +111,7 @@ void u2f_scene_main_on_enter(void* context) {
|
|||||||
|
|
||||||
void u2f_scene_main_on_exit(void* context) {
|
void u2f_scene_main_on_exit(void* context) {
|
||||||
U2fApp* app = context;
|
U2fApp* app = context;
|
||||||
|
notification_message_block(app->notifications, &sequence_reset_rgb);
|
||||||
osTimerStop(app->timer);
|
osTimerStop(app->timer);
|
||||||
osTimerDelete(app->timer);
|
osTimerDelete(app->timer);
|
||||||
if(app->u2f_ready == true) {
|
if(app->u2f_ready == true) {
|
||||||
|
|||||||
@ -25,6 +25,7 @@ static void sd_mount_callback(const void* message, void* context) {
|
|||||||
|
|
||||||
void updater_scene_main_on_enter(void* context) {
|
void updater_scene_main_on_enter(void* context) {
|
||||||
Updater* updater = (Updater*)context;
|
Updater* updater = (Updater*)context;
|
||||||
|
notification_message(updater->notification, &sequence_display_backlight_enforce_on);
|
||||||
UpdaterMainView* main_view = updater->main_view;
|
UpdaterMainView* main_view = updater->main_view;
|
||||||
|
|
||||||
FuriPubSubSubscription* sub =
|
FuriPubSubSubscription* sub =
|
||||||
@ -36,9 +37,9 @@ void updater_scene_main_on_enter(void* context) {
|
|||||||
* will be missing from UI, however, /ext will be fully operational. So, until it's fixed, this
|
* will be missing from UI, however, /ext will be fully operational. So, until it's fixed, this
|
||||||
* should remain commented out. */
|
* should remain commented out. */
|
||||||
// If (somehow) we started after SD card is mounted, initiate update immediately
|
// If (somehow) we started after SD card is mounted, initiate update immediately
|
||||||
//if(storage_sd_status(updater->storage) == FSE_OK) {
|
if(storage_sd_status(updater->storage) == FSE_OK) {
|
||||||
// view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);
|
view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);
|
||||||
//}
|
}
|
||||||
|
|
||||||
updater_main_set_view_dispatcher(main_view, updater->view_dispatcher);
|
updater_main_set_view_dispatcher(main_view, updater->view_dispatcher);
|
||||||
view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewMain);
|
view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewMain);
|
||||||
@ -64,13 +65,6 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case UpdaterCustomEventStartUpdate:
|
case UpdaterCustomEventStartUpdate:
|
||||||
if(!update_task_is_running(updater->update_task) &&
|
|
||||||
update_task_init(updater->update_task)) {
|
|
||||||
update_task_start(updater->update_task);
|
|
||||||
}
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UpdaterCustomEventRetryUpdate:
|
case UpdaterCustomEventRetryUpdate:
|
||||||
if(!update_task_is_running(updater->update_task) &&
|
if(!update_task_is_running(updater->update_task) &&
|
||||||
(update_task_get_state(updater->update_task)->stage != UpdateTaskStageCompleted))
|
(update_task_get_state(updater->update_task)->stage != UpdateTaskStageCompleted))
|
||||||
@ -99,6 +93,7 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||||||
void updater_scene_main_on_exit(void* context) {
|
void updater_scene_main_on_exit(void* context) {
|
||||||
Updater* updater = (Updater*)context;
|
Updater* updater = (Updater*)context;
|
||||||
|
|
||||||
|
notification_message(updater->notification, &sequence_display_backlight_enforce_auto);
|
||||||
furi_pubsub_unsubscribe(
|
furi_pubsub_unsubscribe(
|
||||||
storage_get_pubsub(updater->storage), updater_main_get_storage_pubsub(updater->main_view));
|
storage_get_pubsub(updater->storage), updater_main_get_storage_pubsub(updater->main_view));
|
||||||
|
|
||||||
|
|||||||
@ -47,6 +47,7 @@ Updater* updater_alloc(const char* arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updater->storage = furi_record_open("storage");
|
updater->storage = furi_record_open("storage");
|
||||||
|
updater->notification = furi_record_open("notification");
|
||||||
|
|
||||||
updater->gui = furi_record_open("gui");
|
updater->gui = furi_record_open("gui");
|
||||||
updater->view_dispatcher = view_dispatcher_alloc();
|
updater->view_dispatcher = view_dispatcher_alloc();
|
||||||
@ -119,6 +120,7 @@ void updater_free(Updater* updater) {
|
|||||||
|
|
||||||
furi_record_close("gui");
|
furi_record_close("gui");
|
||||||
furi_record_close("storage");
|
furi_record_close("storage");
|
||||||
|
furi_record_close("notification");
|
||||||
|
|
||||||
free(updater);
|
free(updater);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <gui/modules/widget.h>
|
#include <gui/modules/widget.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
|
#include <notification/notification_app.h>
|
||||||
#include <update_util/update_operation.h>
|
#include <update_util/update_operation.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -41,6 +42,7 @@ typedef struct UpdaterManifestProcessingState {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
// GUI
|
// GUI
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
|
NotificationApp* notification;
|
||||||
SceneManager* scene_manager;
|
SceneManager* scene_manager;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
Storage* storage;
|
Storage* storage;
|
||||||
|
|||||||
@ -15,13 +15,18 @@ static const char* update_task_stage_descr[] = {
|
|||||||
[UpdateTaskStageValidateDFUImage] = "Checking DFU file",
|
[UpdateTaskStageValidateDFUImage] = "Checking DFU file",
|
||||||
[UpdateTaskStageFlashWrite] = "Writing flash",
|
[UpdateTaskStageFlashWrite] = "Writing flash",
|
||||||
[UpdateTaskStageFlashValidate] = "Validating",
|
[UpdateTaskStageFlashValidate] = "Validating",
|
||||||
|
[UpdateTaskStageRadioImageValidate] = "Checking radio image",
|
||||||
|
[UpdateTaskStageRadioErase] = "Removing radio stack",
|
||||||
[UpdateTaskStageRadioWrite] = "Writing radio stack",
|
[UpdateTaskStageRadioWrite] = "Writing radio stack",
|
||||||
[UpdateTaskStageRadioCommit] = "Applying radio stack",
|
[UpdateTaskStageRadioInstall] = "Installing radio stack",
|
||||||
|
[UpdateTaskStageRadioBusy] = "Core2 is updating",
|
||||||
|
[UpdateTaskStageOBValidation] = "Validating opt. bytes",
|
||||||
[UpdateTaskStageLfsBackup] = "Backing up LFS",
|
[UpdateTaskStageLfsBackup] = "Backing up LFS",
|
||||||
[UpdateTaskStageLfsRestore] = "Restoring LFS",
|
[UpdateTaskStageLfsRestore] = "Restoring LFS",
|
||||||
[UpdateTaskStageResourcesUpdate] = "Updating resources",
|
[UpdateTaskStageResourcesUpdate] = "Updating resources",
|
||||||
[UpdateTaskStageCompleted] = "Completed!",
|
[UpdateTaskStageCompleted] = "Completed!",
|
||||||
[UpdateTaskStageError] = "Error",
|
[UpdateTaskStageError] = "Error",
|
||||||
|
[UpdateTaskStageOBError] = "OB error, pls report",
|
||||||
};
|
};
|
||||||
|
|
||||||
static void update_task_set_status(UpdateTask* update_task, const char* status) {
|
static void update_task_set_status(UpdateTask* update_task, const char* status) {
|
||||||
@ -37,7 +42,10 @@ static void update_task_set_status(UpdateTask* update_task, const char* status)
|
|||||||
|
|
||||||
void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) {
|
void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) {
|
||||||
if(stage != UpdateTaskStageProgress) {
|
if(stage != UpdateTaskStageProgress) {
|
||||||
|
// do not override more specific error states
|
||||||
|
if((update_task->state.stage < UpdateTaskStageError) || (stage < UpdateTaskStageError)) {
|
||||||
update_task->state.stage = stage;
|
update_task->state.stage = stage;
|
||||||
|
}
|
||||||
update_task->state.current_stage_idx++;
|
update_task->state.current_stage_idx++;
|
||||||
update_task_set_status(update_task, NULL);
|
update_task_set_status(update_task, NULL);
|
||||||
}
|
}
|
||||||
@ -53,7 +61,7 @@ void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, ui
|
|||||||
progress,
|
progress,
|
||||||
update_task->state.current_stage_idx,
|
update_task->state.current_stage_idx,
|
||||||
update_task->state.total_stages,
|
update_task->state.total_stages,
|
||||||
update_task->state.stage == UpdateTaskStageError,
|
update_task->state.stage >= UpdateTaskStageError,
|
||||||
update_task->status_change_cb_state);
|
update_task->status_change_cb_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,6 +124,7 @@ UpdateTask* update_task_alloc() {
|
|||||||
update_task->storage = furi_record_open("storage");
|
update_task->storage = furi_record_open("storage");
|
||||||
update_task->file = storage_file_alloc(update_task->storage);
|
update_task->file = storage_file_alloc(update_task->storage);
|
||||||
update_task->status_change_cb = NULL;
|
update_task->status_change_cb = NULL;
|
||||||
|
string_init(update_task->update_path);
|
||||||
|
|
||||||
FuriThread* thread = update_task->thread = furi_thread_alloc();
|
FuriThread* thread = update_task->thread = furi_thread_alloc();
|
||||||
|
|
||||||
@ -152,12 +161,6 @@ void update_task_free(UpdateTask* update_task) {
|
|||||||
free(update_task);
|
free(update_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool update_task_init(UpdateTask* update_task) {
|
|
||||||
furi_assert(update_task);
|
|
||||||
string_init(update_task->update_path);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool update_task_parse_manifest(UpdateTask* update_task) {
|
bool update_task_parse_manifest(UpdateTask* update_task) {
|
||||||
furi_assert(update_task);
|
furi_assert(update_task);
|
||||||
update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0);
|
update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0);
|
||||||
|
|||||||
@ -19,13 +19,18 @@ typedef enum {
|
|||||||
UpdateTaskStageValidateDFUImage,
|
UpdateTaskStageValidateDFUImage,
|
||||||
UpdateTaskStageFlashWrite,
|
UpdateTaskStageFlashWrite,
|
||||||
UpdateTaskStageFlashValidate,
|
UpdateTaskStageFlashValidate,
|
||||||
|
UpdateTaskStageRadioImageValidate,
|
||||||
|
UpdateTaskStageRadioErase,
|
||||||
UpdateTaskStageRadioWrite,
|
UpdateTaskStageRadioWrite,
|
||||||
UpdateTaskStageRadioCommit,
|
UpdateTaskStageRadioInstall,
|
||||||
|
UpdateTaskStageRadioBusy,
|
||||||
|
UpdateTaskStageOBValidation,
|
||||||
UpdateTaskStageLfsBackup,
|
UpdateTaskStageLfsBackup,
|
||||||
UpdateTaskStageLfsRestore,
|
UpdateTaskStageLfsRestore,
|
||||||
UpdateTaskStageResourcesUpdate,
|
UpdateTaskStageResourcesUpdate,
|
||||||
UpdateTaskStageCompleted,
|
UpdateTaskStageCompleted,
|
||||||
UpdateTaskStageError,
|
UpdateTaskStageError,
|
||||||
|
UpdateTaskStageOBError
|
||||||
} UpdateTaskStage;
|
} UpdateTaskStage;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -50,8 +55,6 @@ UpdateTask* update_task_alloc();
|
|||||||
|
|
||||||
void update_task_free(UpdateTask* update_task);
|
void update_task_free(UpdateTask* update_task);
|
||||||
|
|
||||||
bool update_task_init(UpdateTask* update_task);
|
|
||||||
|
|
||||||
void update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state);
|
void update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state);
|
||||||
|
|
||||||
bool update_task_start(UpdateTask* update_task);
|
bool update_task_start(UpdateTask* update_task);
|
||||||
|
|||||||
@ -9,99 +9,17 @@
|
|||||||
#include <update_util/lfs_backup.h>
|
#include <update_util/lfs_backup.h>
|
||||||
#include <update_util/update_operation.h>
|
#include <update_util/update_operation.h>
|
||||||
#include <toolbox/tar/tar_archive.h>
|
#include <toolbox/tar/tar_archive.h>
|
||||||
|
#include <toolbox/crc32_calc.h>
|
||||||
|
|
||||||
|
#define TAG "UpdWorkerBackup"
|
||||||
|
|
||||||
#define CHECK_RESULT(x) \
|
#define CHECK_RESULT(x) \
|
||||||
if(!(x)) { \
|
if(!(x)) { \
|
||||||
break; \
|
break; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define STM_DFU_VENDOR_ID 0x0483
|
|
||||||
#define STM_DFU_PRODUCT_ID 0xDF11
|
|
||||||
/* Written into DFU file by build pipeline */
|
|
||||||
#define FLIPPER_ZERO_DFU_DEVICE_CODE 0xFFFF
|
|
||||||
|
|
||||||
#define EXT_PATH "/ext"
|
#define EXT_PATH "/ext"
|
||||||
|
|
||||||
static const DfuValidationParams flipper_dfu_params = {
|
|
||||||
.device = FLIPPER_ZERO_DFU_DEVICE_CODE,
|
|
||||||
.product = STM_DFU_PRODUCT_ID,
|
|
||||||
.vendor = STM_DFU_VENDOR_ID,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void update_task_dfu_progress(const uint8_t progress, void* context) {
|
|
||||||
UpdateTask* update_task = context;
|
|
||||||
update_task_set_progress(update_task, UpdateTaskStageProgress, progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool page_task_compare_flash(
|
|
||||||
const uint8_t i_page,
|
|
||||||
const uint8_t* update_block,
|
|
||||||
uint16_t update_block_len) {
|
|
||||||
const size_t page_addr = furi_hal_flash_get_base() + furi_hal_flash_get_page_size() * i_page;
|
|
||||||
return (memcmp(update_block, (void*)page_addr, update_block_len) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verifies a flash operation address for fitting into writable memory
|
|
||||||
*/
|
|
||||||
static bool check_address_boundaries(const size_t address) {
|
|
||||||
const size_t min_allowed_address = furi_hal_flash_get_base();
|
|
||||||
const size_t max_allowed_address = (size_t)furi_hal_flash_get_free_end_address();
|
|
||||||
return ((address >= min_allowed_address) && (address < max_allowed_address));
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t update_task_worker_flash_writer(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
UpdateTask* update_task = context;
|
|
||||||
bool success = false;
|
|
||||||
DfuUpdateTask page_task = {
|
|
||||||
.address_cb = &check_address_boundaries,
|
|
||||||
.progress_cb = &update_task_dfu_progress,
|
|
||||||
.task_cb = &furi_hal_flash_program_page,
|
|
||||||
.context = update_task,
|
|
||||||
};
|
|
||||||
|
|
||||||
update_task->state.current_stage_idx = 0;
|
|
||||||
update_task->state.total_stages = 4;
|
|
||||||
|
|
||||||
do {
|
|
||||||
CHECK_RESULT(update_task_parse_manifest(update_task));
|
|
||||||
|
|
||||||
if(!string_empty_p(update_task->manifest->firmware_dfu_image)) {
|
|
||||||
update_task_set_progress(update_task, UpdateTaskStageValidateDFUImage, 0);
|
|
||||||
CHECK_RESULT(
|
|
||||||
update_task_open_file(update_task, update_task->manifest->firmware_dfu_image));
|
|
||||||
CHECK_RESULT(
|
|
||||||
dfu_file_validate_crc(update_task->file, &update_task_dfu_progress, update_task));
|
|
||||||
|
|
||||||
const uint8_t valid_targets =
|
|
||||||
dfu_file_validate_headers(update_task->file, &flipper_dfu_params);
|
|
||||||
if(valid_targets == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_task_set_progress(update_task, UpdateTaskStageFlashWrite, 0);
|
|
||||||
CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets));
|
|
||||||
|
|
||||||
page_task.task_cb = &page_task_compare_flash;
|
|
||||||
|
|
||||||
update_task_set_progress(update_task, UpdateTaskStageFlashValidate, 0);
|
|
||||||
CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets));
|
|
||||||
}
|
|
||||||
|
|
||||||
update_task_set_progress(update_task, UpdateTaskStageCompleted, 100);
|
|
||||||
|
|
||||||
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate);
|
|
||||||
|
|
||||||
success = true;
|
|
||||||
} while(false);
|
|
||||||
|
|
||||||
if(!success) {
|
|
||||||
update_task_set_progress(update_task, UpdateTaskStageError, update_task->state.progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
return success ? UPDATE_TASK_NOERR : UPDATE_TASK_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool update_task_pre_update(UpdateTask* update_task) {
|
static bool update_task_pre_update(UpdateTask* update_task) {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
string_t backup_file_path;
|
string_t backup_file_path;
|
||||||
@ -111,7 +29,8 @@ static bool update_task_pre_update(UpdateTask* update_task) {
|
|||||||
|
|
||||||
update_task->state.total_stages = 1;
|
update_task->state.total_stages = 1;
|
||||||
update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0);
|
update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0);
|
||||||
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); // to avoid bootloops
|
/* to avoid bootloops */
|
||||||
|
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
|
||||||
if((success = lfs_backup_create(update_task->storage, string_get_cstr(backup_file_path)))) {
|
if((success = lfs_backup_create(update_task->storage, string_get_cstr(backup_file_path)))) {
|
||||||
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate);
|
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate);
|
||||||
}
|
}
|
||||||
@ -143,7 +62,8 @@ static bool update_task_post_update(UpdateTask* update_task) {
|
|||||||
string_t file_path;
|
string_t file_path;
|
||||||
string_init(file_path);
|
string_init(file_path);
|
||||||
|
|
||||||
update_task->state.total_stages = 2;
|
/* status text is too long, too few stages to bother with a counter */
|
||||||
|
update_task->state.total_stages = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
CHECK_RESULT(update_task_parse_manifest(update_task));
|
CHECK_RESULT(update_task_parse_manifest(update_task));
|
||||||
@ -151,9 +71,6 @@ static bool update_task_post_update(UpdateTask* update_task) {
|
|||||||
string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path);
|
string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path);
|
||||||
|
|
||||||
bool unpack_resources = !string_empty_p(update_task->manifest->resource_bundle);
|
bool unpack_resources = !string_empty_p(update_task->manifest->resource_bundle);
|
||||||
if(unpack_resources) {
|
|
||||||
update_task->state.total_stages++;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0);
|
update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0);
|
||||||
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
|
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
|
||||||
@ -184,6 +101,7 @@ static bool update_task_post_update(UpdateTask* update_task) {
|
|||||||
}
|
}
|
||||||
tar_archive_free(archive);
|
tar_archive_free(archive);
|
||||||
}
|
}
|
||||||
|
success = true;
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
string_clear(file_path);
|
string_clear(file_path);
|
||||||
@ -197,7 +115,7 @@ int32_t update_task_worker_backup_restore(void* context) {
|
|||||||
|
|
||||||
FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode();
|
FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode();
|
||||||
if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) {
|
if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) {
|
||||||
// no idea how we got here. Clear to normal boot
|
/* no idea how we got here. Clear to normal boot */
|
||||||
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
|
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
|
||||||
return UPDATE_TASK_NOERR;
|
return UPDATE_TASK_NOERR;
|
||||||
}
|
}
|
||||||
@ -208,12 +126,17 @@ int32_t update_task_worker_backup_restore(void* context) {
|
|||||||
return UPDATE_TASK_FAILED;
|
return UPDATE_TASK_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Waiting for BT service to 'start', so we don't race for boot mode */
|
||||||
|
furi_record_open("bt");
|
||||||
|
|
||||||
if(boot_mode == FuriHalRtcBootModePreUpdate) {
|
if(boot_mode == FuriHalRtcBootModePreUpdate) {
|
||||||
success = update_task_pre_update(update_task);
|
success = update_task_pre_update(update_task);
|
||||||
} else if(boot_mode == FuriHalRtcBootModePostUpdate) {
|
} else if(boot_mode == FuriHalRtcBootModePostUpdate) {
|
||||||
success = update_task_post_update(update_task);
|
success = update_task_post_update(update_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
furi_record_close("bt");
|
||||||
|
|
||||||
if(success) {
|
if(success) {
|
||||||
update_task_set_progress(update_task, UpdateTaskStageCompleted, 100);
|
update_task_set_progress(update_task, UpdateTaskStageCompleted, 100);
|
||||||
} else {
|
} else {
|
||||||
363
applications/updater/util/update_task_worker_flasher.c
Normal file
363
applications/updater/util/update_task_worker_flasher.c
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
#include "update_task.h"
|
||||||
|
#include "update_task_i.h"
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <storage/storage.h>
|
||||||
|
#include <toolbox/path.h>
|
||||||
|
#include <update_util/dfu_file.h>
|
||||||
|
#include <update_util/lfs_backup.h>
|
||||||
|
#include <update_util/update_operation.h>
|
||||||
|
#include <toolbox/tar/tar_archive.h>
|
||||||
|
#include <toolbox/crc32_calc.h>
|
||||||
|
|
||||||
|
#define TAG "UpdWorkerRAM"
|
||||||
|
|
||||||
|
#define CHECK_RESULT(x) \
|
||||||
|
if(!(x)) { \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STM_DFU_VENDOR_ID 0x0483
|
||||||
|
#define STM_DFU_PRODUCT_ID 0xDF11
|
||||||
|
/* Written into DFU file by build pipeline */
|
||||||
|
#define FLIPPER_ZERO_DFU_DEVICE_CODE 0xFFFF
|
||||||
|
/* Time, in ms, to wait for system restart by C2 before crashing */
|
||||||
|
#define C2_MODE_SWITCH_TIMEOUT 10000
|
||||||
|
|
||||||
|
static const DfuValidationParams flipper_dfu_params = {
|
||||||
|
.device = FLIPPER_ZERO_DFU_DEVICE_CODE,
|
||||||
|
.product = STM_DFU_PRODUCT_ID,
|
||||||
|
.vendor = STM_DFU_VENDOR_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void update_task_file_progress(const uint8_t progress, void* context) {
|
||||||
|
UpdateTask* update_task = context;
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageProgress, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool page_task_compare_flash(
|
||||||
|
const uint8_t i_page,
|
||||||
|
const uint8_t* update_block,
|
||||||
|
uint16_t update_block_len) {
|
||||||
|
const size_t page_addr = furi_hal_flash_get_base() + furi_hal_flash_get_page_size() * i_page;
|
||||||
|
return (memcmp(update_block, (void*)page_addr, update_block_len) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verifies a flash operation address for fitting into writable memory
|
||||||
|
*/
|
||||||
|
static bool check_address_boundaries(const size_t address) {
|
||||||
|
const size_t min_allowed_address = furi_hal_flash_get_base();
|
||||||
|
const size_t max_allowed_address = (size_t)furi_hal_flash_get_free_end_address();
|
||||||
|
return ((address >= min_allowed_address) && (address < max_allowed_address));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool update_task_write_dfu(UpdateTask* update_task) {
|
||||||
|
DfuUpdateTask page_task = {
|
||||||
|
.address_cb = &check_address_boundaries,
|
||||||
|
.progress_cb = &update_task_file_progress,
|
||||||
|
.task_cb = &furi_hal_flash_program_page,
|
||||||
|
.context = update_task,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
do {
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageValidateDFUImage, 0);
|
||||||
|
CHECK_RESULT(
|
||||||
|
update_task_open_file(update_task, update_task->manifest->firmware_dfu_image));
|
||||||
|
CHECK_RESULT(
|
||||||
|
dfu_file_validate_crc(update_task->file, &update_task_file_progress, update_task));
|
||||||
|
|
||||||
|
const uint8_t valid_targets =
|
||||||
|
dfu_file_validate_headers(update_task->file, &flipper_dfu_params);
|
||||||
|
if(valid_targets == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageFlashWrite, 0);
|
||||||
|
CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets));
|
||||||
|
|
||||||
|
page_task.task_cb = &page_task_compare_flash;
|
||||||
|
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageFlashValidate, 0);
|
||||||
|
CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets));
|
||||||
|
success = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool update_task_write_stack_data(UpdateTask* update_task) {
|
||||||
|
furi_check(storage_file_is_open(update_task->file));
|
||||||
|
const size_t FLASH_PAGE_SIZE = furi_hal_flash_get_page_size();
|
||||||
|
|
||||||
|
uint32_t stack_size = storage_file_size(update_task->file);
|
||||||
|
storage_file_seek(update_task->file, 0, true);
|
||||||
|
|
||||||
|
if(!check_address_boundaries(update_task->manifest->radio_address) ||
|
||||||
|
!check_address_boundaries(update_task->manifest->radio_address + stack_size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioWrite, 0);
|
||||||
|
uint8_t* fw_block = malloc(FLASH_PAGE_SIZE);
|
||||||
|
uint16_t bytes_read = 0;
|
||||||
|
uint32_t element_offs = 0;
|
||||||
|
|
||||||
|
while(element_offs < stack_size) {
|
||||||
|
uint32_t n_bytes_to_read = FLASH_PAGE_SIZE;
|
||||||
|
if((element_offs + n_bytes_to_read) > stack_size) {
|
||||||
|
n_bytes_to_read = stack_size - element_offs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_read = storage_file_read(update_task->file, fw_block, n_bytes_to_read);
|
||||||
|
if(bytes_read == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t i_page =
|
||||||
|
furi_hal_flash_get_page_number(update_task->manifest->radio_address + element_offs);
|
||||||
|
if(i_page < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!furi_hal_flash_program_page(i_page, fw_block, bytes_read)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
element_offs += bytes_read;
|
||||||
|
update_task_set_progress(
|
||||||
|
update_task, UpdateTaskStageProgress, element_offs * 100 / stack_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(fw_block);
|
||||||
|
return element_offs == stack_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_task_wait_for_restart(UpdateTask* update_task) {
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10);
|
||||||
|
osDelay(C2_MODE_SWITCH_TIMEOUT);
|
||||||
|
furi_crash("C2 timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool update_task_write_stack(UpdateTask* update_task) {
|
||||||
|
bool success = false;
|
||||||
|
do {
|
||||||
|
FURI_LOG_W(TAG, "Writing stack");
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioImageValidate, 0);
|
||||||
|
CHECK_RESULT(update_task_open_file(update_task, update_task->manifest->radio_image));
|
||||||
|
CHECK_RESULT(
|
||||||
|
crc32_calc_file(update_task->file, &update_task_file_progress, update_task) ==
|
||||||
|
update_task->manifest->radio_crc);
|
||||||
|
|
||||||
|
CHECK_RESULT(update_task_write_stack_data(update_task));
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 0);
|
||||||
|
CHECK_RESULT(
|
||||||
|
ble_glue_fus_stack_install(update_task->manifest->radio_address, 0) !=
|
||||||
|
BleGlueCommandResultError);
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 80);
|
||||||
|
CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK);
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 100);
|
||||||
|
/* ...system will restart here. */
|
||||||
|
update_task_wait_for_restart(update_task);
|
||||||
|
success = true;
|
||||||
|
} while(false);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool update_task_remove_stack(UpdateTask* update_task) {
|
||||||
|
bool success = false;
|
||||||
|
do {
|
||||||
|
FURI_LOG_W(TAG, "Removing stack");
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioErase, 30);
|
||||||
|
CHECK_RESULT(ble_glue_fus_stack_delete() != BleGlueCommandResultError);
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioErase, 80);
|
||||||
|
CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK);
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioErase, 100);
|
||||||
|
/* ...system will restart here. */
|
||||||
|
update_task_wait_for_restart(update_task);
|
||||||
|
success = true;
|
||||||
|
} while(false);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool update_task_manage_radiostack(UpdateTask* update_task) {
|
||||||
|
bool success = false;
|
||||||
|
do {
|
||||||
|
CHECK_RESULT(ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT));
|
||||||
|
|
||||||
|
const BleGlueC2Info* c2_state = ble_glue_get_c2_info();
|
||||||
|
|
||||||
|
const UpdateManifestRadioVersion* radio_ver = &update_task->manifest->radio_version;
|
||||||
|
bool stack_version_match = (c2_state->VersionMajor == radio_ver->version.major) &&
|
||||||
|
(c2_state->VersionMinor == radio_ver->version.minor) &&
|
||||||
|
(c2_state->VersionSub == radio_ver->version.sub) &&
|
||||||
|
(c2_state->VersionBranch == radio_ver->version.branch) &&
|
||||||
|
(c2_state->VersionReleaseType == radio_ver->version.release);
|
||||||
|
bool stack_missing = (c2_state->VersionMajor == 0) && (c2_state->VersionMinor == 0);
|
||||||
|
|
||||||
|
if(c2_state->mode == BleGlueC2ModeStack) {
|
||||||
|
/* Stack type is not available when we have FUS running. */
|
||||||
|
bool total_stack_match = stack_version_match &&
|
||||||
|
(c2_state->StackType == radio_ver->version.type);
|
||||||
|
if(total_stack_match) {
|
||||||
|
/* Nothing to do. */
|
||||||
|
FURI_LOG_W(TAG, "Stack version is up2date");
|
||||||
|
furi_hal_rtc_reset_flag(FuriHalRtcFlagC2Update);
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* Version or type mismatch. Let's boot to FUS and start updating. */
|
||||||
|
FURI_LOG_W(TAG, "Restarting to FUS");
|
||||||
|
furi_hal_rtc_set_flag(FuriHalRtcFlagC2Update);
|
||||||
|
CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeFUS));
|
||||||
|
/* ...system will restart here. */
|
||||||
|
update_task_wait_for_restart(update_task);
|
||||||
|
}
|
||||||
|
} else if(c2_state->mode == BleGlueC2ModeFUS) {
|
||||||
|
/* OK, we're in FUS mode. */
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10);
|
||||||
|
FURI_LOG_W(TAG, "Waiting for FUS to settle");
|
||||||
|
ble_glue_fus_wait_operation();
|
||||||
|
if(stack_version_match) {
|
||||||
|
/* We can't check StackType with FUS, but partial version matches */
|
||||||
|
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagC2Update)) {
|
||||||
|
/* This flag was set when full version was checked.
|
||||||
|
* And something in versions of the stack didn't match.
|
||||||
|
* So, clear the flag and drop the stack. */
|
||||||
|
furi_hal_rtc_reset_flag(FuriHalRtcFlagC2Update);
|
||||||
|
FURI_LOG_W(TAG, "Forcing stack removal (match)");
|
||||||
|
CHECK_RESULT(update_task_remove_stack(update_task));
|
||||||
|
} else {
|
||||||
|
/* We might just had the stack installed.
|
||||||
|
* Let's start it up to check its version */
|
||||||
|
FURI_LOG_W(TAG, "Starting stack to check full version");
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 40);
|
||||||
|
CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack));
|
||||||
|
/* ...system will restart here. */
|
||||||
|
update_task_wait_for_restart(update_task);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(stack_missing) {
|
||||||
|
/* Install stack. */
|
||||||
|
CHECK_RESULT(update_task_write_stack(update_task));
|
||||||
|
} else {
|
||||||
|
CHECK_RESULT(update_task_remove_stack(update_task));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update_task_validate_optionbytes(UpdateTask* update_task) {
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageOBValidation, 0);
|
||||||
|
|
||||||
|
bool match = true;
|
||||||
|
bool ob_dirty = false;
|
||||||
|
const UpdateManifest* manifest = update_task->manifest;
|
||||||
|
const FuriHalFlashRawOptionByteData* device_data = furi_hal_flash_ob_get_raw_ptr();
|
||||||
|
for(size_t idx = 0; idx < FURI_HAL_FLASH_OB_TOTAL_VALUES; ++idx) {
|
||||||
|
update_task_set_progress(
|
||||||
|
update_task, UpdateTaskStageProgress, idx * 100 / FURI_HAL_FLASH_OB_TOTAL_VALUES);
|
||||||
|
const uint32_t ref_value = manifest->ob_reference.obs[idx].values.base;
|
||||||
|
const uint32_t device_ob_value = device_data->obs[idx].values.base;
|
||||||
|
const uint32_t device_ob_value_masked = device_ob_value &
|
||||||
|
manifest->ob_compare_mask.obs[idx].values.base;
|
||||||
|
if(ref_value != device_ob_value_masked) {
|
||||||
|
match = false;
|
||||||
|
FURI_LOG_E(
|
||||||
|
TAG,
|
||||||
|
"OB MISMATCH: #%d: real %08X != %08X (exp.), full %08X",
|
||||||
|
idx,
|
||||||
|
device_ob_value_masked,
|
||||||
|
ref_value,
|
||||||
|
device_ob_value);
|
||||||
|
|
||||||
|
/* any bits we are allowed to write?.. */
|
||||||
|
bool can_patch = ((device_ob_value_masked ^ ref_value) &
|
||||||
|
manifest->ob_write_mask.obs[idx].values.base) != 0;
|
||||||
|
|
||||||
|
if(can_patch) {
|
||||||
|
/* patch & restart loop */
|
||||||
|
const uint32_t patched_value =
|
||||||
|
/* take all non-writable bits from real value */
|
||||||
|
(device_ob_value & ~(manifest->ob_write_mask.obs[idx].values.base)) |
|
||||||
|
/* take all writable bits from reference value */
|
||||||
|
(manifest->ob_reference.obs[idx].values.base &
|
||||||
|
manifest->ob_write_mask.obs[idx].values.base);
|
||||||
|
|
||||||
|
FURI_LOG_W(TAG, "Fixing up OB byte #%d to %08X", idx, patched_value);
|
||||||
|
ob_dirty = true;
|
||||||
|
|
||||||
|
bool is_fixed = furi_hal_flash_ob_set_word(idx, patched_value) &&
|
||||||
|
((device_data->obs[idx].values.base &
|
||||||
|
manifest->ob_compare_mask.obs[idx].values.base) == ref_value);
|
||||||
|
|
||||||
|
if(!is_fixed) {
|
||||||
|
/* Things are so bad that fixing what we are allowed to still doesn't match
|
||||||
|
* reference value
|
||||||
|
*/
|
||||||
|
FURI_LOG_W(
|
||||||
|
TAG,
|
||||||
|
"OB #%d is FUBAR (fixed&masked %08X, not %08X)",
|
||||||
|
idx,
|
||||||
|
patched_value,
|
||||||
|
ref_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"OB MATCH: #%d: real %08X == %08X (exp.)",
|
||||||
|
idx,
|
||||||
|
device_ob_value_masked,
|
||||||
|
ref_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!match) {
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageOBError, 95);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ob_dirty) {
|
||||||
|
FURI_LOG_W(TAG, "OB were changed, applying");
|
||||||
|
furi_hal_flash_ob_apply();
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t update_task_worker_flash_writer(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
UpdateTask* update_task = context;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
update_task->state.current_stage_idx = 0;
|
||||||
|
update_task->state.total_stages = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
CHECK_RESULT(update_task_parse_manifest(update_task));
|
||||||
|
|
||||||
|
if(!string_empty_p(update_task->manifest->radio_image)) {
|
||||||
|
CHECK_RESULT(update_task_manage_radiostack(update_task));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_ob = update_manifest_has_obdata(update_task->manifest);
|
||||||
|
if(check_ob) {
|
||||||
|
update_task->state.total_stages++;
|
||||||
|
CHECK_RESULT(update_task_validate_optionbytes(update_task));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!string_empty_p(update_task->manifest->firmware_dfu_image)) {
|
||||||
|
update_task->state.total_stages += 4;
|
||||||
|
CHECK_RESULT(update_task_write_dfu(update_task));
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate);
|
||||||
|
|
||||||
|
update_task_set_progress(update_task, UpdateTaskStageCompleted, 100);
|
||||||
|
success = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return success ? UPDATE_TASK_NOERR : UPDATE_TASK_FAILED;
|
||||||
|
}
|
||||||
1
assets/.gitignore
vendored
1
assets/.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/headers
|
/headers
|
||||||
|
/core2_firmware
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
PROJECT_ROOT = $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))..)
|
PROJECT_ROOT = $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))..)
|
||||||
|
|
||||||
include $(PROJECT_ROOT)/assets/assets.mk
|
include $(PROJECT_ROOT)/assets/assets.mk
|
||||||
|
include $(PROJECT_ROOT)/assets/copro.mk
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: icons protobuf dolphin manifest
|
all: icons protobuf dolphin manifest
|
||||||
@ -35,7 +36,13 @@ manifest:
|
|||||||
.PHONY: dolphin
|
.PHONY: dolphin
|
||||||
dolphin: $(DOLPHIN_EXTERNAL_OUTPUT_DIR)
|
dolphin: $(DOLPHIN_EXTERNAL_OUTPUT_DIR)
|
||||||
|
|
||||||
|
.PHONY: copro_bundle
|
||||||
|
copro_bundle:
|
||||||
|
@mkdir -p $(COPRO_BUNDLE_DIR)
|
||||||
|
@$(ASSETS_COMPILER) copro $(COPRO_CUBE_DIR) $(COPRO_BUNDLE_DIR) $(COPRO_MCU_FAMILY) --cube_ver=$(COPRO_CUBE_VERSION) --stack_type=$(COPRO_STACK_TYPE) --stack_file=$(COPRO_STACK_BIN) --stack_addr=$(COPRO_STACK_ADDR)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@echo "\tCLEAN\t"
|
@echo "\tCLEAN\t"
|
||||||
@$(RM) $(ASSETS_COMPILED_DIR)/*
|
@$(RM) $(ASSETS_COMPILED_DIR)/*
|
||||||
|
@$(RM) -rf $(COPRO_BUNDLE_DIR)
|
||||||
@$(RM) -rf $(DOLPHIN_EXTERNAL_OUTPUT_DIR)
|
@$(RM) -rf $(DOLPHIN_EXTERNAL_OUTPUT_DIR)
|
||||||
|
|||||||
@ -10,6 +10,13 @@
|
|||||||
make all
|
make all
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Compiling with Docker-Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose exec dev make -C assets clean
|
||||||
|
docker-compose exec dev make -C assets all
|
||||||
|
```
|
||||||
|
|
||||||
# Asset naming rules
|
# Asset naming rules
|
||||||
|
|
||||||
## Images and Animations
|
## Images and Animations
|
||||||
|
|||||||
12
assets/copro.mk
Normal file
12
assets/copro.mk
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
COPRO_CUBE_VERSION := 1.13.3
|
||||||
|
COPRO_MCU_FAMILY := STM32WB5x
|
||||||
|
COPRO_STACK_BIN := stm32wb5x_BLE_Stack_light_fw.bin
|
||||||
|
# See __STACK_TYPE_CODES in scripts/flipper/assets/coprobin.py
|
||||||
|
COPRO_STACK_TYPE := ble_light
|
||||||
|
# Keep 0 for auto, or put a value from release_notes for chosen stack
|
||||||
|
COPRO_STACK_ADDR := 0
|
||||||
|
|
||||||
|
COPRO_BUNDLE_DIR := $(ASSETS_DIR)/core2_firmware
|
||||||
|
COPRO_CUBE_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB
|
||||||
|
COPRO_FIRMWARE_DIR := $(COPRO_CUBE_DIR)/Projects/STM32WB_Copro_Wireless_Binaries/$(COPRO_MCU_FAMILY)
|
||||||
|
COPRO_STACK_BIN_PATH := $(COPRO_FIRMWARE_DIR)/$(COPRO_STACK_BIN)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
V:0
|
V:0
|
||||||
T:1650389893
|
T:1651076680
|
||||||
D:badusb
|
D:badusb
|
||||||
D:dolphin
|
D:dolphin
|
||||||
D:infrared
|
D:infrared
|
||||||
@ -222,7 +222,7 @@ F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin/L3_Lab_research_128x54/frame_8.bm
|
|||||||
F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm
|
F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm
|
||||||
F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt
|
F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt
|
||||||
D:infrared/assets
|
D:infrared/assets
|
||||||
F:5b16e1a59daf3ef1d0fc95b3b5596d67:74300:infrared/assets/tv.ir
|
F:d895fda2f48c6cc4c55e8a398ff52e43:74300:infrared/assets/tv.ir
|
||||||
D:nfc/assets
|
D:nfc/assets
|
||||||
F:c6826a621d081d68309e4be424d3d974:4715:nfc/assets/aid.nfc
|
F:c6826a621d081d68309e4be424d3d974:4715:nfc/assets/aid.nfc
|
||||||
F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc
|
F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
Filetype: IR signals file
|
Filetype: IR library file
|
||||||
Version: 1
|
Version: 1
|
||||||
#
|
#
|
||||||
name: POWER
|
name: POWER
|
||||||
|
|||||||
@ -7,7 +7,8 @@
|
|||||||
#include <flipper_format/flipper_format.h>
|
#include <flipper_format/flipper_format.h>
|
||||||
|
|
||||||
#include <update_util/update_manifest.h>
|
#include <update_util/update_manifest.h>
|
||||||
#include <lib/toolbox/path.h>
|
#include <toolbox/path.h>
|
||||||
|
#include <toolbox/crc32_calc.h>
|
||||||
|
|
||||||
static FATFS* pfs = NULL;
|
static FATFS* pfs = NULL;
|
||||||
|
|
||||||
@ -27,7 +28,6 @@ static bool flipper_update_init() {
|
|||||||
furi_hal_delay_init();
|
furi_hal_delay_init();
|
||||||
|
|
||||||
furi_hal_spi_init();
|
furi_hal_spi_init();
|
||||||
furi_hal_crc_init(false);
|
|
||||||
|
|
||||||
MX_FATFS_Init();
|
MX_FATFS_Init();
|
||||||
if(!hal_sd_detect()) {
|
if(!hal_sd_detect()) {
|
||||||
@ -62,17 +62,15 @@ static bool flipper_update_load_stage(const string_t work_dir, UpdateManifest* m
|
|||||||
uint32_t bytes_read = 0;
|
uint32_t bytes_read = 0;
|
||||||
const uint16_t MAX_READ = 0xFFFF;
|
const uint16_t MAX_READ = 0xFFFF;
|
||||||
|
|
||||||
furi_hal_crc_reset();
|
|
||||||
uint32_t crc = 0;
|
uint32_t crc = 0;
|
||||||
do {
|
do {
|
||||||
uint16_t size_read = 0;
|
uint16_t size_read = 0;
|
||||||
if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) {
|
if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
crc = furi_hal_crc_feed(img + bytes_read, size_read);
|
crc = crc32_calc_buffer(crc, img + bytes_read, size_read);
|
||||||
bytes_read += size_read;
|
bytes_read += size_read;
|
||||||
} while(bytes_read == MAX_READ);
|
} while(bytes_read == MAX_READ);
|
||||||
furi_hal_crc_reset();
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) {
|
if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) {
|
||||||
|
|||||||
@ -396,7 +396,7 @@ typedef enum {
|
|||||||
* keep debugger enabled while in any low power mode when set to 1
|
* keep debugger enabled while in any low power mode when set to 1
|
||||||
* should be set to 0 in production
|
* should be set to 0 in production
|
||||||
*/
|
*/
|
||||||
#define CFG_DEBUGGER_SUPPORTED 0
|
#define CFG_DEBUGGER_SUPPORTED 1
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When set to 1, the traces are enabled in the BLE services
|
* When set to 1, the traces are enabled in the BLE services
|
||||||
|
|||||||
@ -1,25 +1,3 @@
|
|||||||
/* USER CODE BEGIN Header */
|
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* File Name : app_debug.c
|
|
||||||
* Description : Debug capabilities source file for STM32WPAN Middleware
|
|
||||||
******************************************************************************
|
|
||||||
* @attention
|
|
||||||
*
|
|
||||||
* <h2><center>© Copyright (c) 2020 STMicroelectronics.
|
|
||||||
* All rights reserved.</center></h2>
|
|
||||||
*
|
|
||||||
* This software component is licensed by ST under Ultimate Liberty license
|
|
||||||
* SLA0044, the "License"; You may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at:
|
|
||||||
* www.st.com/SLA0044
|
|
||||||
*
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
/* USER CODE END Header */
|
|
||||||
|
|
||||||
/* Includes ------------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Includes */
|
|
||||||
#include "utilities_common.h"
|
#include "utilities_common.h"
|
||||||
|
|
||||||
#include "app_common.h"
|
#include "app_common.h"
|
||||||
@ -28,10 +6,7 @@
|
|||||||
#include "tl.h"
|
#include "tl.h"
|
||||||
#include "dbg_trace.h"
|
#include "dbg_trace.h"
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
/* USER CODE END Includes */
|
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PTD */
|
|
||||||
typedef PACKED_STRUCT {
|
typedef PACKED_STRUCT {
|
||||||
GPIO_TypeDef* port;
|
GPIO_TypeDef* port;
|
||||||
uint16_t pin;
|
uint16_t pin;
|
||||||
@ -39,10 +14,7 @@ typedef PACKED_STRUCT {
|
|||||||
uint8_t reserved;
|
uint8_t reserved;
|
||||||
}
|
}
|
||||||
APPD_GpioConfig_t;
|
APPD_GpioConfig_t;
|
||||||
/* USER CODE END PTD */
|
|
||||||
|
|
||||||
/* Private defines -----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PD */
|
|
||||||
#define GPIO_NBR_OF_RF_SIGNALS 9
|
#define GPIO_NBR_OF_RF_SIGNALS 9
|
||||||
#define GPIO_CFG_NBR_OF_FEATURES 34
|
#define GPIO_CFG_NBR_OF_FEATURES 34
|
||||||
#define NBR_OF_TRACES_CONFIG_PARAMETERS 4
|
#define NBR_OF_TRACES_CONFIG_PARAMETERS 4
|
||||||
@ -51,12 +23,11 @@ APPD_GpioConfig_t;
|
|||||||
/**
|
/**
|
||||||
* THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT
|
* THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT
|
||||||
*/
|
*/
|
||||||
#define BLE_DTB_CFG 7
|
#define BLE_DTB_CFG 0
|
||||||
|
// #define BLE_DTB_CFG 7
|
||||||
#define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN)
|
#define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN)
|
||||||
/* USER CODE END PD */
|
|
||||||
|
|
||||||
/* Private variables ---------------------------------------------------------*/
|
/* Private variables ---------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN PV */
|
|
||||||
PLACE_IN_SECTION("MB_MEM2")
|
PLACE_IN_SECTION("MB_MEM2")
|
||||||
ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0};
|
ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0};
|
||||||
PLACE_IN_SECTION("MB_MEM2")
|
PLACE_IN_SECTION("MB_MEM2")
|
||||||
@ -91,7 +62,7 @@ static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = {
|
|||||||
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */
|
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */
|
||||||
{GPIOA, LL_GPIO_PIN_6, 1, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */
|
{GPIOA, LL_GPIO_PIN_6, 1, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */
|
||||||
/* From v1.1.1 */
|
/* From v1.1.1 */
|
||||||
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */
|
{GPIOC, LL_GPIO_PIN_1, 1, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */
|
||||||
/* From v1.2.0 */
|
/* From v1.2.0 */
|
||||||
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */
|
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */
|
||||||
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */
|
{GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */
|
||||||
@ -130,65 +101,20 @@ static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] = {
|
|||||||
{GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */
|
{GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
/* USER CODE END PV */
|
|
||||||
|
|
||||||
/* Global variables ----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN GV */
|
|
||||||
/* USER CODE END GV */
|
|
||||||
|
|
||||||
/* Private function prototypes -----------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PFP */
|
|
||||||
static void APPD_SetCPU2GpioConfig(void);
|
static void APPD_SetCPU2GpioConfig(void);
|
||||||
static void APPD_BleDtbCfg(void);
|
static void APPD_BleDtbCfg(void);
|
||||||
/* USER CODE END PFP */
|
|
||||||
|
|
||||||
/* Functions Definition ------------------------------------------------------*/
|
|
||||||
void APPD_Init(void) {
|
|
||||||
/* USER CODE BEGIN APPD_Init */
|
|
||||||
#if(CFG_DEBUGGER_SUPPORTED == 1)
|
|
||||||
/**
|
|
||||||
* Keep debugger enabled while in any low power mode
|
|
||||||
*/
|
|
||||||
LL_DBGMCU_EnableDBGSleepMode();
|
|
||||||
LL_DBGMCU_EnableDBGStopMode();
|
|
||||||
|
|
||||||
/***************** ENABLE DEBUGGER *************************************/
|
|
||||||
LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48);
|
|
||||||
|
|
||||||
#else
|
|
||||||
LL_GPIO_InitTypeDef gpio_config = {0};
|
|
||||||
LL_PWR_EnableVddUSB();
|
|
||||||
|
|
||||||
gpio_config.Mode = LL_GPIO_MODE_ANALOG;
|
|
||||||
gpio_config.Speed = LL_GPIO_SPEED_FREQ_LOW;
|
|
||||||
// gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
|
||||||
// gpio_config.Pull = LL_GPIO_PULL_NO;
|
|
||||||
// gpio_config.Alternate = LL_GPIO_AF_10;
|
|
||||||
gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13;
|
|
||||||
LL_GPIO_Init(GPIOA, &gpio_config);
|
|
||||||
|
|
||||||
gpio_config.Pin = LL_GPIO_PIN_4 | LL_GPIO_PIN_3;
|
|
||||||
LL_GPIO_Init(GPIOB, &gpio_config);
|
|
||||||
|
|
||||||
LL_DBGMCU_DisableDBGSleepMode();
|
|
||||||
LL_DBGMCU_DisableDBGStopMode();
|
|
||||||
LL_DBGMCU_DisableDBGStandbyMode();
|
|
||||||
|
|
||||||
#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */
|
|
||||||
|
|
||||||
|
void APPD_Init() {
|
||||||
#if(CFG_DEBUG_TRACE != 0)
|
#if(CFG_DEBUG_TRACE != 0)
|
||||||
DbgTraceInit();
|
DbgTraceInit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
APPD_SetCPU2GpioConfig();
|
APPD_SetCPU2GpioConfig();
|
||||||
APPD_BleDtbCfg();
|
APPD_BleDtbCfg();
|
||||||
|
|
||||||
/* USER CODE END APPD_Init */
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void APPD_EnableCPU2(void) {
|
void APPD_EnableCPU2(void) {
|
||||||
/* USER CODE BEGIN APPD_EnableCPU2 */
|
|
||||||
SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = {
|
SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = {
|
||||||
{{0, 0, 0}}, /**< Does not need to be initialized */
|
{{0, 0, 0}}, /**< Does not need to be initialized */
|
||||||
{(uint8_t*)aGpioConfigList,
|
{(uint8_t*)aGpioConfigList,
|
||||||
@ -204,6 +130,7 @@ void APPD_EnableCPU2(void) {
|
|||||||
/** GPIO DEBUG Initialization */
|
/** GPIO DEBUG Initialization */
|
||||||
SHCI_C2_DEBUG_Init(&DebugCmdPacket);
|
SHCI_C2_DEBUG_Init(&DebugCmdPacket);
|
||||||
|
|
||||||
|
// We don't need External Power Amplifier
|
||||||
// LL_GPIO_InitTypeDef gpio_config;
|
// LL_GPIO_InitTypeDef gpio_config;
|
||||||
// gpio_config.Pull = GPIO_NOPULL;
|
// gpio_config.Pull = GPIO_NOPULL;
|
||||||
// gpio_config.Mode = GPIO_MODE_OUTPUT_PP;
|
// gpio_config.Mode = GPIO_MODE_OUTPUT_PP;
|
||||||
@ -212,17 +139,10 @@ void APPD_EnableCPU2(void) {
|
|||||||
// HAL_GPIO_Init(GPIOC, &gpio_config);
|
// HAL_GPIO_Init(GPIOC, &gpio_config);
|
||||||
// SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED);
|
// SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED);
|
||||||
|
|
||||||
/* USER CODE END APPD_EnableCPU2 */
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************
|
|
||||||
*
|
|
||||||
* LOCAL FUNCTIONS
|
|
||||||
*
|
|
||||||
*************************************************************/
|
|
||||||
static void APPD_SetCPU2GpioConfig(void) {
|
static void APPD_SetCPU2GpioConfig(void) {
|
||||||
/* USER CODE BEGIN APPD_SetCPU2GpioConfig */
|
|
||||||
LL_GPIO_InitTypeDef gpio_config = {0};
|
LL_GPIO_InitTypeDef gpio_config = {0};
|
||||||
uint8_t local_loop;
|
uint8_t local_loop;
|
||||||
uint16_t gpioa_pin_list;
|
uint16_t gpioa_pin_list;
|
||||||
@ -259,8 +179,9 @@ static void APPD_SetCPU2GpioConfig(void) {
|
|||||||
gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||||
gpio_config.Pull = LL_GPIO_PULL_NO;
|
gpio_config.Pull = LL_GPIO_PULL_NO;
|
||||||
|
|
||||||
gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13;
|
// Never disable SWD, why would you?
|
||||||
LL_GPIO_Init(GPIOA, &gpio_config);
|
// gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13;
|
||||||
|
// LL_GPIO_Init(GPIOA, &gpio_config);
|
||||||
|
|
||||||
if(gpioa_pin_list != 0) {
|
if(gpioa_pin_list != 0) {
|
||||||
gpio_config.Pin = gpioa_pin_list;
|
gpio_config.Pin = gpioa_pin_list;
|
||||||
@ -282,13 +203,9 @@ static void APPD_SetCPU2GpioConfig(void) {
|
|||||||
LL_GPIO_Init(GPIOC, &gpio_config);
|
LL_GPIO_Init(GPIOC, &gpio_config);
|
||||||
LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list);
|
LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* USER CODE END APPD_SetCPU2GpioConfig */
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void APPD_BleDtbCfg(void) {
|
static void APPD_BleDtbCfg(void) {
|
||||||
/* USER CODE BEGIN APPD_BleDtbCfg */
|
|
||||||
#if(BLE_DTB_CFG != 0)
|
#if(BLE_DTB_CFG != 0)
|
||||||
LL_GPIO_InitTypeDef gpio_config = {0};
|
LL_GPIO_InitTypeDef gpio_config = {0};
|
||||||
uint8_t local_loop;
|
uint8_t local_loop;
|
||||||
@ -304,11 +221,9 @@ static void APPD_BleDtbCfg(void) {
|
|||||||
case(uint32_t)GPIOA:
|
case(uint32_t)GPIOA:
|
||||||
gpioa_pin_list |= aRfConfigList[local_loop].pin;
|
gpioa_pin_list |= aRfConfigList[local_loop].pin;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case(uint32_t)GPIOB:
|
case(uint32_t)GPIOB:
|
||||||
gpiob_pin_list |= aRfConfigList[local_loop].pin;
|
gpiob_pin_list |= aRfConfigList[local_loop].pin;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -334,16 +249,8 @@ static void APPD_BleDtbCfg(void) {
|
|||||||
LL_GPIO_Init(GPIOB, &gpio_config);
|
LL_GPIO_Init(GPIOB, &gpio_config);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* USER CODE END APPD_BleDtbCfg */
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************
|
|
||||||
*
|
|
||||||
* WRAP FUNCTIONS
|
|
||||||
*
|
|
||||||
*************************************************************/
|
|
||||||
#if(CFG_DEBUG_TRACE != 0)
|
#if(CFG_DEBUG_TRACE != 0)
|
||||||
void DbgOutputInit(void) {
|
void DbgOutputInit(void) {
|
||||||
}
|
}
|
||||||
@ -353,5 +260,3 @@ void DbgOutputTraces(uint8_t* p_data, uint16_t size, void (*cb)(void)) {
|
|||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
|
||||||
|
|||||||
@ -6,7 +6,9 @@
|
|||||||
#include "shci.h"
|
#include "shci.h"
|
||||||
#include "shci_tl.h"
|
#include "shci_tl.h"
|
||||||
#include "app_debug.h"
|
#include "app_debug.h"
|
||||||
|
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
#include <shci/shci.h>
|
||||||
|
|
||||||
#define TAG "Core2"
|
#define TAG "Core2"
|
||||||
|
|
||||||
@ -27,22 +29,13 @@ PLACE_IN_SECTION("MB_MEM2")
|
|||||||
ALIGN(4)
|
ALIGN(4)
|
||||||
static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
|
static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
// Stage 1: core2 startup and FUS
|
|
||||||
BleGlueStatusStartup,
|
|
||||||
BleGlueStatusBroken,
|
|
||||||
BleGlueStatusFusStarted,
|
|
||||||
// Stage 2: radio stack
|
|
||||||
BleGlueStatusRadioStackStarted,
|
|
||||||
BleGlueStatusRadioStackMissing
|
|
||||||
} BleGlueStatus;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
osMutexId_t shci_mtx;
|
osMutexId_t shci_mtx;
|
||||||
osSemaphoreId_t shci_sem;
|
osSemaphoreId_t shci_sem;
|
||||||
FuriThread* thread;
|
FuriThread* thread;
|
||||||
BleGlueStatus status;
|
BleGlueStatus status;
|
||||||
BleGlueKeyStorageChangedCallback callback;
|
BleGlueKeyStorageChangedCallback callback;
|
||||||
|
BleGlueC2Info c2_info;
|
||||||
void* context;
|
void* context;
|
||||||
} BleGlue;
|
} BleGlue;
|
||||||
|
|
||||||
@ -70,9 +63,10 @@ void ble_glue_init() {
|
|||||||
LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
|
LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
|
||||||
/* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */
|
/* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */
|
||||||
LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
|
LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
|
|
||||||
// APPD_Init();
|
#ifdef BLE_GLUE_DEBUG
|
||||||
|
APPD_Init();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Initialize all transport layers
|
// Initialize all transport layers
|
||||||
TL_MM_Config_t tl_mm_config;
|
TL_MM_Config_t tl_mm_config;
|
||||||
@ -111,41 +105,103 @@ void ble_glue_init() {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info) {
|
const BleGlueC2Info* ble_glue_get_c2_info() {
|
||||||
bool ret = false;
|
return &ble_glue->c2_info;
|
||||||
|
}
|
||||||
|
|
||||||
size_t countdown = 1000;
|
BleGlueStatus ble_glue_get_c2_status() {
|
||||||
while(countdown > 0) {
|
return ble_glue->status;
|
||||||
if(ble_glue->status == BleGlueStatusFusStarted) {
|
}
|
||||||
ret = true;
|
|
||||||
break;
|
static void ble_glue_update_c2_fw_info() {
|
||||||
}
|
WirelessFwInfo_t wireless_info;
|
||||||
countdown--;
|
SHCI_GetWirelessFwInfo(&wireless_info);
|
||||||
|
BleGlueC2Info* local_info = &ble_glue->c2_info;
|
||||||
|
|
||||||
|
local_info->VersionMajor = wireless_info.VersionMajor;
|
||||||
|
local_info->VersionMinor = wireless_info.VersionMinor;
|
||||||
|
local_info->VersionMajor = wireless_info.VersionMajor;
|
||||||
|
local_info->VersionMinor = wireless_info.VersionMinor;
|
||||||
|
local_info->VersionSub = wireless_info.VersionSub;
|
||||||
|
local_info->VersionBranch = wireless_info.VersionBranch;
|
||||||
|
local_info->VersionReleaseType = wireless_info.VersionReleaseType;
|
||||||
|
|
||||||
|
local_info->MemorySizeSram2B = wireless_info.MemorySizeSram2B;
|
||||||
|
local_info->MemorySizeSram2A = wireless_info.MemorySizeSram2A;
|
||||||
|
local_info->MemorySizeSram1 = wireless_info.MemorySizeSram1;
|
||||||
|
local_info->MemorySizeFlash = wireless_info.MemorySizeFlash;
|
||||||
|
|
||||||
|
local_info->StackType = wireless_info.StackType;
|
||||||
|
|
||||||
|
local_info->FusVersionMajor = wireless_info.FusVersionMajor;
|
||||||
|
local_info->FusVersionMinor = wireless_info.FusVersionMinor;
|
||||||
|
local_info->FusVersionSub = wireless_info.FusVersionSub;
|
||||||
|
local_info->FusMemorySizeSram2B = wireless_info.FusMemorySizeSram2B;
|
||||||
|
local_info->FusMemorySizeSram2A = wireless_info.FusMemorySizeSram2A;
|
||||||
|
local_info->FusMemorySizeFlash = wireless_info.FusMemorySizeFlash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_glue_dump_stack_info() {
|
||||||
|
const BleGlueC2Info* c2_info = &ble_glue->c2_info;
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"Core2: FUS: %d.%d.%d, mem %d/%d, flash %d pages",
|
||||||
|
c2_info->FusVersionMajor,
|
||||||
|
c2_info->FusVersionMinor,
|
||||||
|
c2_info->FusVersionSub,
|
||||||
|
c2_info->FusMemorySizeSram2B,
|
||||||
|
c2_info->FusMemorySizeSram2A,
|
||||||
|
c2_info->FusMemorySizeFlash);
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"Core2: Stack: %d.%d.%d, branch %d, reltype %d, stacktype %d, flash %d pages",
|
||||||
|
c2_info->VersionMajor,
|
||||||
|
c2_info->VersionMinor,
|
||||||
|
c2_info->VersionSub,
|
||||||
|
c2_info->VersionBranch,
|
||||||
|
c2_info->VersionReleaseType,
|
||||||
|
c2_info->StackType,
|
||||||
|
c2_info->MemorySizeFlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_glue_wait_for_c2_start(int32_t timeout) {
|
||||||
|
bool started = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// TODO: use mutex?
|
||||||
|
started = ble_glue->status == BleGlueStatusC2Started;
|
||||||
|
if(!started) {
|
||||||
|
timeout--;
|
||||||
osDelay(1);
|
osDelay(1);
|
||||||
}
|
}
|
||||||
|
} while(!started && (timeout > 0));
|
||||||
|
|
||||||
if(ble_glue->status == BleGlueStatusFusStarted) {
|
if(started) {
|
||||||
SHCI_GetWirelessFwInfo(info);
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"C2 boot completed, mode: %s",
|
||||||
|
ble_glue->c2_info.mode == BleGlueC2ModeFUS ? "FUS" : "Stack");
|
||||||
|
ble_glue_update_c2_fw_info();
|
||||||
|
ble_glue_dump_stack_info();
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(TAG, "Failed to start FUS");
|
FURI_LOG_E(TAG, "C2 startup failed");
|
||||||
ble_glue->status = BleGlueStatusBroken;
|
ble_glue->status = BleGlueStatusBroken;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return started;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ble_glue_start() {
|
bool ble_glue_start() {
|
||||||
furi_assert(ble_glue);
|
furi_assert(ble_glue);
|
||||||
|
|
||||||
if(ble_glue->status != BleGlueStatusFusStarted) {
|
if(ble_glue->status != BleGlueStatusC2Started) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
if(ble_app_init()) {
|
if(ble_app_init()) {
|
||||||
FURI_LOG_I(TAG, "Radio stack started");
|
FURI_LOG_I(TAG, "Radio stack started");
|
||||||
ble_glue->status = BleGlueStatusRadioStackStarted;
|
ble_glue->status = BleGlueStatusRadioStackRunning;
|
||||||
ret = true;
|
ret = true;
|
||||||
if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) {
|
if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) {
|
||||||
FURI_LOG_I(TAG, "Flash activity control switched to SEM7");
|
FURI_LOG_I(TAG, "Flash activity control switched to SEM7");
|
||||||
@ -157,7 +213,6 @@ bool ble_glue_start() {
|
|||||||
ble_glue->status = BleGlueStatusRadioStackMissing;
|
ble_glue->status = BleGlueStatusRadioStackMissing;
|
||||||
ble_app_thread_stop();
|
ble_app_thread_stop();
|
||||||
}
|
}
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -167,7 +222,7 @@ bool ble_glue_is_alive() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ble_glue->status >= BleGlueStatusFusStarted;
|
return ble_glue->status >= BleGlueStatusC2Started;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ble_glue_is_radio_stack_ready() {
|
bool ble_glue_is_radio_stack_ready() {
|
||||||
@ -175,26 +230,42 @@ bool ble_glue_is_radio_stack_ready() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ble_glue->status == BleGlueStatusRadioStackStarted;
|
return ble_glue->status == BleGlueStatusRadioStackRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ble_glue_radio_stack_fw_launch_started() {
|
BleGlueCommandResult ble_glue_force_c2_mode(BleGlueC2Mode desired_mode) {
|
||||||
bool ret = false;
|
furi_check(desired_mode > BleGlueC2ModeUnknown);
|
||||||
// Get FUS status
|
|
||||||
SHCI_FUS_GetState_ErrorCode_t err_code = 0;
|
if(desired_mode == ble_glue->c2_info.mode) {
|
||||||
uint8_t state = SHCI_C2_FUS_GetState(&err_code);
|
return BleGlueCommandResultOK;
|
||||||
if(state == FUS_STATE_VALUE_IDLE) {
|
}
|
||||||
// When FUS is running we can't read radio stack version correctly
|
|
||||||
// Trying to start radio stack fw, which leads to reset
|
if((ble_glue->c2_info.mode == BleGlueC2ModeFUS) && (desired_mode == BleGlueC2ModeStack)) {
|
||||||
FURI_LOG_W(TAG, "FUS is running. Restart to launch Radio Stack");
|
if((ble_glue->c2_info.VersionMajor == 0) && (ble_glue->c2_info.VersionMinor == 0)) {
|
||||||
|
FURI_LOG_W(TAG, "Stack isn't installed!");
|
||||||
|
return BleGlueCommandResultError;
|
||||||
|
}
|
||||||
SHCI_CmdStatus_t status = SHCI_C2_FUS_StartWs();
|
SHCI_CmdStatus_t status = SHCI_C2_FUS_StartWs();
|
||||||
if(status) {
|
if(status) {
|
||||||
FURI_LOG_E(TAG, "Failed to start Radio Stack with status: %02X", status);
|
FURI_LOG_E(TAG, "Failed to start Radio Stack with status: %02X", status);
|
||||||
} else {
|
return BleGlueCommandResultError;
|
||||||
ret = true;
|
|
||||||
}
|
}
|
||||||
|
return BleGlueCommandResultRestartPending;
|
||||||
}
|
}
|
||||||
return ret;
|
if((ble_glue->c2_info.mode == BleGlueC2ModeStack) && (desired_mode == BleGlueC2ModeFUS)) {
|
||||||
|
SHCI_FUS_GetState_ErrorCode_t error_code = 0;
|
||||||
|
uint8_t fus_state = SHCI_C2_FUS_GetState(&error_code);
|
||||||
|
FURI_LOG_D(TAG, "FUS state: %X, error = %x", fus_state, error_code);
|
||||||
|
if(fus_state == SHCI_FUS_CMD_NOT_SUPPORTED) {
|
||||||
|
// Second call to SHCI_C2_FUS_GetState() restarts whole MCU & boots FUS
|
||||||
|
fus_state = SHCI_C2_FUS_GetState(&error_code);
|
||||||
|
FURI_LOG_D(TAG, "FUS state#2: %X, error = %x", fus_state, error_code);
|
||||||
|
return BleGlueCommandResultRestartPending;
|
||||||
|
}
|
||||||
|
return BleGlueCommandResultOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BleGlueCommandResultError;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) {
|
static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) {
|
||||||
@ -221,19 +292,26 @@ static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) {
|
|||||||
*/
|
*/
|
||||||
static void ble_glue_sys_user_event_callback(void* pPayload) {
|
static void ble_glue_sys_user_event_callback(void* pPayload) {
|
||||||
UNUSED(pPayload);
|
UNUSED(pPayload);
|
||||||
/* Traces channel initialization */
|
|
||||||
// APPD_EnableCPU2( );
|
#ifdef BLE_GLUE_DEBUG
|
||||||
|
APPD_EnableCPU2();
|
||||||
|
#endif
|
||||||
|
|
||||||
TL_AsynchEvt_t* p_sys_event =
|
TL_AsynchEvt_t* p_sys_event =
|
||||||
(TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload);
|
(TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload);
|
||||||
|
|
||||||
if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) {
|
if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) {
|
||||||
FURI_LOG_I(TAG, "Fus started");
|
FURI_LOG_I(TAG, "Core2 started");
|
||||||
ble_glue->status = BleGlueStatusFusStarted;
|
SHCI_C2_Ready_Evt_t* p_c2_ready_evt = (SHCI_C2_Ready_Evt_t*)p_sys_event->payload;
|
||||||
furi_hal_power_insomnia_exit();
|
if(p_c2_ready_evt->sysevt_ready_rsp == WIRELESS_FW_RUNNING) {
|
||||||
|
ble_glue->c2_info.mode = BleGlueC2ModeStack;
|
||||||
|
} else if(p_c2_ready_evt->sysevt_ready_rsp == FUS_FW_RUNNING) {
|
||||||
|
ble_glue->c2_info.mode = BleGlueC2ModeFUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_glue->status = BleGlueStatusC2Started;
|
||||||
} else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) {
|
} else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) {
|
||||||
FURI_LOG_E(TAG, "Error during initialization");
|
FURI_LOG_E(TAG, "Error during initialization");
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
} else if(p_sys_event->subevtcode == SHCI_SUB_EVT_BLE_NVM_RAM_UPDATE) {
|
} else if(p_sys_event->subevtcode == SHCI_SUB_EVT_BLE_NVM_RAM_UPDATE) {
|
||||||
SHCI_C2_BleNvmRamUpdate_Evt_t* p_sys_ble_nvm_ram_update_event =
|
SHCI_C2_BleNvmRamUpdate_Evt_t* p_sys_ble_nvm_ram_update_event =
|
||||||
(SHCI_C2_BleNvmRamUpdate_Evt_t*)p_sys_event->payload;
|
(SHCI_C2_BleNvmRamUpdate_Evt_t*)p_sys_event->payload;
|
||||||
@ -308,3 +386,61 @@ void shci_cmd_resp_wait(uint32_t timeout) {
|
|||||||
osSemaphoreAcquire(ble_glue->shci_sem, osWaitForever);
|
osSemaphoreAcquire(ble_glue->shci_sem, osWaitForever);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ble_glue_reinit_c2() {
|
||||||
|
return SHCI_C2_Reinit() == SHCI_Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
BleGlueCommandResult ble_glue_fus_stack_delete() {
|
||||||
|
FURI_LOG_I(TAG, "Erasing stack");
|
||||||
|
SHCI_CmdStatus_t erase_stat = SHCI_C2_FUS_FwDelete();
|
||||||
|
FURI_LOG_I(TAG, "Cmd res = %x", erase_stat);
|
||||||
|
if(erase_stat == SHCI_Success) {
|
||||||
|
return BleGlueCommandResultOperationOngoing;
|
||||||
|
}
|
||||||
|
ble_glue_fus_get_status();
|
||||||
|
return BleGlueCommandResultError;
|
||||||
|
}
|
||||||
|
|
||||||
|
BleGlueCommandResult ble_glue_fus_stack_install(uint32_t src_addr, uint32_t dst_addr) {
|
||||||
|
FURI_LOG_I(TAG, "Installing stack");
|
||||||
|
SHCI_CmdStatus_t write_stat = SHCI_C2_FUS_FwUpgrade(src_addr, dst_addr);
|
||||||
|
FURI_LOG_I(TAG, "Cmd res = %x", write_stat);
|
||||||
|
if(write_stat == SHCI_Success) {
|
||||||
|
return BleGlueCommandResultOperationOngoing;
|
||||||
|
}
|
||||||
|
ble_glue_fus_get_status();
|
||||||
|
return BleGlueCommandResultError;
|
||||||
|
}
|
||||||
|
|
||||||
|
BleGlueCommandResult ble_glue_fus_get_status() {
|
||||||
|
furi_check(ble_glue->c2_info.mode == BleGlueC2ModeFUS);
|
||||||
|
SHCI_FUS_GetState_ErrorCode_t error_code = 0;
|
||||||
|
uint8_t fus_state = SHCI_C2_FUS_GetState(&error_code);
|
||||||
|
FURI_LOG_I(TAG, "FUS state: %x, error: %x", fus_state, error_code);
|
||||||
|
if((error_code != 0) || (fus_state == FUS_STATE_VALUE_ERROR)) {
|
||||||
|
return BleGlueCommandResultError;
|
||||||
|
} else if(
|
||||||
|
(fus_state >= FUS_STATE_VALUE_FW_UPGRD_ONGOING) &&
|
||||||
|
(fus_state <= FUS_STATE_VALUE_SERVICE_ONGOING_END)) {
|
||||||
|
return BleGlueCommandResultOperationOngoing;
|
||||||
|
}
|
||||||
|
return BleGlueCommandResultOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
BleGlueCommandResult ble_glue_fus_wait_operation() {
|
||||||
|
furi_check(ble_glue->c2_info.mode == BleGlueC2ModeFUS);
|
||||||
|
bool wip;
|
||||||
|
do {
|
||||||
|
BleGlueCommandResult fus_status = ble_glue_fus_get_status();
|
||||||
|
if(fus_status == BleGlueCommandResultError) {
|
||||||
|
return BleGlueCommandResultError;
|
||||||
|
}
|
||||||
|
wip = fus_status == BleGlueCommandResultOperationOngoing;
|
||||||
|
if(wip) {
|
||||||
|
osDelay(20);
|
||||||
|
}
|
||||||
|
} while(wip);
|
||||||
|
|
||||||
|
return BleGlueCommandResultOK;
|
||||||
|
}
|
||||||
|
|||||||
@ -2,12 +2,53 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <shci/shci.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BleGlueC2ModeUnknown = 0,
|
||||||
|
BleGlueC2ModeFUS,
|
||||||
|
BleGlueC2ModeStack,
|
||||||
|
} BleGlueC2Mode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BleGlueC2Mode mode;
|
||||||
|
/**
|
||||||
|
* Wireless Info
|
||||||
|
*/
|
||||||
|
uint8_t VersionMajor;
|
||||||
|
uint8_t VersionMinor;
|
||||||
|
uint8_t VersionSub;
|
||||||
|
uint8_t VersionBranch;
|
||||||
|
uint8_t VersionReleaseType;
|
||||||
|
uint8_t MemorySizeSram2B; /*< Multiple of 1K */
|
||||||
|
uint8_t MemorySizeSram2A; /*< Multiple of 1K */
|
||||||
|
uint8_t MemorySizeSram1; /*< Multiple of 1K */
|
||||||
|
uint8_t MemorySizeFlash; /*< Multiple of 4K */
|
||||||
|
uint8_t StackType;
|
||||||
|
/**
|
||||||
|
* Fus Info
|
||||||
|
*/
|
||||||
|
uint8_t FusVersionMajor;
|
||||||
|
uint8_t FusVersionMinor;
|
||||||
|
uint8_t FusVersionSub;
|
||||||
|
uint8_t FusMemorySizeSram2B; /*< Multiple of 1K */
|
||||||
|
uint8_t FusMemorySizeSram2A; /*< Multiple of 1K */
|
||||||
|
uint8_t FusMemorySizeFlash; /*< Multiple of 4K */
|
||||||
|
} BleGlueC2Info;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// Stage 1: core2 startup and FUS
|
||||||
|
BleGlueStatusStartup,
|
||||||
|
BleGlueStatusBroken,
|
||||||
|
BleGlueStatusC2Started,
|
||||||
|
// Stage 2: radio stack
|
||||||
|
BleGlueStatusRadioStackRunning,
|
||||||
|
BleGlueStatusRadioStackMissing
|
||||||
|
} BleGlueStatus;
|
||||||
|
|
||||||
typedef void (
|
typedef void (
|
||||||
*BleGlueKeyStorageChangedCallback)(uint8_t* change_addr_start, uint16_t size, void* context);
|
*BleGlueKeyStorageChangedCallback)(uint8_t* change_addr_start, uint16_t size, void* context);
|
||||||
|
|
||||||
@ -26,7 +67,15 @@ bool ble_glue_start();
|
|||||||
*/
|
*/
|
||||||
bool ble_glue_is_alive();
|
bool ble_glue_is_alive();
|
||||||
|
|
||||||
bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info);
|
/** Waits for C2 to reports its mode to callback
|
||||||
|
*
|
||||||
|
* @return true if it reported before reaching timeout
|
||||||
|
*/
|
||||||
|
bool ble_glue_wait_for_c2_start(int32_t timeout);
|
||||||
|
|
||||||
|
BleGlueStatus ble_glue_get_c2_status();
|
||||||
|
|
||||||
|
const BleGlueC2Info* ble_glue_get_c2_info();
|
||||||
|
|
||||||
/** Is core2 radio stack present and ready
|
/** Is core2 radio stack present and ready
|
||||||
*
|
*
|
||||||
@ -46,11 +95,29 @@ void ble_glue_set_key_storage_changed_callback(
|
|||||||
/** Stop SHCI thread */
|
/** Stop SHCI thread */
|
||||||
void ble_glue_thread_stop();
|
void ble_glue_thread_stop();
|
||||||
|
|
||||||
|
bool ble_glue_reinit_c2();
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BleGlueCommandResultUnknown,
|
||||||
|
BleGlueCommandResultOK,
|
||||||
|
BleGlueCommandResultError,
|
||||||
|
BleGlueCommandResultRestartPending,
|
||||||
|
BleGlueCommandResultOperationOngoing,
|
||||||
|
} BleGlueCommandResult;
|
||||||
|
|
||||||
/** Restart MCU to launch radio stack firmware if necessary
|
/** Restart MCU to launch radio stack firmware if necessary
|
||||||
*
|
*
|
||||||
* @return true on radio stack start command
|
* @return true on radio stack start command
|
||||||
*/
|
*/
|
||||||
bool ble_glue_radio_stack_fw_launch_started();
|
BleGlueCommandResult ble_glue_force_c2_mode(BleGlueC2Mode mode);
|
||||||
|
|
||||||
|
BleGlueCommandResult ble_glue_fus_stack_delete();
|
||||||
|
|
||||||
|
BleGlueCommandResult ble_glue_fus_stack_install(uint32_t src_addr, uint32_t dst_addr);
|
||||||
|
|
||||||
|
BleGlueCommandResult ble_glue_fus_get_status();
|
||||||
|
|
||||||
|
BleGlueCommandResult ble_glue_fus_wait_operation();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,7 +115,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
|
|||||||
if(gap->enable_adv) {
|
if(gap->enable_adv) {
|
||||||
// Restart advertising
|
// Restart advertising
|
||||||
gap_advertise_start(GapStateAdvFast);
|
gap_advertise_start(GapStateAdvFast);
|
||||||
furi_hal_power_insomnia_exit();
|
|
||||||
}
|
}
|
||||||
GapEvent event = {.type = GapEventTypeDisconnected};
|
GapEvent event = {.type = GapEventTypeDisconnected};
|
||||||
gap->on_event_cb(event, gap->context);
|
gap->on_event_cb(event, gap->context);
|
||||||
@ -151,8 +150,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EVT_LE_CONN_COMPLETE:
|
case EVT_LE_CONN_COMPLETE: {
|
||||||
furi_hal_power_insomnia_enter();
|
|
||||||
hci_le_connection_complete_event_rp0* event =
|
hci_le_connection_complete_event_rp0* event =
|
||||||
(hci_le_connection_complete_event_rp0*)meta_evt->data;
|
(hci_le_connection_complete_event_rp0*)meta_evt->data;
|
||||||
gap->connection_params.conn_interval = event->Conn_Interval;
|
gap->connection_params.conn_interval = event->Conn_Interval;
|
||||||
@ -169,7 +167,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
|
|||||||
gap_verify_connection_parameters(gap);
|
gap_verify_connection_parameters(gap);
|
||||||
// Start pairing by sending security request
|
// Start pairing by sending security request
|
||||||
aci_gap_slave_security_req(event->Connection_Handle);
|
aci_gap_slave_security_req(event->Connection_Handle);
|
||||||
break;
|
} break;
|
||||||
|
|
||||||
case EVT_LE_ADVERTISING_REPORT: {
|
case EVT_LE_ADVERTISING_REPORT: {
|
||||||
if(gap_scan) {
|
if(gap_scan) {
|
||||||
@ -414,7 +412,9 @@ static void gap_advertise_start(GapState new_state) {
|
|||||||
// Stop advertising
|
// Stop advertising
|
||||||
status = aci_gap_set_non_discoverable();
|
status = aci_gap_set_non_discoverable();
|
||||||
if(status) {
|
if(status) {
|
||||||
FURI_LOG_E(TAG, "Stop Advertising Failed, result: %d", status);
|
FURI_LOG_E(TAG, "set_non_discoverable failed %d", status);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "set_non_discoverable success");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Configure advertising
|
// Configure advertising
|
||||||
@ -431,7 +431,7 @@ static void gap_advertise_start(GapState new_state) {
|
|||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
if(status) {
|
if(status) {
|
||||||
FURI_LOG_E(TAG, "Set discoverable err: %d", status);
|
FURI_LOG_E(TAG, "set_discoverable failed %d", status);
|
||||||
}
|
}
|
||||||
gap->state = new_state;
|
gap->state = new_state;
|
||||||
GapEvent event = {.type = GapEventTypeStartAdvertising};
|
GapEvent event = {.type = GapEventTypeStartAdvertising};
|
||||||
@ -440,14 +440,25 @@ static void gap_advertise_start(GapState new_state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void gap_advertise_stop() {
|
static void gap_advertise_stop() {
|
||||||
|
tBleStatus ret;
|
||||||
if(gap->state > GapStateIdle) {
|
if(gap->state > GapStateIdle) {
|
||||||
if(gap->state == GapStateConnected) {
|
if(gap->state == GapStateConnected) {
|
||||||
// Terminate connection
|
// Terminate connection
|
||||||
aci_gap_terminate(gap->service.connection_handle, 0x13);
|
ret = aci_gap_terminate(gap->service.connection_handle, 0x13);
|
||||||
|
if(ret != BLE_STATUS_SUCCESS) {
|
||||||
|
FURI_LOG_E(TAG, "terminate failed %d", ret);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "terminate success");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Stop advertising
|
// Stop advertising
|
||||||
osTimerStop(gap->advertise_timer);
|
osTimerStop(gap->advertise_timer);
|
||||||
aci_gap_set_non_discoverable();
|
ret = aci_gap_set_non_discoverable();
|
||||||
|
if(ret != BLE_STATUS_SUCCESS) {
|
||||||
|
FURI_LOG_E(TAG, "set_non_discoverable failed %d", ret);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "set_non_discoverable success");
|
||||||
|
}
|
||||||
gap->state = GapStateIdle;
|
gap->state = GapStateIdle;
|
||||||
}
|
}
|
||||||
GapEvent event = {.type = GapEventTypeStopAdvertising};
|
GapEvent event = {.type = GapEventTypeStopAdvertising};
|
||||||
|
|||||||
@ -10,10 +10,10 @@ void furi_hal_init_early() {
|
|||||||
furi_hal_clock_init_early();
|
furi_hal_clock_init_early();
|
||||||
furi_hal_delay_init();
|
furi_hal_delay_init();
|
||||||
|
|
||||||
furi_hal_os_init();
|
|
||||||
|
|
||||||
furi_hal_resources_init_early();
|
furi_hal_resources_init_early();
|
||||||
|
|
||||||
|
furi_hal_os_init();
|
||||||
|
|
||||||
furi_hal_spi_init_early();
|
furi_hal_spi_init_early();
|
||||||
|
|
||||||
furi_hal_i2c_init_early();
|
furi_hal_i2c_init_early();
|
||||||
@ -55,7 +55,6 @@ void furi_hal_init() {
|
|||||||
FURI_LOG_I(TAG, "Speaker OK");
|
FURI_LOG_I(TAG, "Speaker OK");
|
||||||
|
|
||||||
furi_hal_crypto_init();
|
furi_hal_crypto_init();
|
||||||
furi_hal_crc_init(true);
|
|
||||||
|
|
||||||
// USB
|
// USB
|
||||||
#ifndef FURI_RAM_EXEC
|
#ifndef FURI_RAM_EXEC
|
||||||
|
|||||||
@ -16,6 +16,9 @@
|
|||||||
#define FURI_HAL_BT_DEFAULT_MAC_ADDR \
|
#define FURI_HAL_BT_DEFAULT_MAC_ADDR \
|
||||||
{ 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 }
|
{ 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 }
|
||||||
|
|
||||||
|
/* Time, in ms, to wait for mode transition before crashing */
|
||||||
|
#define C2_MODE_SWITCH_TIMEOUT 10000
|
||||||
|
|
||||||
osMutexId_t furi_hal_bt_core2_mtx = NULL;
|
osMutexId_t furi_hal_bt_core2_mtx = NULL;
|
||||||
static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown;
|
static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown;
|
||||||
|
|
||||||
@ -99,7 +102,7 @@ void furi_hal_bt_unlock_core2() {
|
|||||||
furi_check(osMutexRelease(furi_hal_bt_core2_mtx) == osOK);
|
furi_check(osMutexRelease(furi_hal_bt_core2_mtx) == osOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool furi_hal_bt_radio_stack_is_supported(WirelessFwInfo_t* info) {
|
static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) {
|
||||||
bool supported = false;
|
bool supported = false;
|
||||||
if(info->StackType == INFO_STACK_TYPE_BLE_HCI) {
|
if(info->StackType == INFO_STACK_TYPE_BLE_HCI) {
|
||||||
furi_hal_bt_stack = FuriHalBtStackHciLayer;
|
furi_hal_bt_stack = FuriHalBtStackHciLayer;
|
||||||
@ -128,21 +131,21 @@ bool furi_hal_bt_start_radio_stack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Wait until FUS is started or timeout
|
// Wait until C2 is started or timeout
|
||||||
WirelessFwInfo_t info = {};
|
if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) {
|
||||||
if(!ble_glue_wait_for_fus_start(&info)) {
|
FURI_LOG_E(TAG, "Core2 start failed");
|
||||||
FURI_LOG_E(TAG, "FUS start failed");
|
|
||||||
LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
|
|
||||||
ble_glue_thread_stop();
|
ble_glue_thread_stop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// If FUS is running, start radio stack fw
|
|
||||||
if(ble_glue_radio_stack_fw_launch_started()) {
|
// If C2 is running, start radio stack fw
|
||||||
// If FUS is running do nothing and wait for system reset
|
if(!furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack)) {
|
||||||
furi_crash("Waiting for FUS to launch radio stack firmware");
|
break;
|
||||||
}
|
}
|
||||||
// Check weather we support radio stack
|
|
||||||
if(!furi_hal_bt_radio_stack_is_supported(&info)) {
|
// Check whether we support radio stack
|
||||||
|
const BleGlueC2Info* c2_info = ble_glue_get_c2_info();
|
||||||
|
if(!furi_hal_bt_radio_stack_is_supported(c2_info)) {
|
||||||
FURI_LOG_E(TAG, "Unsupported radio stack");
|
FURI_LOG_E(TAG, "Unsupported radio stack");
|
||||||
// Don't stop SHCI for crypto enclave support
|
// Don't stop SHCI for crypto enclave support
|
||||||
break;
|
break;
|
||||||
@ -150,7 +153,6 @@ bool furi_hal_bt_start_radio_stack() {
|
|||||||
// Starting radio stack
|
// Starting radio stack
|
||||||
if(!ble_glue_start()) {
|
if(!ble_glue_start()) {
|
||||||
FURI_LOG_E(TAG, "Failed to start radio stack");
|
FURI_LOG_E(TAG, "Failed to start radio stack");
|
||||||
LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
|
|
||||||
ble_glue_thread_stop();
|
ble_glue_thread_stop();
|
||||||
ble_app_thread_stop();
|
ble_app_thread_stop();
|
||||||
break;
|
break;
|
||||||
@ -217,27 +219,39 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void furi_hal_bt_reinit() {
|
||||||
|
FURI_LOG_I(TAG, "Disconnect and stop advertising");
|
||||||
|
furi_hal_bt_stop_advertising();
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Stop current profile services");
|
||||||
|
current_profile->stop();
|
||||||
|
|
||||||
|
// Magic happens here
|
||||||
|
hci_reset();
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Stop BLE related RTOS threads");
|
||||||
|
ble_app_thread_stop();
|
||||||
|
gap_thread_stop();
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Reset SHCI");
|
||||||
|
furi_check(ble_glue_reinit_c2());
|
||||||
|
|
||||||
|
osDelay(100);
|
||||||
|
ble_glue_thread_stop();
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Start BT initialization");
|
||||||
|
furi_hal_bt_init();
|
||||||
|
|
||||||
|
furi_hal_bt_start_radio_stack();
|
||||||
|
}
|
||||||
|
|
||||||
bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) {
|
bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) {
|
||||||
furi_assert(event_cb);
|
furi_assert(event_cb);
|
||||||
furi_assert(profile < FuriHalBtProfileNumber);
|
furi_assert(profile < FuriHalBtProfileNumber);
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "Stop current profile services");
|
furi_hal_bt_reinit();
|
||||||
current_profile->stop();
|
|
||||||
FURI_LOG_I(TAG, "Disconnect and stop advertising");
|
|
||||||
furi_hal_bt_stop_advertising();
|
|
||||||
FURI_LOG_I(TAG, "Shutdow 2nd core");
|
|
||||||
LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
|
|
||||||
FURI_LOG_I(TAG, "Stop BLE related RTOS threads");
|
|
||||||
ble_app_thread_stop();
|
|
||||||
gap_thread_stop();
|
|
||||||
FURI_LOG_I(TAG, "Reset SHCI");
|
|
||||||
SHCI_C2_Reinit();
|
|
||||||
osDelay(100);
|
|
||||||
ble_glue_thread_stop();
|
|
||||||
FURI_LOG_I(TAG, "Start BT initialization");
|
|
||||||
furi_hal_bt_init();
|
|
||||||
furi_hal_bt_start_radio_stack();
|
|
||||||
ret = furi_hal_bt_start_app(profile, event_cb, context);
|
ret = furi_hal_bt_start_app(profile, event_cb, context);
|
||||||
if(ret) {
|
if(ret) {
|
||||||
current_profile = &profile_config[profile];
|
current_profile = &profile_config[profile];
|
||||||
@ -404,3 +418,18 @@ void furi_hal_bt_stop_scan() {
|
|||||||
gap_stop_scan();
|
gap_stop_scan();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) {
|
||||||
|
BleGlueCommandResult fw_start_res = ble_glue_force_c2_mode(mode);
|
||||||
|
if(fw_start_res == BleGlueCommandResultOK) {
|
||||||
|
return true;
|
||||||
|
} else if(fw_start_res == BleGlueCommandResultRestartPending) {
|
||||||
|
// Do nothing and wait for system reset
|
||||||
|
osDelay(C2_MODE_SWITCH_TIMEOUT);
|
||||||
|
furi_crash("Waiting for FUS->radio stack transition");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_E(TAG, "Failed to switch C2 mode: %d", fw_start_res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@ -204,6 +204,8 @@ void furi_hal_clock_switch_to_hsi() {
|
|||||||
|
|
||||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
|
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void furi_hal_clock_switch_to_pll() {
|
void furi_hal_clock_switch_to_pll() {
|
||||||
@ -215,6 +217,8 @@ void furi_hal_clock_switch_to_pll() {
|
|||||||
while(!LL_RCC_PLL_IsReady())
|
while(!LL_RCC_PLL_IsReady())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
LL_FLASH_SetLatency(LL_FLASH_LATENCY_3);
|
||||||
|
|
||||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
|
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
|
||||||
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE);
|
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE);
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user