Merge branch 'dev' into release-candidate
This commit is contained in:
commit
f3603e3c04
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,2 +1 @@
|
|||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
|
|||||||
43
.github/workflows/build.yml
vendored
43
.github/workflows/build.yml
vendored
@ -103,6 +103,32 @@ jobs:
|
|||||||
-o firmware/.obj/${TARGET}/full.hex -Intel
|
-o firmware/.obj/${TARGET}/full.hex -Intel
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: 'Generate full dfu file'
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
uses: ./.github/actions/docker
|
||||||
|
with:
|
||||||
|
run: |
|
||||||
|
for TARGET in ${TARGETS}
|
||||||
|
do
|
||||||
|
hex2dfu \
|
||||||
|
-i firmware/.obj/${TARGET}/full.hex \
|
||||||
|
-o artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.dfu \
|
||||||
|
-l "Flipper Zero $(echo $TARGET | tr a-z A-Z)"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: 'Generate full json file'
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
uses: ./.github/actions/docker
|
||||||
|
with:
|
||||||
|
run: |
|
||||||
|
for TARGET in ${TARGETS}
|
||||||
|
do
|
||||||
|
jq -s '.[0] * .[1]' \
|
||||||
|
bootloader/.obj/${TARGET}/bootloader.json \
|
||||||
|
firmware/.obj/${TARGET}/firmware.json \
|
||||||
|
> artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.json
|
||||||
|
done
|
||||||
|
|
||||||
- name: 'Move upload files'
|
- name: 'Move upload files'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
uses: ./.github/actions/docker
|
uses: ./.github/actions/docker
|
||||||
@ -116,25 +142,16 @@ jobs:
|
|||||||
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.bin
|
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.bin
|
||||||
mv bootloader/.obj/${TARGET}/bootloader.elf \
|
mv bootloader/.obj/${TARGET}/bootloader.elf \
|
||||||
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.elf
|
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.elf
|
||||||
|
mv bootloader/.obj/${TARGET}/bootloader.json \
|
||||||
|
artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.json
|
||||||
mv firmware/.obj/${TARGET}/firmware.dfu \
|
mv firmware/.obj/${TARGET}/firmware.dfu \
|
||||||
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.dfu
|
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.dfu
|
||||||
mv firmware/.obj/${TARGET}/firmware.bin \
|
mv firmware/.obj/${TARGET}/firmware.bin \
|
||||||
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.bin
|
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.bin
|
||||||
mv firmware/.obj/${TARGET}/firmware.elf \
|
mv firmware/.obj/${TARGET}/firmware.elf \
|
||||||
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.elf
|
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.elf
|
||||||
done
|
mv firmware/.obj/${TARGET}/firmware.json \
|
||||||
|
artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.json
|
||||||
- name: 'Generate full dfu file'
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
uses: ./.github/actions/docker
|
|
||||||
with:
|
|
||||||
run: |
|
|
||||||
for TARGET in ${TARGETS}
|
|
||||||
do
|
|
||||||
hex2dfu \
|
|
||||||
-i firmware/.obj/${TARGET}/full.hex \
|
|
||||||
-o artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.dfu \
|
|
||||||
-l "Flipper Zero $(echo $TARGET | tr a-z A-Z)"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: 'Full flash asssembly: bootloader as base'
|
- name: 'Full flash asssembly: bootloader as base'
|
||||||
|
|||||||
@ -129,11 +129,11 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage*
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DialogMessageButton boot_version_screen(DialogsApp* dialogs, DialogMessage* message) {
|
static DialogMessageButton bootloader_version_screen(DialogsApp* dialogs, DialogMessage* message) {
|
||||||
DialogMessageButton result;
|
DialogMessageButton result;
|
||||||
string_t buffer;
|
string_t buffer;
|
||||||
string_init(buffer);
|
string_init(buffer);
|
||||||
const Version* ver = furi_hal_version_get_boot_version();
|
const Version* ver = furi_hal_version_get_bootloader_version();
|
||||||
|
|
||||||
if(!ver) {
|
if(!ver) {
|
||||||
string_cat_printf(buffer, "No info\n");
|
string_cat_printf(buffer, "No info\n");
|
||||||
@ -167,7 +167,7 @@ const AboutDialogScreen about_screens[] = {
|
|||||||
icon2_screen,
|
icon2_screen,
|
||||||
hw_version_screen,
|
hw_version_screen,
|
||||||
fw_version_screen,
|
fw_version_screen,
|
||||||
boot_version_screen};
|
bootloader_version_screen};
|
||||||
|
|
||||||
const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
|
const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
|
||||||
|
|
||||||
|
|||||||
@ -84,7 +84,7 @@ static void bt_on_data_received_callback(uint8_t* data, uint16_t size, void* con
|
|||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
Bt* bt = context;
|
Bt* bt = context;
|
||||||
|
|
||||||
size_t bytes_processed = rpc_feed_bytes(bt->rpc_session, data, size, 1000);
|
size_t bytes_processed = rpc_session_feed(bt->rpc_session, data, size, 1000);
|
||||||
if(bytes_processed != size) {
|
if(bytes_processed != size) {
|
||||||
FURI_LOG_E(BT_SERVICE_TAG, "Only %d of %d bytes processed by RPC", bytes_processed, size);
|
FURI_LOG_E(BT_SERVICE_TAG, "Only %d of %d bytes processed by RPC", bytes_processed, size);
|
||||||
}
|
}
|
||||||
@ -129,8 +129,9 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
|
|||||||
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
|
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
|
||||||
// Open RPC session
|
// Open RPC session
|
||||||
FURI_LOG_I(BT_SERVICE_TAG, "Open RPC connection");
|
FURI_LOG_I(BT_SERVICE_TAG, "Open RPC connection");
|
||||||
bt->rpc_session = rpc_open_session(bt->rpc);
|
bt->rpc_session = rpc_session_open(bt->rpc);
|
||||||
rpc_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback, bt);
|
rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback);
|
||||||
|
rpc_session_set_context(bt->rpc_session, bt);
|
||||||
furi_hal_bt_set_data_event_callbacks(
|
furi_hal_bt_set_data_event_callbacks(
|
||||||
bt_on_data_received_callback, bt_on_data_sent_callback, bt);
|
bt_on_data_received_callback, bt_on_data_sent_callback, bt);
|
||||||
// Update battery level
|
// Update battery level
|
||||||
@ -142,7 +143,7 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
|
|||||||
} else if(event.type == BleEventTypeDisconnected) {
|
} else if(event.type == BleEventTypeDisconnected) {
|
||||||
FURI_LOG_I(BT_SERVICE_TAG, "Close RPC connection");
|
FURI_LOG_I(BT_SERVICE_TAG, "Close RPC connection");
|
||||||
if(bt->rpc_session) {
|
if(bt->rpc_session) {
|
||||||
rpc_close_session(bt->rpc_session);
|
rpc_session_close(bt->rpc_session);
|
||||||
bt->rpc_session = NULL;
|
bt->rpc_session = NULL;
|
||||||
}
|
}
|
||||||
} else if(event.type == BleEventTypeStartAdvertising) {
|
} else if(event.type == BleEventTypeStartAdvertising) {
|
||||||
|
|||||||
@ -263,7 +263,7 @@ static void cli_handle_autocomplete(Cli* cli) {
|
|||||||
cli->cursor_position = string_size(cli->line);
|
cli->cursor_position = string_size(cli->line);
|
||||||
}
|
}
|
||||||
// Cleanup
|
// Cleanup
|
||||||
string_clean(common);
|
string_clear(common);
|
||||||
// Show prompt
|
// Show prompt
|
||||||
cli_prompt(cli);
|
cli_prompt(cli);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,13 +56,13 @@ static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_KEY_SLOTS][ENC
|
|||||||
*/
|
*/
|
||||||
void cli_command_device_info(Cli* cli, string_t args, void* context) {
|
void cli_command_device_info(Cli* cli, string_t args, void* context) {
|
||||||
// Device Info version
|
// Device Info version
|
||||||
printf("device_info_major : %d\r\n", 1);
|
printf("device_info_major : %d\r\n", 2);
|
||||||
printf("device_info_minor : %d\r\n", 0);
|
printf("device_info_minor : %d\r\n", 0);
|
||||||
// Model name
|
// Model name
|
||||||
printf("hardware_model : %s\r\n", furi_hal_version_get_model_name());
|
printf("hardware_model : %s\r\n", furi_hal_version_get_model_name());
|
||||||
|
|
||||||
// Unique ID
|
// Unique ID
|
||||||
printf("hardware_uid : ");
|
printf("hardware_uid : ");
|
||||||
const uint8_t* uid = furi_hal_version_uid();
|
const uint8_t* uid = furi_hal_version_uid();
|
||||||
for(size_t i = 0; i < furi_hal_version_uid_size(); i++) {
|
for(size_t i = 0; i < furi_hal_version_uid_size(); i++) {
|
||||||
printf("%02X", uid[i]);
|
printf("%02X", uid[i]);
|
||||||
@ -70,69 +70,69 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) {
|
|||||||
printf("\r\n");
|
printf("\r\n");
|
||||||
|
|
||||||
// OTP Revision
|
// OTP Revision
|
||||||
printf("hardware_otp_ver : %d\r\n", furi_hal_version_get_otp_version());
|
printf("hardware_otp_ver : %d\r\n", furi_hal_version_get_otp_version());
|
||||||
printf("hardware_timestamp : %lu\r\n", furi_hal_version_get_hw_timestamp());
|
printf("hardware_timestamp : %lu\r\n", furi_hal_version_get_hw_timestamp());
|
||||||
|
|
||||||
// Board Revision
|
// Board Revision
|
||||||
printf("hardware_ver : %d\r\n", furi_hal_version_get_hw_version());
|
printf("hardware_ver : %d\r\n", furi_hal_version_get_hw_version());
|
||||||
printf("hardware_target : %d\r\n", furi_hal_version_get_hw_target());
|
printf("hardware_target : %d\r\n", furi_hal_version_get_hw_target());
|
||||||
printf("hardware_body : %d\r\n", furi_hal_version_get_hw_body());
|
printf("hardware_body : %d\r\n", furi_hal_version_get_hw_body());
|
||||||
printf("hardware_connect : %d\r\n", furi_hal_version_get_hw_connect());
|
printf("hardware_connect : %d\r\n", furi_hal_version_get_hw_connect());
|
||||||
printf("hardware_display : %d\r\n", furi_hal_version_get_hw_display());
|
printf("hardware_display : %d\r\n", furi_hal_version_get_hw_display());
|
||||||
|
|
||||||
// Board Personification
|
// Board Personification
|
||||||
printf("hardware_color : %d\r\n", furi_hal_version_get_hw_color());
|
printf("hardware_color : %d\r\n", furi_hal_version_get_hw_color());
|
||||||
printf("hardware_region : %d\r\n", furi_hal_version_get_hw_region());
|
printf("hardware_region : %d\r\n", furi_hal_version_get_hw_region());
|
||||||
const char* name = furi_hal_version_get_name_ptr();
|
const char* name = furi_hal_version_get_name_ptr();
|
||||||
if(name) {
|
if(name) {
|
||||||
printf("hardware_name : %s\r\n", name);
|
printf("hardware_name : %s\r\n", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bootloader Version
|
// Bootloader Version
|
||||||
const Version* boot_version = furi_hal_version_get_boot_version();
|
const Version* bootloader_version = furi_hal_version_get_bootloader_version();
|
||||||
if(boot_version) {
|
if(bootloader_version) {
|
||||||
printf("boot_commit : %s\r\n", version_get_githash(boot_version));
|
printf("bootloader_commit : %s\r\n", version_get_githash(bootloader_version));
|
||||||
printf("boot_branch : %s\r\n", version_get_gitbranch(boot_version));
|
printf("bootloader_branch : %s\r\n", version_get_gitbranch(bootloader_version));
|
||||||
printf("boot_branch_num : %s\r\n", version_get_gitbranchnum(boot_version));
|
printf("bootloader_branch_num : %s\r\n", version_get_gitbranchnum(bootloader_version));
|
||||||
printf("boot_version : %s\r\n", version_get_version(boot_version));
|
printf("bootloader_version : %s\r\n", version_get_version(bootloader_version));
|
||||||
printf("boot_build_date : %s\r\n", version_get_builddate(boot_version));
|
printf("bootloader_build_date : %s\r\n", version_get_builddate(bootloader_version));
|
||||||
printf("boot_target : %d\r\n", version_get_target(boot_version));
|
printf("bootloader_target : %d\r\n", version_get_target(bootloader_version));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firmware version
|
// Firmware version
|
||||||
const Version* firmware_version = furi_hal_version_get_firmware_version();
|
const Version* firmware_version = furi_hal_version_get_firmware_version();
|
||||||
if(firmware_version) {
|
if(firmware_version) {
|
||||||
printf("firmware_commit : %s\r\n", version_get_githash(firmware_version));
|
printf("firmware_commit : %s\r\n", version_get_githash(firmware_version));
|
||||||
printf("firmware_branch : %s\r\n", version_get_gitbranch(firmware_version));
|
printf("firmware_branch : %s\r\n", version_get_gitbranch(firmware_version));
|
||||||
printf("firmware_branch_num : %s\r\n", version_get_gitbranchnum(firmware_version));
|
printf("firmware_branch_num : %s\r\n", version_get_gitbranchnum(firmware_version));
|
||||||
printf("firmware_version : %s\r\n", version_get_version(firmware_version));
|
printf("firmware_version : %s\r\n", version_get_version(firmware_version));
|
||||||
printf("firmware_build_date : %s\r\n", version_get_builddate(firmware_version));
|
printf("firmware_build_date : %s\r\n", version_get_builddate(firmware_version));
|
||||||
printf("firmware_target : %d\r\n", version_get_target(firmware_version));
|
printf("firmware_target : %d\r\n", version_get_target(firmware_version));
|
||||||
}
|
}
|
||||||
|
|
||||||
WirelessFwInfo_t pWirelessInfo;
|
WirelessFwInfo_t pWirelessInfo;
|
||||||
if(furi_hal_bt_is_alive() && SHCI_GetWirelessFwInfo(&pWirelessInfo) == SHCI_Success) {
|
if(furi_hal_bt_is_alive() && SHCI_GetWirelessFwInfo(&pWirelessInfo) == SHCI_Success) {
|
||||||
printf("radio_alive : true\r\n");
|
printf("radio_alive : true\r\n");
|
||||||
// FUS Info
|
// FUS Info
|
||||||
printf("radio_fus_major : %d\r\n", pWirelessInfo.FusVersionMajor);
|
printf("radio_fus_major : %d\r\n", pWirelessInfo.FusVersionMajor);
|
||||||
printf("radio_fus_minor : %d\r\n", pWirelessInfo.FusVersionMinor);
|
printf("radio_fus_minor : %d\r\n", pWirelessInfo.FusVersionMinor);
|
||||||
printf("radio_fus_sub : %d\r\n", pWirelessInfo.FusVersionSub);
|
printf("radio_fus_sub : %d\r\n", pWirelessInfo.FusVersionSub);
|
||||||
printf("radio_fus_sram2b : %dK\r\n", pWirelessInfo.FusMemorySizeSram2B);
|
printf("radio_fus_sram2b : %dK\r\n", pWirelessInfo.FusMemorySizeSram2B);
|
||||||
printf("radio_fus_sram2a : %dK\r\n", pWirelessInfo.FusMemorySizeSram2A);
|
printf("radio_fus_sram2a : %dK\r\n", pWirelessInfo.FusMemorySizeSram2A);
|
||||||
printf("radio_fus_flash : %dK\r\n", pWirelessInfo.FusMemorySizeFlash * 4);
|
printf("radio_fus_flash : %dK\r\n", pWirelessInfo.FusMemorySizeFlash * 4);
|
||||||
// Stack Info
|
// Stack Info
|
||||||
printf("radio_stack_type : %d\r\n", pWirelessInfo.StackType);
|
printf("radio_stack_type : %d\r\n", pWirelessInfo.StackType);
|
||||||
printf("radio_stack_major : %d\r\n", pWirelessInfo.VersionMajor);
|
printf("radio_stack_major : %d\r\n", pWirelessInfo.VersionMajor);
|
||||||
printf("radio_stack_minor : %d\r\n", pWirelessInfo.VersionMinor);
|
printf("radio_stack_minor : %d\r\n", pWirelessInfo.VersionMinor);
|
||||||
printf("radio_stack_sub : %d\r\n", pWirelessInfo.VersionSub);
|
printf("radio_stack_sub : %d\r\n", pWirelessInfo.VersionSub);
|
||||||
printf("radio_stack_branch : %d\r\n", pWirelessInfo.VersionBranch);
|
printf("radio_stack_branch : %d\r\n", pWirelessInfo.VersionBranch);
|
||||||
printf("radio_stack_release : %d\r\n", pWirelessInfo.VersionReleaseType);
|
printf("radio_stack_release : %d\r\n", pWirelessInfo.VersionReleaseType);
|
||||||
printf("radio_stack_sram2b : %dK\r\n", pWirelessInfo.MemorySizeSram2B);
|
printf("radio_stack_sram2b : %dK\r\n", pWirelessInfo.MemorySizeSram2B);
|
||||||
printf("radio_stack_sram2a : %dK\r\n", pWirelessInfo.MemorySizeSram2A);
|
printf("radio_stack_sram2a : %dK\r\n", pWirelessInfo.MemorySizeSram2A);
|
||||||
printf("radio_stack_sram1 : %dK\r\n", pWirelessInfo.MemorySizeSram1);
|
printf("radio_stack_sram1 : %dK\r\n", pWirelessInfo.MemorySizeSram1);
|
||||||
printf("radio_stack_flash : %dK\r\n", pWirelessInfo.MemorySizeFlash * 4);
|
printf("radio_stack_flash : %dK\r\n", pWirelessInfo.MemorySizeFlash * 4);
|
||||||
// Mac address
|
// Mac address
|
||||||
printf("radio_ble_mac : ");
|
printf("radio_ble_mac : ");
|
||||||
const uint8_t* ble_mac = furi_hal_version_get_ble_mac();
|
const uint8_t* ble_mac = furi_hal_version_get_ble_mac();
|
||||||
for(size_t i = 0; i < 6; i++) {
|
for(size_t i = 0; i < 6; i++) {
|
||||||
printf("%02X", ble_mac[i]);
|
printf("%02X", ble_mac[i]);
|
||||||
@ -154,12 +154,12 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) {
|
|||||||
furi_hal_crypto_store_unload_key(key_slot + 1);
|
furi_hal_crypto_store_unload_key(key_slot + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("enclave_valid_keys : %d\r\n", enclave_valid_keys);
|
printf("enclave_valid_keys : %d\r\n", enclave_valid_keys);
|
||||||
printf(
|
printf(
|
||||||
"enclave_valid : %s\r\n",
|
"enclave_valid : %s\r\n",
|
||||||
(enclave_valid_keys == ENCLAVE_SIGNATURE_KEY_SLOTS) ? "true" : "false");
|
(enclave_valid_keys == ENCLAVE_SIGNATURE_KEY_SLOTS) ? "true" : "false");
|
||||||
} else {
|
} else {
|
||||||
printf("radio_alive : false\r\n");
|
printf("radio_alive : false\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,7 @@ Desktop* desktop_alloc() {
|
|||||||
desktop->debug_view = desktop_debug_alloc();
|
desktop->debug_view = desktop_debug_alloc();
|
||||||
desktop->first_start_view = desktop_first_start_alloc();
|
desktop->first_start_view = desktop_first_start_alloc();
|
||||||
desktop->hw_mismatch_popup = popup_alloc();
|
desktop->hw_mismatch_popup = popup_alloc();
|
||||||
|
desktop->code_input = code_input_alloc();
|
||||||
|
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view));
|
desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view));
|
||||||
@ -62,7 +63,8 @@ Desktop* desktop_alloc() {
|
|||||||
desktop->view_dispatcher,
|
desktop->view_dispatcher,
|
||||||
DesktopViewHwMismatch,
|
DesktopViewHwMismatch,
|
||||||
popup_get_view(desktop->hw_mismatch_popup));
|
popup_get_view(desktop->hw_mismatch_popup));
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
desktop->view_dispatcher, DesktopViewPinSetup, code_input_get_view(desktop->code_input));
|
||||||
// Lock icon
|
// Lock icon
|
||||||
desktop->lock_viewport = view_port_alloc();
|
desktop->lock_viewport = view_port_alloc();
|
||||||
view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8));
|
view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8));
|
||||||
@ -82,6 +84,7 @@ void desktop_free(Desktop* desktop) {
|
|||||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug);
|
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug);
|
||||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart);
|
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart);
|
||||||
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch);
|
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch);
|
||||||
|
view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewPinSetup);
|
||||||
|
|
||||||
view_dispatcher_free(desktop->view_dispatcher);
|
view_dispatcher_free(desktop->view_dispatcher);
|
||||||
scene_manager_free(desktop->scene_manager);
|
scene_manager_free(desktop->scene_manager);
|
||||||
@ -92,6 +95,7 @@ void desktop_free(Desktop* desktop) {
|
|||||||
desktop_debug_free(desktop->debug_view);
|
desktop_debug_free(desktop->debug_view);
|
||||||
desktop_first_start_free(desktop->first_start_view);
|
desktop_first_start_free(desktop->first_start_view);
|
||||||
popup_free(desktop->hw_mismatch_popup);
|
popup_free(desktop->hw_mismatch_popup);
|
||||||
|
code_input_free(desktop->code_input);
|
||||||
|
|
||||||
furi_record_close("gui");
|
furi_record_close("gui");
|
||||||
desktop->gui = NULL;
|
desktop->gui = NULL;
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/modules/popup.h>
|
#include <gui/modules/popup.h>
|
||||||
|
#include <gui/modules/code_input.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
@ -29,6 +30,7 @@ typedef enum {
|
|||||||
DesktopViewDebug,
|
DesktopViewDebug,
|
||||||
DesktopViewFirstStart,
|
DesktopViewFirstStart,
|
||||||
DesktopViewHwMismatch,
|
DesktopViewHwMismatch,
|
||||||
|
DesktopViewPinSetup,
|
||||||
DesktopViewTotal,
|
DesktopViewTotal,
|
||||||
} DesktopViewEnum;
|
} DesktopViewEnum;
|
||||||
|
|
||||||
@ -46,7 +48,10 @@ struct Desktop {
|
|||||||
DesktopLockMenuView* lock_menu;
|
DesktopLockMenuView* lock_menu;
|
||||||
DesktopLockedView* locked_view;
|
DesktopLockedView* locked_view;
|
||||||
DesktopDebugView* debug_view;
|
DesktopDebugView* debug_view;
|
||||||
|
CodeInput* code_input;
|
||||||
|
|
||||||
DesktopSettings settings;
|
DesktopSettings settings;
|
||||||
|
PinCode pincode_buffer;
|
||||||
|
|
||||||
ViewPort* lock_viewport;
|
ViewPort* lock_viewport;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,11 +3,20 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define DESKTOP_SETTINGS_VER (0)
|
#define DESKTOP_SETTINGS_VER (1)
|
||||||
|
#define PIN_MAX_LENGTH 12
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t data[PIN_MAX_LENGTH];
|
||||||
|
} PinCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
uint16_t favorite;
|
uint16_t favorite;
|
||||||
|
|
||||||
|
PinCode pincode;
|
||||||
|
bool locked;
|
||||||
} DesktopSettings;
|
} DesktopSettings;
|
||||||
|
|
||||||
bool desktop_settings_load(DesktopSettings* desktop_settings);
|
bool desktop_settings_load(DesktopSettings* desktop_settings);
|
||||||
|
|||||||
@ -33,10 +33,13 @@ DesktopSettingsApp* desktop_settings_app_alloc() {
|
|||||||
|
|
||||||
app->submenu = submenu_alloc();
|
app->submenu = submenu_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher, DesktopSettingsAppViewMain, submenu_get_view(app->submenu));
|
app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu));
|
||||||
|
|
||||||
|
app->code_input = code_input_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher, DesktopSettingsAppViewFavorite, submenu_get_view(app->submenu));
|
app->view_dispatcher,
|
||||||
|
DesktopSettingsAppViewPincodeInput,
|
||||||
|
code_input_get_view(app->code_input));
|
||||||
|
|
||||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
|
||||||
return app;
|
return app;
|
||||||
@ -45,9 +48,10 @@ DesktopSettingsApp* desktop_settings_app_alloc() {
|
|||||||
void desktop_settings_app_free(DesktopSettingsApp* app) {
|
void desktop_settings_app_free(DesktopSettingsApp* app) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
// Variable item list
|
// Variable item list
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMain);
|
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewFavorite);
|
|
||||||
submenu_free(app->submenu);
|
submenu_free(app->submenu);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput);
|
||||||
|
code_input_free(app->code_input);
|
||||||
// View dispatcher
|
// View dispatcher
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
scene_manager_free(app->scene_manager);
|
scene_manager_free(app->scene_manager);
|
||||||
|
|||||||
@ -6,20 +6,32 @@
|
|||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <gui/modules/submenu.h>
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/modules/code_input.h>
|
||||||
|
|
||||||
#include "desktop_settings.h"
|
#include "desktop_settings.h"
|
||||||
|
|
||||||
#include "scenes/desktop_settings_scene.h"
|
#include "scenes/desktop_settings_scene.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DesktopSettingsAppViewMain,
|
CodeEventsSetPin,
|
||||||
DesktopSettingsAppViewFavorite,
|
CodeEventsChangePin,
|
||||||
|
CodeEventsDisablePin,
|
||||||
|
} CodeEventsEnum;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DesktopSettingsAppViewMenu,
|
||||||
|
DesktopSettingsAppViewPincodeInput,
|
||||||
} DesktopSettingsAppView;
|
} DesktopSettingsAppView;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
DesktopSettings settings;
|
DesktopSettings settings;
|
||||||
|
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
SceneManager* scene_manager;
|
SceneManager* scene_manager;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
Submenu* submenu;
|
Submenu* submenu;
|
||||||
|
CodeInput* code_input;
|
||||||
|
|
||||||
|
uint8_t menu_idx;
|
||||||
|
|
||||||
} DesktopSettingsApp;
|
} DesktopSettingsApp;
|
||||||
|
|||||||
@ -1,2 +1,4 @@
|
|||||||
ADD_SCENE(desktop_settings, start, Start)
|
ADD_SCENE(desktop_settings, start, Start)
|
||||||
ADD_SCENE(desktop_settings, favorite, Favorite)
|
ADD_SCENE(desktop_settings, favorite, Favorite)
|
||||||
|
ADD_SCENE(desktop_settings, pincode_menu, PinCodeMenu)
|
||||||
|
ADD_SCENE(desktop_settings, pincode_input, PinCodeInput)
|
||||||
|
|||||||
@ -22,7 +22,7 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
|
|||||||
|
|
||||||
submenu_set_header(app->submenu, "Quick access app:");
|
submenu_set_header(app->submenu, "Quick access app:");
|
||||||
submenu_set_selected_item(app->submenu, app->settings.favorite);
|
submenu_set_selected_item(app->submenu, app->settings.favorite);
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewFavorite);
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) {
|
bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
|||||||
@ -0,0 +1,62 @@
|
|||||||
|
#include "../desktop_settings_app.h"
|
||||||
|
|
||||||
|
#define SCENE_EXIT_EVENT (0U)
|
||||||
|
|
||||||
|
void desktop_settings_scene_ok_callback(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
uint32_t state =
|
||||||
|
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
|
||||||
|
if(state == CodeEventsDisablePin) {
|
||||||
|
memset(app->settings.pincode.data, 0, app->settings.pincode.length * sizeof(uint8_t));
|
||||||
|
app->settings.pincode.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_settings_scene_pincode_input_on_enter(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
CodeInput* code_input = app->code_input;
|
||||||
|
|
||||||
|
uint32_t state =
|
||||||
|
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
bool update = state != CodeEventsDisablePin;
|
||||||
|
|
||||||
|
code_input_set_header_text(code_input, "PIN Code Setup");
|
||||||
|
code_input_set_result_callback(
|
||||||
|
code_input,
|
||||||
|
desktop_settings_scene_ok_callback,
|
||||||
|
NULL,
|
||||||
|
app,
|
||||||
|
app->settings.pincode.data,
|
||||||
|
&app->settings.pincode.length,
|
||||||
|
update);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_settings_scene_pincode_input_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
switch(event.event) {
|
||||||
|
case SCENE_EXIT_EVENT:
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_settings_scene_pincode_input_on_exit(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0);
|
||||||
|
code_input_set_header_text(app->code_input, "");
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
#include "../desktop_settings_app.h"
|
||||||
|
#include "applications.h"
|
||||||
|
|
||||||
|
static void desktop_settings_scene_pincode_menu_submenu_callback(void* context, uint32_t index) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_settings_scene_pincode_menu_on_enter(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
submenu_clean(submenu);
|
||||||
|
|
||||||
|
if(!app->settings.pincode.length) {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Set Pin",
|
||||||
|
CodeEventsSetPin,
|
||||||
|
desktop_settings_scene_pincode_menu_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Change Pin",
|
||||||
|
CodeEventsChangePin,
|
||||||
|
desktop_settings_scene_pincode_menu_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Disable",
|
||||||
|
CodeEventsDisablePin,
|
||||||
|
desktop_settings_scene_pincode_menu_submenu_callback,
|
||||||
|
app);
|
||||||
|
}
|
||||||
|
|
||||||
|
submenu_set_header(app->submenu, "Pin code settings:");
|
||||||
|
submenu_set_selected_item(app->submenu, app->menu_idx);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
switch(event.event) {
|
||||||
|
case CodeEventsSetPin:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
|
||||||
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case CodeEventsChangePin:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
|
||||||
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case CodeEventsDisablePin:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event);
|
||||||
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_settings_scene_pincode_menu_on_exit(void* context) {
|
||||||
|
DesktopSettingsApp* app = context;
|
||||||
|
submenu_clean(app->submenu);
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@ void desktop_settings_scene_start_on_enter(void* context) {
|
|||||||
desktop_settings_scene_start_submenu_callback,
|
desktop_settings_scene_start_submenu_callback,
|
||||||
app);
|
app);
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMain);
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
|
bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
@ -39,7 +39,11 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even
|
|||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case DesktopSettingsStartSubmenuIndexFavorite:
|
case DesktopSettingsStartSubmenuIndexFavorite:
|
||||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppViewFavorite);
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case DesktopSettingsStartSubmenuIndexPinSetup:
|
||||||
|
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeMenu);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,3 +4,4 @@ ADD_SCENE(desktop, locked, Locked)
|
|||||||
ADD_SCENE(desktop, debug, Debug)
|
ADD_SCENE(desktop, debug, Debug)
|
||||||
ADD_SCENE(desktop, first_start, FirstStart)
|
ADD_SCENE(desktop, first_start, FirstStart)
|
||||||
ADD_SCENE(desktop, hw_mismatch, HwMismatch)
|
ADD_SCENE(desktop, hw_mismatch, HwMismatch)
|
||||||
|
ADD_SCENE(desktop, pinsetup, PinSetup)
|
||||||
|
|||||||
@ -9,7 +9,10 @@ void desktop_scene_lock_menu_callback(DesktopLockMenuEvent event, void* context)
|
|||||||
void desktop_scene_lock_menu_on_enter(void* context) {
|
void desktop_scene_lock_menu_on_enter(void* context) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
|
|
||||||
|
desktop_settings_load(&desktop->settings);
|
||||||
|
|
||||||
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
|
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
|
||||||
|
desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pincode.length > 0);
|
||||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu);
|
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,10 +23,25 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case DesktopLockMenuEventLock:
|
case DesktopLockMenuEventLock:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
desktop->scene_manager, DesktopSceneLocked, DesktopLockedNoPin);
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
|
case DesktopLockMenuEventPinLock:
|
||||||
|
|
||||||
|
if(desktop->settings.pincode.length > 0) {
|
||||||
|
desktop->settings.locked = true;
|
||||||
|
desktop_settings_save(&desktop->settings);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
desktop->scene_manager, DesktopSceneLocked, DesktopLockedWithPin);
|
||||||
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||||
|
} else {
|
||||||
|
scene_manager_next_scene(desktop->scene_manager, DesktopScenePinSetup);
|
||||||
|
}
|
||||||
|
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
case DesktopLockMenuEventExit:
|
case DesktopLockMenuEventExit:
|
||||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
|||||||
@ -15,12 +15,39 @@ void desktop_scene_locked_on_enter(void* context) {
|
|||||||
desktop_locked_update_hint_timeout(locked_view);
|
desktop_locked_update_hint_timeout(locked_view);
|
||||||
desktop_locked_set_dolphin_animation(locked_view);
|
desktop_locked_set_dolphin_animation(locked_view);
|
||||||
|
|
||||||
|
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopViewLocked);
|
||||||
|
|
||||||
|
desktop_locked_with_pin(desktop->locked_view, state == DesktopLockedWithPin);
|
||||||
|
|
||||||
view_port_enabled_set(desktop->lock_viewport, true);
|
view_port_enabled_set(desktop->lock_viewport, true);
|
||||||
osTimerStart(locked_view->timer, 63);
|
osTimerStart(locked_view->timer, 63);
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked);
|
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool desktop_scene_locked_check_pin(Desktop* desktop, DesktopMainEvent event) {
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
size_t length = desktop->pincode_buffer.length;
|
||||||
|
length = code_input_push(desktop->pincode_buffer.data, length, event);
|
||||||
|
desktop->pincode_buffer.length = length;
|
||||||
|
|
||||||
|
match = code_input_compare(
|
||||||
|
desktop->pincode_buffer.data,
|
||||||
|
length,
|
||||||
|
desktop->settings.pincode.data,
|
||||||
|
desktop->settings.pincode.length);
|
||||||
|
|
||||||
|
if(match) {
|
||||||
|
desktop->pincode_buffer.length = 0;
|
||||||
|
desktop->settings.locked = false;
|
||||||
|
desktop_settings_save(&desktop->settings);
|
||||||
|
desktop_main_unlocked(desktop->main_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
||||||
Desktop* desktop = (Desktop*)context;
|
Desktop* desktop = (Desktop*)context;
|
||||||
|
|
||||||
@ -36,7 +63,17 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
|
|||||||
case DesktopLockedEventUpdate:
|
case DesktopLockedEventUpdate:
|
||||||
desktop_locked_manage_redraw(desktop->locked_view);
|
desktop_locked_manage_redraw(desktop->locked_view);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
break;
|
||||||
|
case DesktopLockedEventInputReset:
|
||||||
|
desktop->pincode_buffer.length = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
if(desktop_scene_locked_check_pin(desktop, event.event)) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
desktop->scene_manager, DesktopSceneMain, DesktopMainEventUnlocked);
|
||||||
|
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,8 @@ void desktop_scene_main_on_enter(void* context) {
|
|||||||
desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop);
|
desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop);
|
||||||
view_port_enabled_set(desktop->lock_viewport, false);
|
view_port_enabled_set(desktop->lock_viewport, false);
|
||||||
|
|
||||||
|
desktop_settings_load(&desktop->settings);
|
||||||
|
|
||||||
if(scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain) ==
|
if(scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain) ==
|
||||||
DesktopMainEventUnlocked) {
|
DesktopMainEventUnlocked) {
|
||||||
desktop_main_unlocked(desktop->main_view);
|
desktop_main_unlocked(desktop->main_view);
|
||||||
|
|||||||
50
applications/desktop/scenes/desktop_scene_pinsetup.c
Normal file
50
applications/desktop/scenes/desktop_scene_pinsetup.c
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include "../desktop_i.h"
|
||||||
|
|
||||||
|
#define SCENE_EXIT_EVENT (0U)
|
||||||
|
|
||||||
|
void desktop_scene_ok_callback(void* context) {
|
||||||
|
Desktop* app = context;
|
||||||
|
desktop_settings_save(&app->settings);
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_scene_pinsetup_on_enter(void* context) {
|
||||||
|
Desktop* app = context;
|
||||||
|
CodeInput* code_input = app->code_input;
|
||||||
|
|
||||||
|
code_input_set_result_callback(
|
||||||
|
code_input,
|
||||||
|
desktop_scene_ok_callback,
|
||||||
|
NULL,
|
||||||
|
app,
|
||||||
|
app->settings.pincode.data,
|
||||||
|
&app->settings.pincode.length,
|
||||||
|
true);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopViewPinSetup);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool desktop_scene_pinsetup_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Desktop* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
switch(event.event) {
|
||||||
|
case SCENE_EXIT_EVENT:
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void desktop_scene_pinsetup_on_exit(void* context) {
|
||||||
|
Desktop* app = context;
|
||||||
|
code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0);
|
||||||
|
code_input_set_header_text(app->code_input, "");
|
||||||
|
}
|
||||||
@ -42,7 +42,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
|||||||
my_name ? my_name : "Unknown");
|
my_name ? my_name : "Unknown");
|
||||||
canvas_draw_str(canvas, 5, 23, buffer);
|
canvas_draw_str(canvas, 5, 23, buffer);
|
||||||
|
|
||||||
ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_boot_version() :
|
ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_bootloader_version() :
|
||||||
furi_hal_version_get_firmware_version();
|
furi_hal_version_get_firmware_version();
|
||||||
|
|
||||||
if(!ver) {
|
if(!ver) {
|
||||||
|
|||||||
@ -12,6 +12,14 @@ void desktop_lock_menu_set_callback(
|
|||||||
lock_menu->context = context;
|
lock_menu->context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set) {
|
||||||
|
with_view_model(
|
||||||
|
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
||||||
|
model->pin_set = pin_is_set;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) {
|
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
||||||
@ -26,6 +34,10 @@ static void lock_menu_callback(void* context, uint8_t index) {
|
|||||||
switch(index) {
|
switch(index) {
|
||||||
case 0: // lock
|
case 0: // lock
|
||||||
lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context);
|
lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context);
|
||||||
|
break;
|
||||||
|
case 1: // lock
|
||||||
|
lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context);
|
||||||
|
break;
|
||||||
default: // wip message
|
default: // wip message
|
||||||
with_view_model(
|
with_view_model(
|
||||||
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
lock_menu->view, (DesktopLockMenuViewModel * model) {
|
||||||
@ -37,7 +49,7 @@ static void lock_menu_callback(void* context, uint8_t index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void desktop_lock_menu_render(Canvas* canvas, void* model) {
|
void desktop_lock_menu_render(Canvas* canvas, void* model) {
|
||||||
const char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"};
|
const char* Lockmenu_Items[3] = {"Lock", "Lock with PIN", "DUMB mode"};
|
||||||
|
|
||||||
DesktopLockMenuViewModel* m = model;
|
DesktopLockMenuViewModel* m = model;
|
||||||
canvas_clear(canvas);
|
canvas_clear(canvas);
|
||||||
@ -47,13 +59,13 @@ void desktop_lock_menu_render(Canvas* canvas, void* model) {
|
|||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
for(uint8_t i = 0; i < 3; ++i) {
|
for(uint8_t i = 0; i < 3; ++i) {
|
||||||
canvas_draw_str_aligned(
|
const char* str = Lockmenu_Items[i];
|
||||||
canvas,
|
|
||||||
64,
|
if(i == 1 && !m->pin_set) str = "Set PIN";
|
||||||
13 + (i * 17),
|
if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not implemented";
|
||||||
AlignCenter,
|
|
||||||
AlignCenter,
|
canvas_draw_str_aligned(canvas, 64, 13 + (i * 17), AlignCenter, AlignCenter, str);
|
||||||
(m->hint_timeout && m->idx == i && m->idx) ? "Not implemented" : Lockmenu_Items[i]);
|
|
||||||
if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15);
|
if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
DesktopLockMenuEventLock,
|
DesktopLockMenuEventLock,
|
||||||
DesktopLockMenuEventUnlock,
|
DesktopLockMenuEventUnlock,
|
||||||
|
DesktopLockMenuEventPinLock,
|
||||||
DesktopLockMenuEventExit,
|
DesktopLockMenuEventExit,
|
||||||
} DesktopLockMenuEvent;
|
} DesktopLockMenuEvent;
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ struct DesktopLockMenuView {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t idx;
|
uint8_t idx;
|
||||||
uint8_t hint_timeout;
|
uint8_t hint_timeout;
|
||||||
|
bool pin_set;
|
||||||
} DesktopLockMenuViewModel;
|
} DesktopLockMenuViewModel;
|
||||||
|
|
||||||
void desktop_lock_menu_set_callback(
|
void desktop_lock_menu_set_callback(
|
||||||
@ -35,6 +37,7 @@ void desktop_lock_menu_set_callback(
|
|||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);
|
View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);
|
||||||
|
void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set);
|
||||||
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu);
|
void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu);
|
||||||
DesktopLockMenuView* desktop_lock_menu_alloc();
|
DesktopLockMenuView* desktop_lock_menu_alloc();
|
||||||
void desktop_lock_menu_free(DesktopLockMenuView* lock_menu);
|
void desktop_lock_menu_free(DesktopLockMenuView* lock_menu);
|
||||||
|
|||||||
@ -80,6 +80,14 @@ void desktop_locked_reset_counter(DesktopLockedView* locked_view) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void desktop_locked_with_pin(DesktopLockedView* locked_view, bool locked) {
|
||||||
|
with_view_model(
|
||||||
|
locked_view->view, (DesktopLockedViewModel * model) {
|
||||||
|
model->pin_lock = locked;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void desktop_locked_render(Canvas* canvas, void* model) {
|
void desktop_locked_render(Canvas* canvas, void* model) {
|
||||||
DesktopLockedViewModel* m = model;
|
DesktopLockedViewModel* m = model;
|
||||||
uint32_t now = osKernelGetTickCount();
|
uint32_t now = osKernelGetTickCount();
|
||||||
@ -100,7 +108,7 @@ void desktop_locked_render(Canvas* canvas, void* model) {
|
|||||||
canvas_set_font(canvas, FontPrimary);
|
canvas_set_font(canvas, FontPrimary);
|
||||||
elements_multiline_text_framed(canvas, 42, 30, "Locked");
|
elements_multiline_text_framed(canvas, 42, 30, "Locked");
|
||||||
|
|
||||||
} else {
|
} else if(!m->pin_lock) {
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49);
|
canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49);
|
||||||
elements_multiline_text(canvas, 65, 20, "To unlock\npress:");
|
elements_multiline_text(canvas, 65, 20, "To unlock\npress:");
|
||||||
@ -116,27 +124,49 @@ View* desktop_locked_get_view(DesktopLockedView* locked_view) {
|
|||||||
bool desktop_locked_input(InputEvent* event, void* context) {
|
bool desktop_locked_input(InputEvent* event, void* context) {
|
||||||
furi_assert(event);
|
furi_assert(event);
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
|
||||||
DesktopLockedView* locked_view = context;
|
DesktopLockedView* locked_view = context;
|
||||||
|
|
||||||
|
uint32_t press_time = 0;
|
||||||
|
bool locked_with_pin = false;
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
locked_view->view, (DesktopLockedViewModel * model) {
|
||||||
|
locked_with_pin = model->pin_lock;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
if(event->type == InputTypeShort) {
|
if(event->type == InputTypeShort) {
|
||||||
desktop_locked_update_hint_timeout(locked_view);
|
if(locked_with_pin) {
|
||||||
|
press_time = osKernelGetTickCount();
|
||||||
|
|
||||||
if(event->key == InputKeyBack) {
|
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT * 3) {
|
||||||
uint32_t press_time = osKernelGetTickCount();
|
|
||||||
// check if pressed sequentially
|
|
||||||
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
|
|
||||||
locked_view->lock_lastpress = press_time;
|
locked_view->lock_lastpress = press_time;
|
||||||
locked_view->lock_count = 0;
|
locked_view->callback(DesktopLockedEventInputReset, locked_view->context);
|
||||||
} else if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) {
|
|
||||||
locked_view->lock_lastpress = press_time;
|
|
||||||
locked_view->lock_count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(locked_view->lock_count == UNLOCK_CNT) {
|
locked_view->callback(event->key, locked_view->context);
|
||||||
locked_view->lock_count = 0;
|
} else {
|
||||||
locked_view->callback(DesktopLockedEventUnlock, locked_view->context);
|
desktop_locked_update_hint_timeout(locked_view);
|
||||||
|
|
||||||
|
if(event->key == InputKeyBack) {
|
||||||
|
press_time = osKernelGetTickCount();
|
||||||
|
// check if pressed sequentially
|
||||||
|
if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) {
|
||||||
|
locked_view->lock_lastpress = press_time;
|
||||||
|
locked_view->lock_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(locked_view->lock_count == UNLOCK_CNT) {
|
||||||
|
locked_view->lock_count = 0;
|
||||||
|
locked_view->callback(DesktopLockedEventUnlock, locked_view->context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
|
||||||
|
locked_view->lock_lastpress = press_time;
|
||||||
|
locked_view->lock_count = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// All events consumed
|
// All events consumed
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -15,10 +15,16 @@
|
|||||||
#define DOOR_R_POS_MIN 60
|
#define DOOR_R_POS_MIN 60
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DesktopLockedEventUnlock,
|
DesktopLockedEventUnlock = 10U,
|
||||||
DesktopLockedEventUpdate,
|
DesktopLockedEventUpdate = 11U,
|
||||||
|
DesktopLockedEventInputReset = 12U,
|
||||||
} DesktopLockedEvent;
|
} DesktopLockedEvent;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DesktopLockedWithPin,
|
||||||
|
DesktopLockedNoPin,
|
||||||
|
} DesktopLockedSceneState;
|
||||||
|
|
||||||
typedef struct DesktopLockedView DesktopLockedView;
|
typedef struct DesktopLockedView DesktopLockedView;
|
||||||
|
|
||||||
typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context);
|
typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context);
|
||||||
@ -42,6 +48,7 @@ typedef struct {
|
|||||||
int8_t door_right_x;
|
int8_t door_right_x;
|
||||||
bool animation_seq_end;
|
bool animation_seq_end;
|
||||||
|
|
||||||
|
bool pin_lock;
|
||||||
} DesktopLockedViewModel;
|
} DesktopLockedViewModel;
|
||||||
|
|
||||||
void desktop_locked_set_callback(
|
void desktop_locked_set_callback(
|
||||||
@ -58,5 +65,4 @@ void desktop_locked_manage_redraw(DesktopLockedView* locked_view);
|
|||||||
View* desktop_locked_get_view(DesktopLockedView* locked_view);
|
View* desktop_locked_get_view(DesktopLockedView* locked_view);
|
||||||
DesktopLockedView* desktop_locked_alloc();
|
DesktopLockedView* desktop_locked_alloc();
|
||||||
void desktop_locked_free(DesktopLockedView* locked_view);
|
void desktop_locked_free(DesktopLockedView* locked_view);
|
||||||
void desktop_main_unlocked(DesktopMainView* main_view);
|
void desktop_locked_with_pin(DesktopLockedView* lock_menu, bool locked);
|
||||||
void desktop_main_reset_hint(DesktopMainView* main_view);
|
|
||||||
@ -67,6 +67,7 @@ bool desktop_main_input(InputEvent* event, void* context) {
|
|||||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||||
main_view->callback(DesktopMainEventOpenFavorite, main_view->context);
|
main_view->callback(DesktopMainEventOpenFavorite, main_view->context);
|
||||||
}
|
}
|
||||||
|
|
||||||
desktop_main_reset_hint(main_view);
|
desktop_main_reset_hint(main_view);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -7,12 +7,12 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DesktopMainEventOpenMenu,
|
|
||||||
DesktopMainEventOpenLockMenu,
|
DesktopMainEventOpenLockMenu,
|
||||||
DesktopMainEventOpenDebug,
|
|
||||||
DesktopMainEventUnlocked,
|
|
||||||
DesktopMainEventOpenArchive,
|
DesktopMainEventOpenArchive,
|
||||||
DesktopMainEventOpenFavorite,
|
DesktopMainEventOpenFavorite,
|
||||||
|
DesktopMainEventOpenMenu,
|
||||||
|
DesktopMainEventOpenDebug,
|
||||||
|
DesktopMainEventUnlocked,
|
||||||
} DesktopMainEvent;
|
} DesktopMainEvent;
|
||||||
|
|
||||||
typedef struct DesktopMainView DesktopMainView;
|
typedef struct DesktopMainView DesktopMainView;
|
||||||
@ -37,9 +37,8 @@ void desktop_main_set_callback(
|
|||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
View* desktop_main_get_view(DesktopMainView* main_view);
|
View* desktop_main_get_view(DesktopMainView* main_view);
|
||||||
|
|
||||||
DesktopMainView* desktop_main_alloc();
|
DesktopMainView* desktop_main_alloc();
|
||||||
|
|
||||||
void desktop_main_free(DesktopMainView* main_view);
|
void desktop_main_free(DesktopMainView* main_view);
|
||||||
|
|
||||||
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view);
|
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view);
|
||||||
|
void desktop_main_unlocked(DesktopMainView* main_view);
|
||||||
|
void desktop_main_reset_hint(DesktopMainView* main_view);
|
||||||
|
|||||||
@ -12,6 +12,14 @@
|
|||||||
#include <gui/modules/variable-item-list.h>
|
#include <gui/modules/variable-item-list.h>
|
||||||
#include "views/gpio_test.h"
|
#include "views/gpio_test.h"
|
||||||
|
|
||||||
|
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL)
|
||||||
|
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL)
|
||||||
|
#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL)
|
||||||
|
#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL)
|
||||||
|
|
||||||
|
#define GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE (4UL)
|
||||||
|
#define GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE (5UL)
|
||||||
|
|
||||||
struct GpioApp {
|
struct GpioApp {
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
#include "../gpio_app_i.h"
|
#include "../gpio_app_i.h"
|
||||||
#include "furi-hal-power.h"
|
#include "furi-hal-power.h"
|
||||||
|
|
||||||
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL)
|
|
||||||
#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL)
|
|
||||||
#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL)
|
|
||||||
#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL)
|
|
||||||
|
|
||||||
enum GpioItem {
|
enum GpioItem {
|
||||||
GpioItemOtg,
|
GpioItemOtg,
|
||||||
GpioItemTest,
|
GpioItemTest,
|
||||||
|
|||||||
@ -1,17 +1,6 @@
|
|||||||
|
#include "../usb_uart_bridge.h"
|
||||||
#include "../gpio_app_i.h"
|
#include "../gpio_app_i.h"
|
||||||
#include "furi-hal.h"
|
#include "furi-hal.h"
|
||||||
#include <stream_buffer.h>
|
|
||||||
#include <furi-hal-usb-cdc_i.h>
|
|
||||||
#include "usb_cdc.h"
|
|
||||||
|
|
||||||
#define USB_PKT_LEN CDC_DATA_SZ
|
|
||||||
#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3)
|
|
||||||
#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3)
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
WorkerCmdStop = (1 << 0),
|
|
||||||
|
|
||||||
} WorkerCommandFlags;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UsbUartLineIndexVcp,
|
UsbUartLineIndexVcp,
|
||||||
@ -21,42 +10,7 @@ typedef enum {
|
|||||||
UsbUartLineIndexDisable,
|
UsbUartLineIndexDisable,
|
||||||
} LineIndex;
|
} LineIndex;
|
||||||
|
|
||||||
typedef enum {
|
static UsbUartConfig* cfg_set;
|
||||||
UsbUartPortUSART1 = 0,
|
|
||||||
UsbUartPortLPUART1 = 1,
|
|
||||||
} PortIdx;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t vcp_ch;
|
|
||||||
PortIdx uart_ch;
|
|
||||||
uint32_t baudrate;
|
|
||||||
} UsbUartConfig;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UsbUartConfig cfg_cur;
|
|
||||||
UsbUartConfig cfg_set;
|
|
||||||
char br_text[8];
|
|
||||||
|
|
||||||
bool running;
|
|
||||||
osThreadId_t parent_thread;
|
|
||||||
|
|
||||||
osThreadAttr_t thread_attr;
|
|
||||||
osThreadId_t thread;
|
|
||||||
|
|
||||||
osThreadAttr_t tx_thread_attr;
|
|
||||||
osThreadId_t tx_thread;
|
|
||||||
|
|
||||||
StreamBufferHandle_t rx_stream;
|
|
||||||
osSemaphoreId_t rx_done_sem;
|
|
||||||
osSemaphoreId_t usb_sof_sem;
|
|
||||||
|
|
||||||
StreamBufferHandle_t tx_stream;
|
|
||||||
|
|
||||||
uint8_t rx_buf[USB_PKT_LEN];
|
|
||||||
uint8_t tx_buf[USB_PKT_LEN];
|
|
||||||
} UsbUartParams;
|
|
||||||
|
|
||||||
static UsbUartParams* usb_uart;
|
|
||||||
|
|
||||||
static const char* vcp_ch[] = {"0 (CLI)", "1"};
|
static const char* vcp_ch[] = {"0 (CLI)", "1"};
|
||||||
static const char* uart_ch[] = {"USART1", "LPUART1"};
|
static const char* uart_ch[] = {"USART1", "LPUART1"};
|
||||||
@ -73,197 +27,14 @@ static const uint32_t baudrate_list[] = {
|
|||||||
921600,
|
921600,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void vcp_on_cdc_tx_complete();
|
|
||||||
static void vcp_on_cdc_rx();
|
|
||||||
static void vcp_state_callback(uint8_t state);
|
|
||||||
static void vcp_on_cdc_control_line(uint8_t state);
|
|
||||||
static void vcp_on_line_config(struct usb_cdc_line_coding* config);
|
|
||||||
|
|
||||||
static CdcCallbacks cdc_cb = {
|
|
||||||
vcp_on_cdc_tx_complete,
|
|
||||||
vcp_on_cdc_rx,
|
|
||||||
vcp_state_callback,
|
|
||||||
vcp_on_cdc_control_line,
|
|
||||||
vcp_on_line_config,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* USB UART worker */
|
|
||||||
|
|
||||||
static void usb_uart_tx_thread(void* context);
|
|
||||||
|
|
||||||
static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
|
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
|
|
||||||
if(ev == UartIrqEventRXNE) {
|
|
||||||
size_t ret =
|
|
||||||
xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
|
|
||||||
furi_check(ret == 1);
|
|
||||||
ret = xStreamBufferBytesAvailable(usb_uart->rx_stream);
|
|
||||||
if(ret > USB_PKT_LEN) osSemaphoreRelease(usb_uart->rx_done_sem);
|
|
||||||
} else if(ev == UartIrqEventIDLE) {
|
|
||||||
osSemaphoreRelease(usb_uart->rx_done_sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_uart_worker(void* context) {
|
|
||||||
memcpy(&usb_uart->cfg_cur, &usb_uart->cfg_set, sizeof(UsbUartConfig));
|
|
||||||
|
|
||||||
usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
|
|
||||||
usb_uart->rx_done_sem = osSemaphoreNew(1, 1, NULL);
|
|
||||||
usb_uart->usb_sof_sem = osSemaphoreNew(1, 1, NULL);
|
|
||||||
|
|
||||||
usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1);
|
|
||||||
|
|
||||||
usb_uart->tx_thread = NULL;
|
|
||||||
usb_uart->tx_thread_attr.name = "usb_uart_tx";
|
|
||||||
usb_uart->tx_thread_attr.stack_size = 512;
|
|
||||||
|
|
||||||
UsbMode usb_mode_prev = furi_hal_usb_get_config();
|
|
||||||
if(usb_uart->cfg_cur.vcp_ch == 0) {
|
|
||||||
furi_hal_usb_set_config(UsbModeVcpSingle);
|
|
||||||
furi_hal_vcp_disable();
|
|
||||||
} else {
|
|
||||||
furi_hal_usb_set_config(UsbModeVcpDual);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) {
|
|
||||||
furi_hal_usart_init();
|
|
||||||
furi_hal_usart_set_irq_cb(usb_uart_on_irq_cb);
|
|
||||||
if(usb_uart->cfg_cur.baudrate != 0)
|
|
||||||
furi_hal_usart_set_br(usb_uart->cfg_cur.baudrate);
|
|
||||||
else
|
|
||||||
vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
|
|
||||||
} else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) {
|
|
||||||
furi_hal_lpuart_init();
|
|
||||||
furi_hal_lpuart_set_irq_cb(usb_uart_on_irq_cb);
|
|
||||||
if(usb_uart->cfg_cur.baudrate != 0)
|
|
||||||
furi_hal_lpuart_set_br(usb_uart->cfg_cur.baudrate);
|
|
||||||
else
|
|
||||||
vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch));
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, &cdc_cb);
|
|
||||||
usb_uart->tx_thread = osThreadNew(usb_uart_tx_thread, NULL, &usb_uart->tx_thread_attr);
|
|
||||||
|
|
||||||
while(1) {
|
|
||||||
furi_check(osSemaphoreAcquire(usb_uart->rx_done_sem, osWaitForever) == osOK);
|
|
||||||
if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, 0) == WorkerCmdStop) break;
|
|
||||||
size_t len = 0;
|
|
||||||
do {
|
|
||||||
len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0);
|
|
||||||
if(len > 0) {
|
|
||||||
if(osSemaphoreAcquire(usb_uart->usb_sof_sem, 100) == osOK)
|
|
||||||
furi_hal_cdc_send(usb_uart->cfg_cur.vcp_ch, usb_uart->rx_buf, len);
|
|
||||||
else
|
|
||||||
xStreamBufferReset(usb_uart->rx_stream);
|
|
||||||
}
|
|
||||||
} while(len > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
osThreadTerminate(usb_uart->tx_thread);
|
|
||||||
|
|
||||||
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
|
|
||||||
furi_hal_usart_deinit();
|
|
||||||
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
|
|
||||||
furi_hal_lpuart_deinit();
|
|
||||||
|
|
||||||
furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, NULL);
|
|
||||||
furi_hal_usb_set_config(usb_mode_prev);
|
|
||||||
if(usb_uart->cfg_cur.vcp_ch == 0) furi_hal_vcp_enable();
|
|
||||||
|
|
||||||
vStreamBufferDelete(usb_uart->rx_stream);
|
|
||||||
osSemaphoreDelete(usb_uart->rx_done_sem);
|
|
||||||
osSemaphoreDelete(usb_uart->usb_sof_sem);
|
|
||||||
|
|
||||||
vStreamBufferDelete(usb_uart->tx_stream);
|
|
||||||
osThreadFlagsSet(usb_uart->parent_thread, WorkerCmdStop);
|
|
||||||
osThreadExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_uart_tx_thread(void* context) {
|
|
||||||
uint8_t data = 0;
|
|
||||||
while(1) {
|
|
||||||
size_t len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, osWaitForever);
|
|
||||||
if(len > 0) {
|
|
||||||
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
|
|
||||||
furi_hal_usart_tx(&data, len);
|
|
||||||
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
|
|
||||||
furi_hal_lpuart_tx(&data, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
osThreadExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VCP callbacks */
|
|
||||||
|
|
||||||
static void vcp_on_cdc_tx_complete() {
|
|
||||||
osSemaphoreRelease(usb_uart->usb_sof_sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vcp_on_cdc_rx() {
|
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
|
|
||||||
uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream);
|
|
||||||
if(max_len > 0) {
|
|
||||||
if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN;
|
|
||||||
int32_t size = furi_hal_cdc_receive(usb_uart->cfg_cur.vcp_ch, usb_uart->tx_buf, max_len);
|
|
||||||
|
|
||||||
if(size > 0) {
|
|
||||||
size_t ret = xStreamBufferSendFromISR(
|
|
||||||
usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken);
|
|
||||||
furi_check(ret == size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vcp_state_callback(uint8_t state) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vcp_on_cdc_control_line(uint8_t state) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vcp_on_line_config(struct usb_cdc_line_coding* config) {
|
|
||||||
if((usb_uart->cfg_cur.baudrate == 0) && (config->dwDTERate != 0)) {
|
|
||||||
if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1)
|
|
||||||
furi_hal_usart_set_br(config->dwDTERate);
|
|
||||||
else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1)
|
|
||||||
furi_hal_lpuart_set_br(config->dwDTERate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* USB UART app */
|
|
||||||
|
|
||||||
static void usb_uart_enable() {
|
|
||||||
if(usb_uart->running == false) {
|
|
||||||
usb_uart->thread = NULL;
|
|
||||||
usb_uart->thread_attr.name = "usb_uart";
|
|
||||||
usb_uart->thread_attr.stack_size = 1024;
|
|
||||||
usb_uart->parent_thread = osThreadGetId();
|
|
||||||
usb_uart->running = true;
|
|
||||||
usb_uart->thread = osThreadNew(usb_uart_worker, NULL, &usb_uart->thread_attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_uart_disable() {
|
|
||||||
if(usb_uart->running == true) {
|
|
||||||
osThreadFlagsSet(usb_uart->thread, WorkerCmdStop);
|
|
||||||
osSemaphoreRelease(usb_uart->rx_done_sem);
|
|
||||||
osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, osWaitForever);
|
|
||||||
usb_uart->running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
|
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
|
||||||
//GpioApp* app = context;
|
//GpioApp* app = context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == UsbUartLineIndexEnable) {
|
if(event.event == GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE) {
|
||||||
usb_uart_enable();
|
usb_uart_enable(cfg_set);
|
||||||
} else if(event.event == UsbUartLineIndexDisable) {
|
} else if(event.event == GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE) {
|
||||||
usb_uart_disable();
|
usb_uart_disable();
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
@ -271,15 +42,13 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
|
|||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scene callbacks */
|
|
||||||
|
|
||||||
static void line_vcp_cb(VariableItem* item) {
|
static void line_vcp_cb(VariableItem* item) {
|
||||||
//GpioApp* app = variable_item_get_context(item);
|
//GpioApp* app = variable_item_get_context(item);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
variable_item_set_current_value_text(item, vcp_ch[index]);
|
variable_item_set_current_value_text(item, vcp_ch[index]);
|
||||||
|
|
||||||
usb_uart->cfg_set.vcp_ch = index;
|
cfg_set->vcp_ch = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_port_cb(VariableItem* item) {
|
static void line_port_cb(VariableItem* item) {
|
||||||
@ -288,34 +57,44 @@ static void line_port_cb(VariableItem* item) {
|
|||||||
|
|
||||||
variable_item_set_current_value_text(item, uart_ch[index]);
|
variable_item_set_current_value_text(item, uart_ch[index]);
|
||||||
|
|
||||||
usb_uart->cfg_set.uart_ch = index;
|
if(index == 0)
|
||||||
|
cfg_set->uart_ch = FuriHalUartIdUSART1;
|
||||||
|
else if(index == 1)
|
||||||
|
cfg_set->uart_ch = FuriHalUartIdLPUART1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_baudrate_cb(VariableItem* item) {
|
static void line_baudrate_cb(VariableItem* item) {
|
||||||
//GpioApp* app = variable_item_get_context(item);
|
//GpioApp* app = variable_item_get_context(item);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
|
char br_text[8];
|
||||||
|
|
||||||
if(index > 0) {
|
if(index > 0) {
|
||||||
snprintf(usb_uart->br_text, 7, "%lu", baudrate_list[index - 1]);
|
snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
|
||||||
variable_item_set_current_value_text(item, usb_uart->br_text);
|
variable_item_set_current_value_text(item, br_text);
|
||||||
usb_uart->cfg_set.baudrate = baudrate_list[index - 1];
|
cfg_set->baudrate = baudrate_list[index - 1];
|
||||||
} else {
|
} else {
|
||||||
variable_item_set_current_value_text(item, baudrate_mode[index]);
|
variable_item_set_current_value_text(item, baudrate_mode[index]);
|
||||||
usb_uart->cfg_set.baudrate = 0;
|
cfg_set->baudrate = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) {
|
static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
GpioApp* app = context;
|
GpioApp* app = context;
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
if(index == UsbUartLineIndexEnable)
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE);
|
||||||
|
else if(index == UsbUartLineIndexDisable)
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpio_scene_usb_uart_on_enter(void* context) {
|
void gpio_scene_usb_uart_on_enter(void* context) {
|
||||||
GpioApp* app = context;
|
GpioApp* app = context;
|
||||||
VariableItemList* var_item_list = app->var_item_list;
|
VariableItemList* var_item_list = app->var_item_list;
|
||||||
|
|
||||||
usb_uart = furi_alloc(sizeof(UsbUartParams));
|
cfg_set = furi_alloc(sizeof(UsbUartConfig));
|
||||||
|
|
||||||
VariableItem* item;
|
VariableItem* item;
|
||||||
|
|
||||||
@ -348,5 +127,5 @@ void gpio_scene_usb_uart_on_exit(void* context) {
|
|||||||
GpioApp* app = context;
|
GpioApp* app = context;
|
||||||
usb_uart_disable();
|
usb_uart_disable();
|
||||||
variable_item_list_clean(app->var_item_list);
|
variable_item_list_clean(app->var_item_list);
|
||||||
free(usb_uart);
|
free(cfg_set);
|
||||||
}
|
}
|
||||||
246
applications/gpio/usb_uart_bridge.c
Normal file
246
applications/gpio/usb_uart_bridge.c
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
#include "usb_uart_bridge.h"
|
||||||
|
#include "furi-hal.h"
|
||||||
|
#include <stream_buffer.h>
|
||||||
|
#include <furi-hal-usb-cdc_i.h>
|
||||||
|
#include "usb_cdc.h"
|
||||||
|
|
||||||
|
#define USB_PKT_LEN CDC_DATA_SZ
|
||||||
|
#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3)
|
||||||
|
#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WorkerEvtStop = (1 << 0),
|
||||||
|
WorkerEvtRxReady = (1 << 1),
|
||||||
|
|
||||||
|
WorkerEvtTxStop = (1 << 2),
|
||||||
|
WorkerEvtTxReady = (1 << 3),
|
||||||
|
|
||||||
|
WorkerEvtSof = (1 << 4),
|
||||||
|
|
||||||
|
} WorkerEvtFlags;
|
||||||
|
|
||||||
|
#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxReady)
|
||||||
|
#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtTxReady)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
UsbUartConfig cfg;
|
||||||
|
|
||||||
|
FuriThread* thread;
|
||||||
|
FuriThread* tx_thread;
|
||||||
|
|
||||||
|
osEventFlagsId_t events;
|
||||||
|
|
||||||
|
StreamBufferHandle_t rx_stream;
|
||||||
|
StreamBufferHandle_t tx_stream;
|
||||||
|
|
||||||
|
uint8_t rx_buf[USB_PKT_LEN];
|
||||||
|
uint8_t tx_buf[USB_PKT_LEN];
|
||||||
|
|
||||||
|
bool buf_full;
|
||||||
|
} UsbUartParams;
|
||||||
|
|
||||||
|
static UsbUartParams* usb_uart;
|
||||||
|
static bool running = false;
|
||||||
|
|
||||||
|
static void vcp_on_cdc_tx_complete();
|
||||||
|
static void vcp_on_cdc_rx();
|
||||||
|
static void vcp_state_callback(uint8_t state);
|
||||||
|
static void vcp_on_cdc_control_line(uint8_t state);
|
||||||
|
static void vcp_on_line_config(struct usb_cdc_line_coding* config);
|
||||||
|
|
||||||
|
static CdcCallbacks cdc_cb = {
|
||||||
|
vcp_on_cdc_tx_complete,
|
||||||
|
vcp_on_cdc_rx,
|
||||||
|
vcp_state_callback,
|
||||||
|
vcp_on_cdc_control_line,
|
||||||
|
vcp_on_line_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* USB UART worker */
|
||||||
|
|
||||||
|
static int32_t usb_uart_tx_thread(void* context);
|
||||||
|
|
||||||
|
static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) {
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
|
if(ev == UartIrqEventRXNE) {
|
||||||
|
xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
|
||||||
|
|
||||||
|
size_t ret = xStreamBufferBytesAvailable(usb_uart->rx_stream);
|
||||||
|
if(ret > USB_PKT_LEN) osEventFlagsSet(usb_uart->events, WorkerEvtRxReady);
|
||||||
|
} else if(ev == UartIrqEventIDLE) {
|
||||||
|
osEventFlagsSet(usb_uart->events, WorkerEvtRxReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t usb_uart_worker(void* context) {
|
||||||
|
memcpy(&usb_uart->cfg, context, sizeof(UsbUartConfig));
|
||||||
|
|
||||||
|
usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1);
|
||||||
|
usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1);
|
||||||
|
|
||||||
|
usb_uart->tx_thread = furi_thread_alloc();
|
||||||
|
furi_thread_set_name(usb_uart->tx_thread, "usb_uart_tx");
|
||||||
|
furi_thread_set_stack_size(usb_uart->tx_thread, 512);
|
||||||
|
furi_thread_set_context(usb_uart->tx_thread, NULL);
|
||||||
|
furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread);
|
||||||
|
|
||||||
|
UsbMode usb_mode_prev = furi_hal_usb_get_config();
|
||||||
|
if(usb_uart->cfg.vcp_ch == 0) {
|
||||||
|
furi_hal_usb_set_config(UsbModeVcpSingle);
|
||||||
|
furi_hal_vcp_disable();
|
||||||
|
osEventFlagsSet(usb_uart->events, WorkerEvtSof);
|
||||||
|
} else {
|
||||||
|
furi_hal_usb_set_config(UsbModeVcpDual);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) {
|
||||||
|
furi_hal_console_disable();
|
||||||
|
} else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) {
|
||||||
|
furi_hal_uart_init(usb_uart->cfg.uart_ch, 115200);
|
||||||
|
furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb);
|
||||||
|
if(usb_uart->cfg.baudrate != 0)
|
||||||
|
furi_hal_uart_set_br(usb_uart->cfg.uart_ch, usb_uart->cfg.baudrate);
|
||||||
|
else
|
||||||
|
vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch));
|
||||||
|
|
||||||
|
furi_hal_cdc_set_callbacks(usb_uart->cfg.vcp_ch, &cdc_cb);
|
||||||
|
|
||||||
|
furi_thread_start(usb_uart->tx_thread);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
uint32_t events = osEventFlagsWait(
|
||||||
|
usb_uart->events, WORKER_ALL_RX_EVENTS, osFlagsWaitAny, osWaitForever);
|
||||||
|
furi_check((events & osFlagsError) == 0);
|
||||||
|
if(events & WorkerEvtStop) break;
|
||||||
|
if(events & WorkerEvtRxReady) {
|
||||||
|
size_t len = 0;
|
||||||
|
do {
|
||||||
|
len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0);
|
||||||
|
if(len > 0) {
|
||||||
|
if((osEventFlagsWait(usb_uart->events, WorkerEvtSof, osFlagsWaitAny, 100) &
|
||||||
|
osFlagsError) == 0)
|
||||||
|
furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len);
|
||||||
|
else
|
||||||
|
xStreamBufferReset(usb_uart->rx_stream);
|
||||||
|
}
|
||||||
|
} while(len > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osEventFlagsSet(usb_uart->events, WorkerEvtTxStop);
|
||||||
|
furi_thread_join(usb_uart->tx_thread);
|
||||||
|
furi_thread_free(usb_uart->tx_thread);
|
||||||
|
|
||||||
|
if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1)
|
||||||
|
furi_hal_console_enable();
|
||||||
|
else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1)
|
||||||
|
furi_hal_uart_deinit(usb_uart->cfg.uart_ch);
|
||||||
|
|
||||||
|
furi_hal_cdc_set_callbacks(usb_uart->cfg.vcp_ch, NULL);
|
||||||
|
furi_hal_usb_set_config(usb_mode_prev);
|
||||||
|
if(usb_uart->cfg.vcp_ch == 0) furi_hal_vcp_enable();
|
||||||
|
|
||||||
|
vStreamBufferDelete(usb_uart->rx_stream);
|
||||||
|
vStreamBufferDelete(usb_uart->tx_stream);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t usb_uart_tx_thread(void* context) {
|
||||||
|
uint8_t data[USB_PKT_LEN];
|
||||||
|
while(1) {
|
||||||
|
uint32_t events = osEventFlagsWait(
|
||||||
|
usb_uart->events, WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever);
|
||||||
|
furi_check((events & osFlagsError) == 0);
|
||||||
|
if(events & WorkerEvtTxStop) break;
|
||||||
|
if(events & WorkerEvtTxReady) {
|
||||||
|
size_t len = 0;
|
||||||
|
do {
|
||||||
|
len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, 0);
|
||||||
|
if(len > 0) {
|
||||||
|
furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len);
|
||||||
|
}
|
||||||
|
if((usb_uart->buf_full == true) &&
|
||||||
|
(xStreamBufferBytesAvailable(usb_uart->tx_stream) == 0)) {
|
||||||
|
// Stream buffer was overflown, but now is free. Reading USB buffer to resume USB transfers
|
||||||
|
usb_uart->buf_full = false;
|
||||||
|
int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_PKT_LEN);
|
||||||
|
if(size > 0) {
|
||||||
|
furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(len > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VCP callbacks */
|
||||||
|
|
||||||
|
static void vcp_on_cdc_tx_complete() {
|
||||||
|
osEventFlagsSet(usb_uart->events, WorkerEvtSof);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcp_on_cdc_rx() {
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
|
uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream);
|
||||||
|
if(max_len >= USB_PKT_LEN) {
|
||||||
|
//if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN;
|
||||||
|
int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, usb_uart->tx_buf, USB_PKT_LEN);
|
||||||
|
if(size > 0) {
|
||||||
|
size_t ret = xStreamBufferSendFromISR(
|
||||||
|
usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken);
|
||||||
|
furi_check(ret == size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
usb_uart->buf_full = true;
|
||||||
|
}
|
||||||
|
osEventFlagsSet(usb_uart->events, WorkerEvtTxReady);
|
||||||
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcp_state_callback(uint8_t state) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcp_on_cdc_control_line(uint8_t state) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcp_on_line_config(struct usb_cdc_line_coding* config) {
|
||||||
|
if((usb_uart->cfg.baudrate == 0) && (config->dwDTERate != 0))
|
||||||
|
furi_hal_uart_set_br(usb_uart->cfg.uart_ch, config->dwDTERate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_uart_enable(UsbUartConfig* cfg) {
|
||||||
|
if(running == false) {
|
||||||
|
running = true;
|
||||||
|
usb_uart = furi_alloc(sizeof(UsbUartParams));
|
||||||
|
|
||||||
|
usb_uart->thread = furi_thread_alloc();
|
||||||
|
furi_thread_set_name(usb_uart->thread, "usb_uart");
|
||||||
|
furi_thread_set_stack_size(usb_uart->thread, 1024);
|
||||||
|
furi_thread_set_context(usb_uart->thread, cfg);
|
||||||
|
furi_thread_set_callback(usb_uart->thread, usb_uart_worker);
|
||||||
|
|
||||||
|
usb_uart->events = osEventFlagsNew(NULL);
|
||||||
|
|
||||||
|
furi_thread_start(usb_uart->thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_uart_disable() {
|
||||||
|
if(running == true) {
|
||||||
|
osEventFlagsSet(usb_uart->events, WorkerEvtStop);
|
||||||
|
furi_thread_join(usb_uart->thread);
|
||||||
|
furi_thread_free(usb_uart->thread);
|
||||||
|
osEventFlagsDelete(usb_uart->events);
|
||||||
|
free(usb_uart);
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
applications/gpio/usb_uart_bridge.h
Normal file
13
applications/gpio/usb_uart_bridge.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t vcp_ch;
|
||||||
|
uint8_t uart_ch;
|
||||||
|
uint32_t baudrate;
|
||||||
|
} UsbUartConfig;
|
||||||
|
|
||||||
|
void usb_uart_enable(UsbUartConfig* cfg);
|
||||||
|
|
||||||
|
void usb_uart_disable();
|
||||||
36
applications/gui/canvas.c
Executable file → Normal file
36
applications/gui/canvas.c
Executable file → Normal file
@ -6,22 +6,32 @@
|
|||||||
#include <furi-hal.h>
|
#include <furi-hal.h>
|
||||||
#include <u8g2_glue.h>
|
#include <u8g2_glue.h>
|
||||||
|
|
||||||
|
const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
|
||||||
|
[FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2},
|
||||||
|
[FontSecondary] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2},
|
||||||
|
[FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2},
|
||||||
|
[FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0},
|
||||||
|
};
|
||||||
|
|
||||||
Canvas* canvas_init() {
|
Canvas* canvas_init() {
|
||||||
Canvas* canvas = furi_alloc(sizeof(Canvas));
|
Canvas* canvas = furi_alloc(sizeof(Canvas));
|
||||||
|
|
||||||
furi_hal_power_insomnia_enter();
|
furi_hal_power_insomnia_enter();
|
||||||
|
|
||||||
canvas->orientation = CanvasOrientationHorizontal;
|
// 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;
|
||||||
// send init sequence to the display, display is in sleep mode after this
|
// Initialize display
|
||||||
u8g2_InitDisplay(&canvas->fb);
|
u8g2_InitDisplay(&canvas->fb);
|
||||||
// wake up display
|
// Wake up display
|
||||||
u8g2_ClearBuffer(&canvas->fb);
|
|
||||||
u8g2_SetPowerSave(&canvas->fb, 0);
|
u8g2_SetPowerSave(&canvas->fb, 0);
|
||||||
u8g2_SendBuffer(&canvas->fb);
|
|
||||||
|
// Clear buffer and send to device
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_commit(canvas);
|
||||||
|
|
||||||
furi_hal_power_insomnia_exit();
|
furi_hal_power_insomnia_exit();
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,9 +42,12 @@ void canvas_free(Canvas* canvas) {
|
|||||||
|
|
||||||
void canvas_reset(Canvas* canvas) {
|
void canvas_reset(Canvas* canvas) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
|
|
||||||
canvas_clear(canvas);
|
canvas_clear(canvas);
|
||||||
|
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_set_font_direction(canvas, CanvasFontDirectionLeftToRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void canvas_commit(Canvas* canvas) {
|
void canvas_commit(Canvas* canvas) {
|
||||||
@ -86,6 +99,12 @@ uint8_t canvas_current_font_height(Canvas* canvas) {
|
|||||||
return font_height;
|
return font_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
furi_assert(font < FontTotalNumber);
|
||||||
|
return (CanvasFontParameters*)&canvas_font_params[font];
|
||||||
|
}
|
||||||
|
|
||||||
void canvas_clear(Canvas* canvas) {
|
void canvas_clear(Canvas* canvas) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
u8g2_ClearBuffer(&canvas->fb);
|
u8g2_ClearBuffer(&canvas->fb);
|
||||||
@ -96,6 +115,11 @@ void canvas_set_color(Canvas* canvas, Color color) {
|
|||||||
u8g2_SetDrawColor(&canvas->fb, color);
|
u8g2_SetDrawColor(&canvas->fb, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void canvas_set_font_direction(Canvas* canvas, CanvasFontDirection dir) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
u8g2_SetFontDirection(&canvas->fb, dir);
|
||||||
|
}
|
||||||
|
|
||||||
void canvas_invert_color(Canvas* canvas) {
|
void canvas_invert_color(Canvas* canvas) {
|
||||||
canvas->fb.draw_color = !canvas->fb.draw_color;
|
canvas->fb.draw_color = !canvas->fb.draw_color;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,15 @@ typedef enum {
|
|||||||
} Color;
|
} Color;
|
||||||
|
|
||||||
/** Fonts enumeration */
|
/** Fonts enumeration */
|
||||||
typedef enum { FontPrimary, FontSecondary, FontKeyboard, FontBigNumbers } Font;
|
typedef enum {
|
||||||
|
FontPrimary,
|
||||||
|
FontSecondary,
|
||||||
|
FontKeyboard,
|
||||||
|
FontBigNumbers,
|
||||||
|
|
||||||
|
// Keep last for fonts number calculation
|
||||||
|
FontTotalNumber,
|
||||||
|
} Font;
|
||||||
|
|
||||||
/** Alignment enumeration */
|
/** Alignment enumeration */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -37,6 +45,22 @@ typedef enum {
|
|||||||
CanvasOrientationVertical,
|
CanvasOrientationVertical,
|
||||||
} CanvasOrientation;
|
} CanvasOrientation;
|
||||||
|
|
||||||
|
/** Font Direction */
|
||||||
|
typedef enum {
|
||||||
|
CanvasFontDirectionLeftToRight,
|
||||||
|
CanvasFontDirectionTopToDown,
|
||||||
|
CanvasFontDirectionRightToLeft,
|
||||||
|
CanvasFontDirectionDownToTop,
|
||||||
|
} CanvasFontDirection;
|
||||||
|
|
||||||
|
/** Font parameters */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t leading_default;
|
||||||
|
uint8_t leading_min;
|
||||||
|
uint8_t height;
|
||||||
|
uint8_t descender;
|
||||||
|
} CanvasFontParameters;
|
||||||
|
|
||||||
/** Canvas anonymouse structure */
|
/** Canvas anonymouse structure */
|
||||||
typedef struct Canvas Canvas;
|
typedef struct Canvas Canvas;
|
||||||
|
|
||||||
@ -64,6 +88,15 @@ uint8_t canvas_height(Canvas* canvas);
|
|||||||
*/
|
*/
|
||||||
uint8_t canvas_current_font_height(Canvas* canvas);
|
uint8_t canvas_current_font_height(Canvas* canvas);
|
||||||
|
|
||||||
|
/** Get font parameters
|
||||||
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param font Font
|
||||||
|
*
|
||||||
|
* @return pointer to CanvasFontParameters structure
|
||||||
|
*/
|
||||||
|
CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font);
|
||||||
|
|
||||||
/** Clear canvas
|
/** Clear canvas
|
||||||
*
|
*
|
||||||
* @param canvas Canvas instance
|
* @param canvas Canvas instance
|
||||||
@ -77,6 +110,14 @@ void canvas_clear(Canvas* canvas);
|
|||||||
*/
|
*/
|
||||||
void canvas_set_color(Canvas* canvas, Color color);
|
void canvas_set_color(Canvas* canvas, Color color);
|
||||||
|
|
||||||
|
/** Set font swap
|
||||||
|
* Argument String Rotation Description
|
||||||
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param dir Direction font
|
||||||
|
*/
|
||||||
|
void canvas_set_font_direction(Canvas* canvas, CanvasFontDirection dir);
|
||||||
|
|
||||||
/** Invert drawing color
|
/** Invert drawing color
|
||||||
*
|
*
|
||||||
* @param canvas Canvas instance
|
* @param canvas Canvas instance
|
||||||
|
|||||||
215
applications/gui/elements.c
Normal file → Executable file
215
applications/gui/elements.c
Normal file → Executable file
@ -10,6 +10,18 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t x;
|
||||||
|
uint8_t y;
|
||||||
|
uint8_t leading_min;
|
||||||
|
uint8_t leading_default;
|
||||||
|
uint8_t height;
|
||||||
|
uint8_t descender;
|
||||||
|
uint8_t len;
|
||||||
|
const char* text;
|
||||||
|
} ElementTextBoxLine;
|
||||||
|
|
||||||
void elements_progress_bar(
|
void elements_progress_bar(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -352,3 +364,206 @@ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) {
|
|||||||
string_cat(string, "...");
|
string_cat(string, "...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void elements_text_box(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
|
||||||
|
ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM];
|
||||||
|
bool bold = false;
|
||||||
|
bool mono = false;
|
||||||
|
bool inversed = false;
|
||||||
|
bool inversed_present = false;
|
||||||
|
Font current_font = FontSecondary;
|
||||||
|
Font prev_font = FontSecondary;
|
||||||
|
CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font);
|
||||||
|
|
||||||
|
// Fill line parameters
|
||||||
|
uint8_t line_leading_min = font_params->leading_min;
|
||||||
|
uint8_t line_leading_default = font_params->leading_default;
|
||||||
|
uint8_t line_height = font_params->height;
|
||||||
|
uint8_t line_descender = font_params->descender;
|
||||||
|
uint8_t line_num = 0;
|
||||||
|
uint8_t line_width = 0;
|
||||||
|
uint8_t line_len = 0;
|
||||||
|
uint8_t total_height_min = 0;
|
||||||
|
uint8_t total_height_default = 0;
|
||||||
|
uint16_t i = 0;
|
||||||
|
bool full_text_processed = false;
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
// Fill all lines
|
||||||
|
line[0].text = text;
|
||||||
|
for(i = 0; !full_text_processed; i++) {
|
||||||
|
line_len++;
|
||||||
|
// Identify line height
|
||||||
|
if(prev_font != current_font) {
|
||||||
|
font_params = canvas_get_font_params(canvas, current_font);
|
||||||
|
line_leading_min = MAX(line_leading_min, font_params->leading_min);
|
||||||
|
line_leading_default = MAX(line_leading_default, font_params->leading_default);
|
||||||
|
line_height = MAX(line_height, font_params->height);
|
||||||
|
line_descender = MAX(line_descender, font_params->descender);
|
||||||
|
prev_font = current_font;
|
||||||
|
}
|
||||||
|
// Set the font
|
||||||
|
if(text[i] == '\e' && text[i + 1]) {
|
||||||
|
i++;
|
||||||
|
line_len++;
|
||||||
|
if(text[i] == ELEMENTS_BOLD_MARKER) {
|
||||||
|
if(bold) {
|
||||||
|
current_font = FontSecondary;
|
||||||
|
} else {
|
||||||
|
current_font = FontPrimary;
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, current_font);
|
||||||
|
bold = !bold;
|
||||||
|
}
|
||||||
|
if(text[i] == ELEMENTS_MONO_MARKER) {
|
||||||
|
if(mono) {
|
||||||
|
current_font = FontSecondary;
|
||||||
|
} else {
|
||||||
|
current_font = FontKeyboard;
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, FontKeyboard);
|
||||||
|
mono = !mono;
|
||||||
|
}
|
||||||
|
if(text[i] == ELEMENTS_INVERSED_MARKER) {
|
||||||
|
inversed_present = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(text[i] != '\n') {
|
||||||
|
line_width += canvas_glyph_width(canvas, text[i]);
|
||||||
|
}
|
||||||
|
// Process new line
|
||||||
|
if(text[i] == '\n' || text[i] == '\0' || line_width > width) {
|
||||||
|
if(line_width > width) {
|
||||||
|
line_width -= canvas_glyph_width(canvas, text[i--]);
|
||||||
|
line_len--;
|
||||||
|
}
|
||||||
|
if(text[i] == '\0') {
|
||||||
|
full_text_processed = true;
|
||||||
|
}
|
||||||
|
if(inversed_present) {
|
||||||
|
line_leading_min += 1;
|
||||||
|
line_leading_default += 1;
|
||||||
|
inversed_present = false;
|
||||||
|
}
|
||||||
|
line[line_num].leading_min = line_leading_min;
|
||||||
|
line[line_num].leading_default = line_leading_default;
|
||||||
|
line[line_num].height = line_height;
|
||||||
|
line[line_num].descender = line_descender;
|
||||||
|
if(total_height_min + line_leading_min > height) {
|
||||||
|
line_num--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
total_height_min += line_leading_min;
|
||||||
|
total_height_default += line_leading_default;
|
||||||
|
line[line_num].len = line_len;
|
||||||
|
if(horizontal == AlignCenter) {
|
||||||
|
line[line_num].x = x + (width - line_width) / 2;
|
||||||
|
} else if(horizontal == AlignRight) {
|
||||||
|
line[line_num].x = x + (width - line_width);
|
||||||
|
} else {
|
||||||
|
line[line_num].x = x;
|
||||||
|
}
|
||||||
|
line[line_num].y = total_height_min;
|
||||||
|
line_num++;
|
||||||
|
if(text[i + 1]) {
|
||||||
|
line[line_num].text = &text[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
line_leading_min = font_params->leading_min;
|
||||||
|
line_height = font_params->height;
|
||||||
|
line_descender = font_params->descender;
|
||||||
|
line_width = 0;
|
||||||
|
line_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set vertical alignment for all lines
|
||||||
|
if(full_text_processed) {
|
||||||
|
if(total_height_default < height) {
|
||||||
|
if(vertical == AlignTop) {
|
||||||
|
line[0].y = y + line[0].height;
|
||||||
|
} else if(vertical == AlignCenter) {
|
||||||
|
line[0].y = y + line[0].height + (height - total_height_default) / 2;
|
||||||
|
} else if(vertical == AlignBottom) {
|
||||||
|
line[0].y = y + line[0].height + (height - total_height_default);
|
||||||
|
}
|
||||||
|
if(line_num > 1) {
|
||||||
|
for(uint8_t i = 1; i < line_num; i++) {
|
||||||
|
line[i].y = line[i - 1].y + line[i - 1].leading_default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(line_num > 1) {
|
||||||
|
uint8_t free_pixel_num = height - total_height_min;
|
||||||
|
uint8_t fill_pixel = 0;
|
||||||
|
uint8_t j = 1;
|
||||||
|
line[0].y = line[0].height;
|
||||||
|
while(fill_pixel < free_pixel_num) {
|
||||||
|
line[j].y = line[j - 1].y + line[j - 1].leading_min + 1;
|
||||||
|
fill_pixel++;
|
||||||
|
j = j % (line_num - 1) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw line by line
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
bold = false;
|
||||||
|
mono = false;
|
||||||
|
inversed = false;
|
||||||
|
for(uint8_t i = 0; i < line_num; i++) {
|
||||||
|
for(uint8_t j = 0; j < line[i].len; j++) {
|
||||||
|
// Process format symbols
|
||||||
|
if(line[i].text[j] == ELEMENTS_BOLD_MARKER) {
|
||||||
|
if(bold) {
|
||||||
|
current_font = FontSecondary;
|
||||||
|
} else {
|
||||||
|
current_font = FontPrimary;
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, current_font);
|
||||||
|
bold = !bold;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(line[i].text[j] == ELEMENTS_MONO_MARKER) {
|
||||||
|
if(mono) {
|
||||||
|
current_font = FontSecondary;
|
||||||
|
} else {
|
||||||
|
current_font = FontKeyboard;
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, current_font);
|
||||||
|
mono = !mono;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(line[i].text[j] == ELEMENTS_INVERSED_MARKER) {
|
||||||
|
inversed = !inversed;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(inversed) {
|
||||||
|
canvas_draw_box(
|
||||||
|
canvas,
|
||||||
|
line[i].x - 1,
|
||||||
|
line[i].y - line[i].height - 1,
|
||||||
|
canvas_glyph_width(canvas, line[i].text[j]) + 1,
|
||||||
|
line[i].height + line[i].descender + 2);
|
||||||
|
canvas_invert_color(canvas);
|
||||||
|
canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);
|
||||||
|
canvas_invert_color(canvas);
|
||||||
|
} else {
|
||||||
|
canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);
|
||||||
|
}
|
||||||
|
line[i].x += canvas_glyph_width(canvas, line[i].text[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
}
|
||||||
|
|||||||
127
applications/gui/elements.h
Normal file → Executable file
127
applications/gui/elements.h
Normal file → Executable file
@ -16,12 +16,19 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ELEMENTS_MAX_LINES_NUM (7)
|
||||||
|
#define ELEMENTS_BOLD_MARKER '#'
|
||||||
|
#define ELEMENTS_MONO_MARKER '*'
|
||||||
|
#define ELEMENTS_INVERSED_MARKER '!'
|
||||||
|
|
||||||
/** Draw progress bar.
|
/** Draw progress bar.
|
||||||
* @param x - progress bar position on X axis
|
*
|
||||||
* @param y - progress bar position on Y axis
|
* @param canvas Canvas instance
|
||||||
* @param width - progress bar width
|
* @param x progress bar position on X axis
|
||||||
* @param progress - progress in unnamed metric
|
* @param y progress bar position on Y axis
|
||||||
* @param total - total amount in unnamed metric
|
* @param width progress bar width
|
||||||
|
* @param progress progress in unnamed metric
|
||||||
|
* @param total total amount in unnamed metric
|
||||||
*/
|
*/
|
||||||
void elements_progress_bar(
|
void elements_progress_bar(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -32,11 +39,13 @@ void elements_progress_bar(
|
|||||||
uint8_t total);
|
uint8_t total);
|
||||||
|
|
||||||
/** Draw scrollbar on canvas at specific position.
|
/** Draw scrollbar on canvas at specific position.
|
||||||
* @param x - scrollbar position on X axis
|
*
|
||||||
* @param y - scrollbar position on Y axis
|
* @param canvas Canvas instance
|
||||||
* @param height - scrollbar height
|
* @param x scrollbar position on X axis
|
||||||
* @param pos - current element
|
* @param y scrollbar position on Y axis
|
||||||
* @param total - total elements
|
* @param height scrollbar height
|
||||||
|
* @param pos current element
|
||||||
|
* @param total total elements
|
||||||
*/
|
*/
|
||||||
void elements_scrollbar_pos(
|
void elements_scrollbar_pos(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -47,37 +56,49 @@ void elements_scrollbar_pos(
|
|||||||
uint16_t total);
|
uint16_t total);
|
||||||
|
|
||||||
/** Draw scrollbar on canvas.
|
/** Draw scrollbar on canvas.
|
||||||
* width 3px, height equal to canvas height
|
* @note width 3px, height equal to canvas height
|
||||||
* @param pos - current element of total elements
|
*
|
||||||
* @param total - total elements
|
* @param canvas Canvas instance
|
||||||
|
* @param pos current element of total elements
|
||||||
|
* @param total total elements
|
||||||
*/
|
*/
|
||||||
void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total);
|
void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total);
|
||||||
|
|
||||||
/** Draw rounded frame
|
/** Draw rounded frame
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param width, height - frame width and height
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param width, height frame width and height
|
||||||
*/
|
*/
|
||||||
void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
||||||
|
|
||||||
/** Draw button in left corner
|
/** Draw button in left corner
|
||||||
* @param str - button text
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param str button text
|
||||||
*/
|
*/
|
||||||
void elements_button_left(Canvas* canvas, const char* str);
|
void elements_button_left(Canvas* canvas, const char* str);
|
||||||
|
|
||||||
/** Draw button in right corner
|
/** Draw button in right corner
|
||||||
* @param str - button text
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param str button text
|
||||||
*/
|
*/
|
||||||
void elements_button_right(Canvas* canvas, const char* str);
|
void elements_button_right(Canvas* canvas, const char* str);
|
||||||
|
|
||||||
/** Draw button in center
|
/** Draw button in center
|
||||||
* @param str - button text
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param str button text
|
||||||
*/
|
*/
|
||||||
void elements_button_center(Canvas* canvas, const char* str);
|
void elements_button_center(Canvas* canvas, const char* str);
|
||||||
|
|
||||||
/** Draw aligned multiline text
|
/** Draw aligned multiline text
|
||||||
* @param x, y - coordinates based on align param
|
*
|
||||||
* @param horizontal, vertical - aligment of multiline text
|
* @param canvas Canvas instance
|
||||||
* @param text - string (possible multiline)
|
* @param x, y coordinates based on align param
|
||||||
|
* @param horizontal, vertical aligment of multiline text
|
||||||
|
* @param text string (possible multiline)
|
||||||
*/
|
*/
|
||||||
void elements_multiline_text_aligned(
|
void elements_multiline_text_aligned(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -88,20 +109,26 @@ void elements_multiline_text_aligned(
|
|||||||
const char* text);
|
const char* text);
|
||||||
|
|
||||||
/** Draw multiline text
|
/** Draw multiline text
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param text - string (possible multiline)
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param text string (possible multiline)
|
||||||
*/
|
*/
|
||||||
void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
||||||
|
|
||||||
/** Draw framed multiline text
|
/** Draw framed multiline text
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param text - string (possible multiline)
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param text string (possible multiline)
|
||||||
*/
|
*/
|
||||||
void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text);
|
||||||
|
|
||||||
/** Draw slightly rounded frame
|
/** Draw slightly rounded frame
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param width, height - size of frame
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param width, height size of frame
|
||||||
*/
|
*/
|
||||||
void elements_slightly_rounded_frame(
|
void elements_slightly_rounded_frame(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -111,8 +138,10 @@ void elements_slightly_rounded_frame(
|
|||||||
uint8_t height);
|
uint8_t height);
|
||||||
|
|
||||||
/** Draw slightly rounded box
|
/** Draw slightly rounded box
|
||||||
* @param x, y - top left corner coordinates
|
*
|
||||||
* @param width, height - size of box
|
* @param canvas Canvas instance
|
||||||
|
* @param x, y top left corner coordinates
|
||||||
|
* @param width, height size of box
|
||||||
*/
|
*/
|
||||||
void elements_slightly_rounded_box(
|
void elements_slightly_rounded_box(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
@ -122,19 +151,47 @@ void elements_slightly_rounded_box(
|
|||||||
uint8_t height);
|
uint8_t height);
|
||||||
|
|
||||||
/** Draw bubble frame for text
|
/** Draw bubble frame for text
|
||||||
* @param x - left x coordinates
|
*
|
||||||
* @param y - top y coordinate
|
* @param canvas Canvas instance
|
||||||
* @param width - bubble width
|
* @param x left x coordinates
|
||||||
* @param height - bubble height
|
* @param y top y coordinate
|
||||||
|
* @param width bubble width
|
||||||
|
* @param height bubble height
|
||||||
*/
|
*/
|
||||||
void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height);
|
||||||
|
|
||||||
/** Trim string buffer to fit width in pixels
|
/** Trim string buffer to fit width in pixels
|
||||||
* @param string - string to trim
|
*
|
||||||
* @param width - max width
|
* @param canvas Canvas instance
|
||||||
|
* @param string string to trim
|
||||||
|
* @param width max width
|
||||||
*/
|
*/
|
||||||
void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width);
|
void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width);
|
||||||
|
|
||||||
|
/** Draw text box element
|
||||||
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param x x coordinate
|
||||||
|
* @param y y coordinate
|
||||||
|
* @param width width to fit text
|
||||||
|
* @param height height to fit text
|
||||||
|
* @param horizontal Align instance
|
||||||
|
* @param vertical Align instance
|
||||||
|
* @param[in] text Formatted text. The following formats are available:
|
||||||
|
* "\e#Bold text\e#" - bold font is used
|
||||||
|
* "\e*Monospaced text\e*" - monospaced font is used
|
||||||
|
* "\e#Inversed text\e#" - white text on black background
|
||||||
|
*/
|
||||||
|
void elements_text_box(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
475
applications/gui/modules/code_input.c
Normal file
475
applications/gui/modules/code_input.c
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
#include "code_input.h"
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
#define MAX_CODE_LEN 10
|
||||||
|
|
||||||
|
struct CodeInput {
|
||||||
|
View* view;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CodeInputStateVerify,
|
||||||
|
CodeInputStateUpdate,
|
||||||
|
CodeInputStateTotal,
|
||||||
|
} CodeInputStateEnum;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CodeInputFirst,
|
||||||
|
CodeInputSecond,
|
||||||
|
CodeInputTotal,
|
||||||
|
} CodeInputsEnum;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t state;
|
||||||
|
uint8_t current;
|
||||||
|
bool ext_update;
|
||||||
|
|
||||||
|
uint8_t input_length[CodeInputTotal];
|
||||||
|
uint8_t local_buffer[CodeInputTotal][MAX_CODE_LEN];
|
||||||
|
|
||||||
|
CodeInputOkCallback ok_callback;
|
||||||
|
CodeInputFailCallback fail_callback;
|
||||||
|
void* callback_context;
|
||||||
|
|
||||||
|
const char* header;
|
||||||
|
|
||||||
|
uint8_t* ext_buffer;
|
||||||
|
uint8_t* ext_buffer_length;
|
||||||
|
} CodeInputModel;
|
||||||
|
|
||||||
|
static const Icon* keys_assets[] = {
|
||||||
|
[InputKeyUp] = &I_ButtonUp_7x4,
|
||||||
|
[InputKeyDown] = &I_ButtonDown_7x4,
|
||||||
|
[InputKeyRight] = &I_ButtonRight_4x7,
|
||||||
|
[InputKeyLeft] = &I_ButtonLeft_4x7,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare buffers
|
||||||
|
*
|
||||||
|
* @param in Input buffer pointer
|
||||||
|
* @param len_in Input array length
|
||||||
|
* @param src Source buffer pointer
|
||||||
|
* @param len_src Source array length
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src) {
|
||||||
|
bool result = false;
|
||||||
|
do {
|
||||||
|
result = (len_in && len_src);
|
||||||
|
if(!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = (len_in == len_src);
|
||||||
|
if(!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < len_in; i++) {
|
||||||
|
result = (in[i] == src[i]);
|
||||||
|
if(!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare local buffers
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static bool code_input_compare_local(CodeInputModel* model) {
|
||||||
|
uint8_t* source = model->local_buffer[CodeInputFirst];
|
||||||
|
size_t source_length = model->input_length[CodeInputFirst];
|
||||||
|
|
||||||
|
uint8_t* input = model->local_buffer[CodeInputSecond];
|
||||||
|
size_t input_length = model->input_length[CodeInputSecond];
|
||||||
|
|
||||||
|
return code_input_compare(input, input_length, source, source_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare ext with local
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static bool code_input_compare_ext(CodeInputModel* model) {
|
||||||
|
uint8_t* input = model->local_buffer[CodeInputFirst];
|
||||||
|
size_t input_length = model->input_length[CodeInputFirst];
|
||||||
|
|
||||||
|
uint8_t* source = model->ext_buffer;
|
||||||
|
size_t source_length = *model->ext_buffer_length;
|
||||||
|
|
||||||
|
return code_input_compare(input, input_length, source, source_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set ext buffer
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_set_ext(CodeInputModel* model) {
|
||||||
|
*model->ext_buffer_length = model->input_length[CodeInputFirst];
|
||||||
|
for(size_t i = 0; i <= model->input_length[CodeInputFirst]; i++) {
|
||||||
|
model->ext_buffer[i] = model->local_buffer[CodeInputFirst][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw input sequence
|
||||||
|
*
|
||||||
|
* @param canvas
|
||||||
|
* @param buffer
|
||||||
|
* @param length
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param active
|
||||||
|
*/
|
||||||
|
static void code_input_draw_sequence(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t* buffer,
|
||||||
|
uint8_t length,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
bool active) {
|
||||||
|
uint8_t pos_x = x + 6;
|
||||||
|
uint8_t pos_y = y + 3;
|
||||||
|
|
||||||
|
if(active) canvas_draw_icon(canvas, x - 4, y + 5, &I_ButtonRightSmall_3x5);
|
||||||
|
|
||||||
|
elements_slightly_rounded_frame(canvas, x, y, 116, 15);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < length; i++) {
|
||||||
|
// maybe symmetrical assets? :-/
|
||||||
|
uint8_t offset_y = buffer[i] < 2 ? 2 + (buffer[i] * 2) : 1;
|
||||||
|
canvas_draw_icon(canvas, pos_x, pos_y + offset_y, keys_assets[buffer[i]]);
|
||||||
|
pos_x += buffer[i] > 1 ? 9 : 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset input count
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_reset_count(CodeInputModel* model) {
|
||||||
|
model->input_length[model->current] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Call input callback
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_call_ok_callback(CodeInputModel* model) {
|
||||||
|
if(model->ok_callback != NULL) {
|
||||||
|
model->ok_callback(model->callback_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Call changed callback
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_call_fail_callback(CodeInputModel* model) {
|
||||||
|
if(model->fail_callback != NULL) {
|
||||||
|
model->fail_callback(model->callback_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle Back button
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static bool code_input_handle_back(CodeInputModel* model) {
|
||||||
|
if(model->current && !model->input_length[model->current]) {
|
||||||
|
--model->current;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(model->input_length[model->current]) {
|
||||||
|
code_input_reset_count(model);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_input_call_fail_callback(model);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle OK button
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
static void code_input_handle_ok(CodeInputModel* model) {
|
||||||
|
switch(model->state) {
|
||||||
|
case CodeInputStateVerify:
|
||||||
|
|
||||||
|
if(code_input_compare_ext(model)) {
|
||||||
|
if(model->ext_update) {
|
||||||
|
model->state = CodeInputStateUpdate;
|
||||||
|
} else {
|
||||||
|
code_input_call_ok_callback(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code_input_reset_count(model);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CodeInputStateUpdate:
|
||||||
|
|
||||||
|
if(!model->current && model->input_length[model->current]) {
|
||||||
|
model->current++;
|
||||||
|
} else {
|
||||||
|
if(code_input_compare_local(model)) {
|
||||||
|
if(model->ext_update) {
|
||||||
|
code_input_set_ext(model);
|
||||||
|
}
|
||||||
|
code_input_call_ok_callback(model);
|
||||||
|
} else {
|
||||||
|
code_input_reset_count(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle input
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t code_input_push(uint8_t* buffer, size_t length, InputKey key) {
|
||||||
|
buffer[length] = key;
|
||||||
|
length = CLAMP(length + 1, MAX_CODE_LEN, 0);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle D-pad keys
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
static void code_input_handle_dpad(CodeInputModel* model, InputKey key) {
|
||||||
|
uint8_t at = model->current;
|
||||||
|
size_t new_length = code_input_push(model->local_buffer[at], model->input_length[at], key);
|
||||||
|
model->input_length[at] = new_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw callback
|
||||||
|
*
|
||||||
|
* @param canvas
|
||||||
|
* @param _model
|
||||||
|
*/
|
||||||
|
static void code_input_view_draw_callback(Canvas* canvas, void* _model) {
|
||||||
|
CodeInputModel* model = _model;
|
||||||
|
uint8_t y_offset = 0;
|
||||||
|
if(!strlen(model->header)) y_offset = 5;
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
|
canvas_draw_str(canvas, 2, 9, model->header);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
switch(model->state) {
|
||||||
|
case CodeInputStateVerify:
|
||||||
|
code_input_draw_sequence(
|
||||||
|
canvas,
|
||||||
|
model->local_buffer[CodeInputFirst],
|
||||||
|
model->input_length[CodeInputFirst],
|
||||||
|
6,
|
||||||
|
30 - y_offset,
|
||||||
|
true);
|
||||||
|
break;
|
||||||
|
case CodeInputStateUpdate:
|
||||||
|
code_input_draw_sequence(
|
||||||
|
canvas,
|
||||||
|
model->local_buffer[CodeInputFirst],
|
||||||
|
model->input_length[CodeInputFirst],
|
||||||
|
6,
|
||||||
|
14 - y_offset,
|
||||||
|
!model->current);
|
||||||
|
code_input_draw_sequence(
|
||||||
|
canvas,
|
||||||
|
model->local_buffer[CodeInputSecond],
|
||||||
|
model->input_length[CodeInputSecond],
|
||||||
|
6,
|
||||||
|
44 - y_offset,
|
||||||
|
model->current);
|
||||||
|
|
||||||
|
if(model->current) canvas_draw_str(canvas, 2, 39 - y_offset, "Repeat code");
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Input callback
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @param context
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
static bool code_input_view_input_callback(InputEvent* event, void* context) {
|
||||||
|
CodeInput* code_input = context;
|
||||||
|
furi_assert(code_input);
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||||
|
switch(event->key) {
|
||||||
|
case InputKeyBack:
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
consumed = code_input_handle_back(model);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InputKeyOk:
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
code_input_handle_ok(model);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
code_input_handle_dpad(model, event->key);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset all input-related data in model
|
||||||
|
*
|
||||||
|
* @param model CodeInputModel
|
||||||
|
*/
|
||||||
|
static void code_input_reset_model_input_data(CodeInputModel* model) {
|
||||||
|
model->current = 0;
|
||||||
|
model->input_length[CodeInputFirst] = 0;
|
||||||
|
model->input_length[CodeInputSecond] = 0;
|
||||||
|
model->ext_buffer = NULL;
|
||||||
|
model->ext_update = false;
|
||||||
|
model->state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate and initialize code input. This code input is used to enter codes.
|
||||||
|
*
|
||||||
|
* @return CodeInput instance pointer
|
||||||
|
*/
|
||||||
|
CodeInput* code_input_alloc() {
|
||||||
|
CodeInput* code_input = furi_alloc(sizeof(CodeInput));
|
||||||
|
code_input->view = view_alloc();
|
||||||
|
view_set_context(code_input->view, code_input);
|
||||||
|
view_allocate_model(code_input->view, ViewModelTypeLocking, sizeof(CodeInputModel));
|
||||||
|
view_set_draw_callback(code_input->view, code_input_view_draw_callback);
|
||||||
|
view_set_input_callback(code_input->view, code_input_view_input_callback);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
model->header = "";
|
||||||
|
model->ok_callback = NULL;
|
||||||
|
model->fail_callback = NULL;
|
||||||
|
model->callback_context = NULL;
|
||||||
|
code_input_reset_model_input_data(model);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return code_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deinitialize and free code input
|
||||||
|
*
|
||||||
|
* @param code_input Code input instance
|
||||||
|
*/
|
||||||
|
void code_input_free(CodeInput* code_input) {
|
||||||
|
furi_assert(code_input);
|
||||||
|
view_free(code_input->view);
|
||||||
|
free(code_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get code input view
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @return View instance that can be used for embedding
|
||||||
|
*/
|
||||||
|
View* code_input_get_view(CodeInput* code_input) {
|
||||||
|
furi_assert(code_input);
|
||||||
|
return code_input->view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set code input callbacks
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @param ok_callback input callback fn
|
||||||
|
* @param fail_callback code match callback fn
|
||||||
|
* @param callback_context callback context
|
||||||
|
* @param buffer buffer
|
||||||
|
* @param buffer_length ptr to buffer length uint
|
||||||
|
* @param ext_update true to update buffer
|
||||||
|
*/
|
||||||
|
void code_input_set_result_callback(
|
||||||
|
CodeInput* code_input,
|
||||||
|
CodeInputOkCallback ok_callback,
|
||||||
|
CodeInputFailCallback fail_callback,
|
||||||
|
void* callback_context,
|
||||||
|
uint8_t* buffer,
|
||||||
|
uint8_t* buffer_length,
|
||||||
|
bool ext_update) {
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
code_input_reset_model_input_data(model);
|
||||||
|
model->ok_callback = ok_callback;
|
||||||
|
model->fail_callback = fail_callback;
|
||||||
|
model->callback_context = callback_context;
|
||||||
|
|
||||||
|
model->ext_buffer = buffer;
|
||||||
|
model->ext_buffer_length = buffer_length;
|
||||||
|
model->state = (*buffer_length == 0) ? 1 : 0;
|
||||||
|
model->ext_update = ext_update;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set code input header text
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @param text text to be shown
|
||||||
|
*/
|
||||||
|
void code_input_set_header_text(CodeInput* code_input, const char* text) {
|
||||||
|
with_view_model(
|
||||||
|
code_input->view, (CodeInputModel * model) {
|
||||||
|
model->header = text;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
91
applications/gui/modules/code_input.h
Normal file
91
applications/gui/modules/code_input.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @file code_input.h
|
||||||
|
* GUI: CodeInput keyboard view module API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Code input anonymous structure */
|
||||||
|
typedef struct CodeInput CodeInput;
|
||||||
|
|
||||||
|
/** callback that is executed when entered code matches ext buffer */
|
||||||
|
typedef void (*CodeInputOkCallback)(void* context);
|
||||||
|
|
||||||
|
/** callback that is executed when entered code does not matches ext buffer */
|
||||||
|
typedef void (*CodeInputFailCallback)(void* context);
|
||||||
|
|
||||||
|
/** Allocate and initialize code input. This code input is used to enter codes.
|
||||||
|
*
|
||||||
|
* @return CodeInput instance pointer
|
||||||
|
*/
|
||||||
|
CodeInput* code_input_alloc();
|
||||||
|
|
||||||
|
/** Deinitialize and free code input
|
||||||
|
*
|
||||||
|
* @param code_input Code input instance
|
||||||
|
*/
|
||||||
|
void code_input_free(CodeInput* code_input);
|
||||||
|
|
||||||
|
/** Get code input view
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
*
|
||||||
|
* @return View instance that can be used for embedding
|
||||||
|
*/
|
||||||
|
View* code_input_get_view(CodeInput* code_input);
|
||||||
|
|
||||||
|
/** Set code input result callback
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @param ok_callback ok callback fn
|
||||||
|
* @param fail_callback fail callback fn
|
||||||
|
* @param callback_context callback context
|
||||||
|
* @param buffer buffer to use
|
||||||
|
* @param buffer_length buffer length
|
||||||
|
* @param update set true to update buffer
|
||||||
|
*/
|
||||||
|
void code_input_set_result_callback(
|
||||||
|
CodeInput* code_input,
|
||||||
|
CodeInputOkCallback ok_callback,
|
||||||
|
CodeInputFailCallback fail_callback,
|
||||||
|
void* callback_context,
|
||||||
|
uint8_t* buffer,
|
||||||
|
uint8_t* buffer_length,
|
||||||
|
bool update);
|
||||||
|
|
||||||
|
/** Set code input header text
|
||||||
|
*
|
||||||
|
* @param code_input code input instance
|
||||||
|
* @param text text to be shown
|
||||||
|
*/
|
||||||
|
void code_input_set_header_text(CodeInput* code_input, const char* text);
|
||||||
|
|
||||||
|
/** Compare two buffers
|
||||||
|
*
|
||||||
|
* @param in buffer to compare to source
|
||||||
|
* @param len_in length of input buffer
|
||||||
|
* @param src source buffer
|
||||||
|
* @param len_src length of insourceput buffer
|
||||||
|
* @return true if buffers match
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src);
|
||||||
|
|
||||||
|
/** Push input into the end of array
|
||||||
|
*
|
||||||
|
* @param buffer buffer
|
||||||
|
* @param length length of buffer
|
||||||
|
* @param key input key
|
||||||
|
* @return new length of input buffer
|
||||||
|
*/
|
||||||
|
size_t code_input_push(uint8_t* buffer, size_t length, InputKey key);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -146,6 +146,21 @@ void widget_add_string_element(
|
|||||||
widget_add_element(widget, string_element);
|
widget_add_element(widget, string_element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void widget_add_text_box_element(
|
||||||
|
Widget* widget,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text) {
|
||||||
|
furi_assert(widget);
|
||||||
|
WidgetElement* text_box_element =
|
||||||
|
widget_element_text_box_create(x, y, width, height, horizontal, vertical, text);
|
||||||
|
widget_add_element(widget, text_box_element);
|
||||||
|
}
|
||||||
|
|
||||||
void widget_add_button_element(
|
void widget_add_button_element(
|
||||||
Widget* widget,
|
Widget* widget,
|
||||||
GuiButtonType button_type,
|
GuiButtonType button_type,
|
||||||
|
|||||||
@ -75,6 +75,30 @@ void widget_add_string_element(
|
|||||||
Font font,
|
Font font,
|
||||||
const char* text);
|
const char* text);
|
||||||
|
|
||||||
|
/** Add Text Box Element
|
||||||
|
*
|
||||||
|
* @param widget Widget instance
|
||||||
|
* @param x x coordinate
|
||||||
|
* @param y y coordinate
|
||||||
|
* @param width width to fit text
|
||||||
|
* @param height height to fit text
|
||||||
|
* @param horizontal Align instance
|
||||||
|
* @param vertical Align instance
|
||||||
|
* @param[in] text Formatted text. The following formats are available:
|
||||||
|
* "\e#Bold text\e#" - bold font is used
|
||||||
|
* "\e*Monospaced text\e*" - monospaced font is used
|
||||||
|
* "\e#Inversed text\e#" - white text on black background
|
||||||
|
*/
|
||||||
|
void widget_add_text_box_element(
|
||||||
|
Widget* widget,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text);
|
||||||
|
|
||||||
/** Add Button Element
|
/** Add Button Element
|
||||||
*
|
*
|
||||||
* @param widget Widget instance
|
* @param widget Widget instance
|
||||||
|
|||||||
@ -52,6 +52,16 @@ WidgetElement* widget_element_string_create(
|
|||||||
Font font,
|
Font font,
|
||||||
const char* text);
|
const char* text);
|
||||||
|
|
||||||
|
/** Create text box element */
|
||||||
|
WidgetElement* widget_element_text_box_create(
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text);
|
||||||
|
|
||||||
/** Create button element */
|
/** Create button element */
|
||||||
WidgetElement* widget_element_button_create(
|
WidgetElement* widget_element_button_create(
|
||||||
GuiButtonType button_type,
|
GuiButtonType button_type,
|
||||||
|
|||||||
@ -0,0 +1,71 @@
|
|||||||
|
#include "widget_element_i.h"
|
||||||
|
#include <m-string.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t x;
|
||||||
|
uint8_t y;
|
||||||
|
uint8_t width;
|
||||||
|
uint8_t height;
|
||||||
|
Align horizontal;
|
||||||
|
Align vertical;
|
||||||
|
string_t text;
|
||||||
|
} GuiTextBoxModel;
|
||||||
|
|
||||||
|
static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
furi_assert(element);
|
||||||
|
GuiTextBoxModel* model = element->model;
|
||||||
|
|
||||||
|
if(string_size(model->text)) {
|
||||||
|
elements_text_box(
|
||||||
|
canvas,
|
||||||
|
model->x,
|
||||||
|
model->y,
|
||||||
|
model->width,
|
||||||
|
model->height,
|
||||||
|
model->horizontal,
|
||||||
|
model->vertical,
|
||||||
|
string_get_cstr(model->text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gui_text_box_free(WidgetElement* gui_string) {
|
||||||
|
furi_assert(gui_string);
|
||||||
|
|
||||||
|
GuiTextBoxModel* model = gui_string->model;
|
||||||
|
string_clear(model->text);
|
||||||
|
free(gui_string->model);
|
||||||
|
free(gui_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetElement* widget_element_text_box_create(
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
Align horizontal,
|
||||||
|
Align vertical,
|
||||||
|
const char* text) {
|
||||||
|
furi_assert(text);
|
||||||
|
|
||||||
|
// Allocate and init model
|
||||||
|
GuiTextBoxModel* model = furi_alloc(sizeof(GuiTextBoxModel));
|
||||||
|
model->x = x;
|
||||||
|
model->y = y;
|
||||||
|
model->width = width;
|
||||||
|
model->height = height;
|
||||||
|
model->horizontal = horizontal;
|
||||||
|
model->vertical = vertical;
|
||||||
|
string_init_set_str(model->text, text);
|
||||||
|
|
||||||
|
// Allocate and init Element
|
||||||
|
WidgetElement* gui_string = furi_alloc(sizeof(WidgetElement));
|
||||||
|
gui_string->parent = NULL;
|
||||||
|
gui_string->input = NULL;
|
||||||
|
gui_string->draw = gui_text_box_draw;
|
||||||
|
gui_string->free = gui_text_box_free;
|
||||||
|
gui_string->model = model;
|
||||||
|
|
||||||
|
return gui_string;
|
||||||
|
}
|
||||||
@ -12,8 +12,8 @@ void nfc_scene_delete_on_enter(void* context) {
|
|||||||
|
|
||||||
// Setup Custom Widget view
|
// Setup Custom Widget view
|
||||||
char delete_str[64];
|
char delete_str[64];
|
||||||
snprintf(delete_str, sizeof(delete_str), "Delete %s", nfc->dev.dev_name);
|
snprintf(delete_str, sizeof(delete_str), "\e#Delete %s\e#", nfc->dev.dev_name);
|
||||||
widget_add_string_element(nfc->widget, 64, 6, AlignCenter, AlignTop, FontPrimary, delete_str);
|
widget_add_text_box_element(nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, delete_str);
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
|
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
|
|||||||
@ -35,8 +35,8 @@ void nfc_scene_device_info_on_enter(void* context) {
|
|||||||
Nfc* nfc = context;
|
Nfc* nfc = context;
|
||||||
|
|
||||||
// Setup Custom Widget view
|
// Setup Custom Widget view
|
||||||
widget_add_string_element(
|
widget_add_text_box_element(
|
||||||
nfc->widget, 64, 6, AlignCenter, AlignTop, FontSecondary, nfc->dev.dev_name);
|
nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, nfc->dev.dev_name);
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
|
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
|
|||||||
@ -23,7 +23,7 @@ void power_cli_factory_reset(Cli* cli, string_t args, void* context) {
|
|||||||
char c = cli_getc(cli);
|
char c = cli_getc(cli);
|
||||||
if(c == 'y' || c == 'Y') {
|
if(c == 'y' || c == 'Y') {
|
||||||
printf("Data will be wiped after reboot.\r\n");
|
printf("Data will be wiped after reboot.\r\n");
|
||||||
furi_hal_boot_set_flags(FuriHalBootFlagFactoryReset);
|
furi_hal_bootloader_set_flags(FuriHalBootloaderFlagFactoryReset);
|
||||||
power_reboot(PowerBootModeNormal);
|
power_reboot(PowerBootModeNormal);
|
||||||
} else {
|
} else {
|
||||||
printf("Safe choice.\r\n");
|
printf("Safe choice.\r\n");
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#include "power_i.h"
|
#include "power_i.h"
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include "furi-hal-power.h"
|
#include "furi-hal-power.h"
|
||||||
#include "furi-hal-boot.h"
|
#include "furi-hal-bootloader.h"
|
||||||
|
|
||||||
void power_off(Power* power) {
|
void power_off(Power* power) {
|
||||||
furi_hal_power_off();
|
furi_hal_power_off();
|
||||||
@ -14,9 +14,9 @@ void power_off(Power* power) {
|
|||||||
|
|
||||||
void power_reboot(PowerBootMode mode) {
|
void power_reboot(PowerBootMode mode) {
|
||||||
if(mode == PowerBootModeNormal) {
|
if(mode == PowerBootModeNormal) {
|
||||||
furi_hal_boot_set_mode(FuriHalBootModeNormal);
|
furi_hal_bootloader_set_mode(FuriHalBootloaderModeNormal);
|
||||||
} else if(mode == PowerBootModeDfu) {
|
} else if(mode == PowerBootModeDfu) {
|
||||||
furi_hal_boot_set_mode(FuriHalBootModeDFU);
|
furi_hal_bootloader_set_mode(FuriHalBootloaderModeDFU);
|
||||||
}
|
}
|
||||||
furi_hal_power_reset();
|
furi_hal_power_reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,20 @@
|
|||||||
#include "cmsis_os.h"
|
#include "rpc_i.h"
|
||||||
#include "cmsis_os2.h"
|
#include <pb.h>
|
||||||
#include "flipper.pb.h"
|
#include <pb_decode.h>
|
||||||
#include "furi-hal-delay.h"
|
#include <pb_encode.h>
|
||||||
#include "furi/check.h"
|
#include <status.pb.h>
|
||||||
#include "furi/log.h"
|
#include <storage.pb.h>
|
||||||
#include <m-string.h>
|
#include <flipper.pb.h>
|
||||||
#include "pb.h"
|
#include <cmsis_os.h>
|
||||||
#include "pb_decode.h"
|
#include <cmsis_os2.h>
|
||||||
#include "pb_encode.h"
|
#include <portmacro.h>
|
||||||
#include "portmacro.h"
|
#include <furi.h>
|
||||||
#include "status.pb.h"
|
#include <cli/cli.h>
|
||||||
#include "storage.pb.h"
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <furi.h>
|
|
||||||
#include <stream_buffer.h>
|
#include <stream_buffer.h>
|
||||||
|
#include <m-string.h>
|
||||||
#include <m-dict.h>
|
#include <m-dict.h>
|
||||||
#include "rpc_i.h"
|
|
||||||
|
|
||||||
#define RPC_TAG "RPC"
|
#define RPC_TAG "RPC"
|
||||||
|
|
||||||
@ -51,10 +49,11 @@ static RpcSystemCallbacks rpc_systems[] = {
|
|||||||
|
|
||||||
struct RpcSession {
|
struct RpcSession {
|
||||||
RpcSendBytesCallback send_bytes_callback;
|
RpcSendBytesCallback send_bytes_callback;
|
||||||
void* send_bytes_context;
|
RpcSessionClosedCallback closed_callback;
|
||||||
osMutexId_t send_bytes_mutex;
|
void* context;
|
||||||
|
osMutexId_t callbacks_mutex;
|
||||||
Rpc* rpc;
|
Rpc* rpc;
|
||||||
bool terminate_session;
|
bool terminate;
|
||||||
void** system_contexts;
|
void** system_contexts;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,6 +69,20 @@ struct Rpc {
|
|||||||
|
|
||||||
static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg);
|
static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg);
|
||||||
|
|
||||||
|
static void rpc_close_session_process(const PB_Main* msg_request, void* context) {
|
||||||
|
furi_assert(msg_request);
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
Rpc* rpc = context;
|
||||||
|
rpc_send_and_release_empty(rpc, msg_request->command_id, PB_CommandStatus_OK);
|
||||||
|
|
||||||
|
osMutexAcquire(rpc->session.callbacks_mutex, osWaitForever);
|
||||||
|
if(rpc->session.closed_callback) {
|
||||||
|
rpc->session.closed_callback(rpc->session.context);
|
||||||
|
}
|
||||||
|
osMutexRelease(rpc->session.callbacks_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
static size_t rpc_sprintf_msg_file(
|
static size_t rpc_sprintf_msg_file(
|
||||||
string_t str,
|
string_t str,
|
||||||
const char* prefix,
|
const char* prefix,
|
||||||
@ -105,6 +118,31 @@ static size_t rpc_sprintf_msg_file(
|
|||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rpc_print_data(const char* prefix, uint8_t* buffer, size_t size) {
|
||||||
|
string_t str;
|
||||||
|
string_init(str);
|
||||||
|
string_reserve(str, 100 + size * 5);
|
||||||
|
|
||||||
|
string_cat_printf(str, "\r\n%s DEC(%d): {", prefix, size);
|
||||||
|
for(int i = 0; i < size; ++i) {
|
||||||
|
string_cat_printf(str, "%d, ", buffer[i]);
|
||||||
|
}
|
||||||
|
string_cat_printf(str, "}\r\n");
|
||||||
|
|
||||||
|
printf("%s", string_get_cstr(str));
|
||||||
|
string_clean(str);
|
||||||
|
string_reserve(str, 100 + size * 3);
|
||||||
|
|
||||||
|
string_cat_printf(str, "%s HEX(%d): {", prefix, size);
|
||||||
|
for(int i = 0; i < size; ++i) {
|
||||||
|
string_cat_printf(str, "%02X", buffer[i]);
|
||||||
|
}
|
||||||
|
string_cat_printf(str, "}\r\n\r\n");
|
||||||
|
|
||||||
|
printf("%s", string_get_cstr(str));
|
||||||
|
string_clear(str);
|
||||||
|
}
|
||||||
|
|
||||||
void rpc_print_message(const PB_Main* message) {
|
void rpc_print_message(const PB_Main* message) {
|
||||||
string_t str;
|
string_t str;
|
||||||
string_init(str);
|
string_init(str);
|
||||||
@ -120,6 +158,9 @@ void rpc_print_message(const PB_Main* message) {
|
|||||||
/* not implemented yet */
|
/* not implemented yet */
|
||||||
string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content);
|
string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content);
|
||||||
break;
|
break;
|
||||||
|
case PB_Main_stop_session_tag:
|
||||||
|
string_cat_printf(str, "\tstop_session {\r\n");
|
||||||
|
break;
|
||||||
case PB_Main_app_start_tag: {
|
case PB_Main_app_start_tag: {
|
||||||
string_cat_printf(str, "\tapp_start {\r\n");
|
string_cat_printf(str, "\tapp_start {\r\n");
|
||||||
const char* name = message->content.app_start.name;
|
const char* name = message->content.app_start.name;
|
||||||
@ -242,7 +283,7 @@ static Rpc* rpc_alloc(void) {
|
|||||||
return rpc;
|
return rpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
RpcSession* rpc_open_session(Rpc* rpc) {
|
RpcSession* rpc_session_open(Rpc* rpc) {
|
||||||
furi_assert(rpc);
|
furi_assert(rpc);
|
||||||
bool result = false;
|
bool result = false;
|
||||||
furi_check(osMutexAcquire(rpc->busy_mutex, osWaitForever) == osOK);
|
furi_check(osMutexAcquire(rpc->busy_mutex, osWaitForever) == osOK);
|
||||||
@ -256,41 +297,94 @@ RpcSession* rpc_open_session(Rpc* rpc) {
|
|||||||
|
|
||||||
if(result) {
|
if(result) {
|
||||||
RpcSession* session = &rpc->session;
|
RpcSession* session = &rpc->session;
|
||||||
session->send_bytes_mutex = osMutexNew(NULL);
|
session->callbacks_mutex = osMutexNew(NULL);
|
||||||
session->rpc = rpc;
|
session->rpc = rpc;
|
||||||
session->terminate_session = false;
|
session->terminate = false;
|
||||||
|
xStreamBufferReset(rpc->stream);
|
||||||
|
|
||||||
session->system_contexts = furi_alloc(COUNT_OF(rpc_systems) * sizeof(void*));
|
session->system_contexts = furi_alloc(COUNT_OF(rpc_systems) * sizeof(void*));
|
||||||
for(int i = 0; i < COUNT_OF(rpc_systems); ++i) {
|
for(int i = 0; i < COUNT_OF(rpc_systems); ++i) {
|
||||||
session->system_contexts[i] = rpc_systems[i].alloc(rpc);
|
session->system_contexts[i] = rpc_systems[i].alloc(rpc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RpcHandler rpc_handler = {
|
||||||
|
.message_handler = rpc_close_session_process,
|
||||||
|
.decode_submessage = NULL,
|
||||||
|
.context = rpc,
|
||||||
|
};
|
||||||
|
rpc_add_handler(rpc, PB_Main_stop_session_tag, &rpc_handler);
|
||||||
|
|
||||||
FURI_LOG_D(RPC_TAG, "Session started\r\n");
|
FURI_LOG_D(RPC_TAG, "Session started\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result ? &rpc->session : NULL; /* support 1 open session for now */
|
return result ? &rpc->session : NULL; /* support 1 open session for now */
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpc_close_session(RpcSession* session) {
|
void rpc_session_close(RpcSession* session) {
|
||||||
furi_assert(session);
|
furi_assert(session);
|
||||||
furi_assert(session->rpc);
|
furi_assert(session->rpc);
|
||||||
furi_assert(session->rpc->busy);
|
furi_assert(session->rpc->busy);
|
||||||
|
|
||||||
rpc_set_send_bytes_callback(session, NULL, NULL);
|
rpc_session_set_send_bytes_callback(session, NULL);
|
||||||
|
rpc_session_set_close_callback(session, NULL);
|
||||||
osEventFlagsSet(session->rpc->events, RPC_EVENT_DISCONNECT);
|
osEventFlagsSet(session->rpc->events, RPC_EVENT_DISCONNECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpc_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback, void* context) {
|
static void rpc_free_session(RpcSession* session) {
|
||||||
|
furi_assert(session);
|
||||||
|
|
||||||
|
for(int i = 0; i < COUNT_OF(rpc_systems); ++i) {
|
||||||
|
if(rpc_systems[i].free) {
|
||||||
|
rpc_systems[i].free(session->system_contexts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(session->system_contexts);
|
||||||
|
osMutexDelete(session->callbacks_mutex);
|
||||||
|
RpcHandlerDict_clean(session->rpc->handlers);
|
||||||
|
|
||||||
|
session->context = NULL;
|
||||||
|
session->closed_callback = NULL;
|
||||||
|
session->send_bytes_callback = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_session_set_context(RpcSession* session, void* context) {
|
||||||
furi_assert(session);
|
furi_assert(session);
|
||||||
furi_assert(session->rpc);
|
furi_assert(session->rpc);
|
||||||
furi_assert(session->rpc->busy);
|
furi_assert(session->rpc->busy);
|
||||||
|
|
||||||
osMutexAcquire(session->send_bytes_mutex, osWaitForever);
|
osMutexAcquire(session->callbacks_mutex, osWaitForever);
|
||||||
session->send_bytes_callback = callback;
|
session->context = context;
|
||||||
session->send_bytes_context = context;
|
osMutexRelease(session->callbacks_mutex);
|
||||||
osMutexRelease(session->send_bytes_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback) {
|
||||||
|
furi_assert(session);
|
||||||
|
furi_assert(session->rpc);
|
||||||
|
furi_assert(session->rpc->busy);
|
||||||
|
|
||||||
|
osMutexAcquire(session->callbacks_mutex, osWaitForever);
|
||||||
|
session->closed_callback = callback;
|
||||||
|
osMutexRelease(session->callbacks_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback) {
|
||||||
|
furi_assert(session);
|
||||||
|
furi_assert(session->rpc);
|
||||||
|
furi_assert(session->rpc->busy);
|
||||||
|
|
||||||
|
osMutexAcquire(session->callbacks_mutex, osWaitForever);
|
||||||
|
session->send_bytes_callback = callback;
|
||||||
|
osMutexRelease(session->callbacks_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Doesn't forbid using rpc_feed_bytes() after session close - it's safe.
|
||||||
|
* Because any bytes received in buffer will be flushed before next session.
|
||||||
|
* If bytes get into stream buffer before it's get epmtified and this
|
||||||
|
* command is gets processed - it's safe either. But case of it is quite
|
||||||
|
* odd: client sends close request and sends command after.
|
||||||
|
*/
|
||||||
size_t
|
size_t
|
||||||
rpc_feed_bytes(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) {
|
rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) {
|
||||||
furi_assert(session);
|
furi_assert(session);
|
||||||
Rpc* rpc = session->rpc;
|
Rpc* rpc = session->rpc;
|
||||||
furi_assert(rpc->busy);
|
furi_assert(rpc->busy);
|
||||||
@ -306,6 +400,8 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
|
|||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
size_t bytes_received = 0;
|
size_t bytes_received = 0;
|
||||||
|
|
||||||
|
furi_assert(istream->bytes_left);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
bytes_received +=
|
bytes_received +=
|
||||||
xStreamBufferReceive(rpc->stream, buf + bytes_received, count - bytes_received, 0);
|
xStreamBufferReceive(rpc->stream, buf + bytes_received, count - bytes_received, 0);
|
||||||
@ -315,7 +411,9 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
|
|||||||
flags = osEventFlagsWait(rpc->events, RPC_EVENTS_ALL, 0, osWaitForever);
|
flags = osEventFlagsWait(rpc->events, RPC_EVENTS_ALL, 0, osWaitForever);
|
||||||
if(flags & RPC_EVENT_DISCONNECT) {
|
if(flags & RPC_EVENT_DISCONNECT) {
|
||||||
if(xStreamBufferIsEmpty(rpc->stream)) {
|
if(xStreamBufferIsEmpty(rpc->stream)) {
|
||||||
rpc->session.terminate_session = true;
|
rpc->session.terminate = true;
|
||||||
|
istream->bytes_left = 0;
|
||||||
|
bytes_received = 0;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
/* Save disconnect flag and continue reading buffer */
|
/* Save disconnect flag and continue reading buffer */
|
||||||
@ -325,61 +423,44 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
rpc_print_data("INPUT", buf, bytes_received);
|
||||||
|
#endif
|
||||||
|
|
||||||
return (count == bytes_received);
|
return (count == bytes_received);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpc_encode_and_send(Rpc* rpc, PB_Main* main_message) {
|
void rpc_send_and_release(Rpc* rpc, PB_Main* message) {
|
||||||
furi_assert(rpc);
|
furi_assert(rpc);
|
||||||
furi_assert(main_message);
|
furi_assert(message);
|
||||||
RpcSession* session = &rpc->session;
|
RpcSession* session = &rpc->session;
|
||||||
pb_ostream_t ostream = PB_OSTREAM_SIZING;
|
pb_ostream_t ostream = PB_OSTREAM_SIZING;
|
||||||
|
|
||||||
#if DEBUG_PRINT
|
#if DEBUG_PRINT
|
||||||
FURI_LOG_I(RPC_TAG, "OUTPUT:");
|
FURI_LOG_I(RPC_TAG, "OUTPUT:");
|
||||||
rpc_print_message(main_message);
|
rpc_print_message(message);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool result = pb_encode_ex(&ostream, &PB_Main_msg, main_message, PB_ENCODE_DELIMITED);
|
bool result = pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED);
|
||||||
furi_check(result && ostream.bytes_written);
|
furi_check(result && ostream.bytes_written);
|
||||||
|
|
||||||
uint8_t* buffer = furi_alloc(ostream.bytes_written);
|
uint8_t* buffer = furi_alloc(ostream.bytes_written);
|
||||||
ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written);
|
ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written);
|
||||||
|
|
||||||
pb_encode_ex(&ostream, &PB_Main_msg, main_message, PB_ENCODE_DELIMITED);
|
pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED);
|
||||||
|
|
||||||
{
|
|
||||||
#if DEBUG_PRINT
|
#if DEBUG_PRINT
|
||||||
string_t str;
|
rpc_print_data("OUTPUT", buffer, ostream.bytes_written);
|
||||||
string_init(str);
|
#endif
|
||||||
string_reserve(str, 100 + ostream.bytes_written * 5);
|
|
||||||
|
|
||||||
string_cat_printf(str, "\r\nREPONSE DEC(%d): {", ostream.bytes_written);
|
osMutexAcquire(session->callbacks_mutex, osWaitForever);
|
||||||
for(int i = 0; i < ostream.bytes_written; ++i) {
|
if(session->send_bytes_callback) {
|
||||||
string_cat_printf(str, "%d, ", buffer[i]);
|
session->send_bytes_callback(session->context, buffer, ostream.bytes_written);
|
||||||
}
|
|
||||||
string_cat_printf(str, "}\r\n");
|
|
||||||
|
|
||||||
printf("%s", string_get_cstr(str));
|
|
||||||
string_clean(str);
|
|
||||||
string_reserve(str, 100 + ostream.bytes_written * 3);
|
|
||||||
|
|
||||||
string_cat_printf(str, "REPONSE HEX(%d): {", ostream.bytes_written);
|
|
||||||
for(int i = 0; i < ostream.bytes_written; ++i) {
|
|
||||||
string_cat_printf(str, "%02X", buffer[i]);
|
|
||||||
}
|
|
||||||
string_cat_printf(str, "}\r\n\r\n");
|
|
||||||
|
|
||||||
printf("%s", string_get_cstr(str));
|
|
||||||
#endif // DEBUG_PRINT
|
|
||||||
|
|
||||||
osMutexAcquire(session->send_bytes_mutex, osWaitForever);
|
|
||||||
if(session->send_bytes_callback) {
|
|
||||||
session->send_bytes_callback(
|
|
||||||
session->send_bytes_context, buffer, ostream.bytes_written);
|
|
||||||
}
|
|
||||||
osMutexRelease(session->send_bytes_mutex);
|
|
||||||
}
|
}
|
||||||
|
osMutexRelease(session->callbacks_mutex);
|
||||||
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
pb_release(&PB_Main_msg, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) {
|
static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) {
|
||||||
@ -399,12 +480,17 @@ int32_t rpc_srv(void* p) {
|
|||||||
Rpc* rpc = rpc_alloc();
|
Rpc* rpc = rpc_alloc();
|
||||||
furi_record_create("rpc", rpc);
|
furi_record_create("rpc", rpc);
|
||||||
|
|
||||||
|
Cli* cli = furi_record_open("cli");
|
||||||
|
|
||||||
|
cli_add_command(
|
||||||
|
cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
pb_istream_t istream = {
|
pb_istream_t istream = {
|
||||||
.callback = rpc_pb_stream_read,
|
.callback = rpc_pb_stream_read,
|
||||||
.state = rpc,
|
.state = rpc,
|
||||||
.errmsg = NULL,
|
.errmsg = NULL,
|
||||||
.bytes_left = 0x7FFFFFFF,
|
.bytes_left = 1024, /* max incoming message size */
|
||||||
};
|
};
|
||||||
|
|
||||||
if(pb_decode_ex(&istream, &PB_Main_msg, rpc->decoded_message, PB_DECODE_DELIMITED)) {
|
if(pb_decode_ex(&istream, &PB_Main_msg, rpc->decoded_message, PB_DECODE_DELIMITED)) {
|
||||||
@ -417,35 +503,25 @@ int32_t rpc_srv(void* p) {
|
|||||||
|
|
||||||
if(handler && handler->message_handler) {
|
if(handler && handler->message_handler) {
|
||||||
handler->message_handler(rpc->decoded_message, handler->context);
|
handler->message_handler(rpc->decoded_message, handler->context);
|
||||||
} else if(!handler) {
|
} else if(!handler && !rpc->session.terminate) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
RPC_TAG,
|
RPC_TAG, "Unhandled message, tag: %d", rpc->decoded_message->which_content);
|
||||||
"Unhandled message, tag: %d\r\n",
|
|
||||||
rpc->decoded_message->which_content);
|
|
||||||
}
|
}
|
||||||
pb_release(&PB_Main_msg, rpc->decoded_message);
|
|
||||||
} else {
|
} else {
|
||||||
pb_release(&PB_Main_msg, rpc->decoded_message);
|
xStreamBufferReset(rpc->stream);
|
||||||
RpcSession* session = &rpc->session;
|
if(!rpc->session.terminate) {
|
||||||
if(session->terminate_session) {
|
FURI_LOG_E(RPC_TAG, "Decode failed, error: \'%.128s\'", PB_GET_ERROR(&istream));
|
||||||
session->terminate_session = false;
|
|
||||||
osEventFlagsClear(rpc->events, RPC_EVENTS_ALL);
|
|
||||||
FURI_LOG_D(RPC_TAG, "Session terminated\r\n");
|
|
||||||
for(int i = 0; i < COUNT_OF(rpc_systems); ++i) {
|
|
||||||
if(rpc_systems[i].free) {
|
|
||||||
rpc_systems[i].free(session->system_contexts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(session->system_contexts);
|
|
||||||
osMutexDelete(session->send_bytes_mutex);
|
|
||||||
RpcHandlerDict_clean(rpc->handlers);
|
|
||||||
rpc->busy = false;
|
|
||||||
} else {
|
|
||||||
xStreamBufferReset(rpc->stream);
|
|
||||||
FURI_LOG_E(
|
|
||||||
RPC_TAG, "Decode failed, error: \'%.128s\'\r\n", PB_GET_ERROR(&istream));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pb_release(&PB_Main_msg, rpc->decoded_message);
|
||||||
|
|
||||||
|
if(rpc->session.terminate) {
|
||||||
|
FURI_LOG_D(RPC_TAG, "Session terminated");
|
||||||
|
osEventFlagsClear(rpc->events, RPC_EVENTS_ALL);
|
||||||
|
rpc_free_session(&rpc->session);
|
||||||
|
rpc->busy = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -456,13 +532,13 @@ void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler) {
|
|||||||
RpcHandlerDict_set_at(rpc->handlers, message_tag, *handler);
|
RpcHandlerDict_set_at(rpc->handlers, message_tag, *handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpc_encode_and_send_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status) {
|
void rpc_send_and_release_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status) {
|
||||||
PB_Main message = {
|
PB_Main message = {
|
||||||
.command_id = command_id,
|
.command_id = command_id,
|
||||||
.command_status = status,
|
.command_status = status,
|
||||||
.has_next = false,
|
.has_next = false,
|
||||||
.which_content = PB_Main_empty_tag,
|
.which_content = PB_Main_empty_tag,
|
||||||
};
|
};
|
||||||
rpc_encode_and_send(rpc, &message);
|
rpc_send_and_release(rpc, &message);
|
||||||
pb_release(&PB_Main_msg, &message);
|
pb_release(&PB_Main_msg, &message);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,79 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include "cmsis_os.h"
|
#include "cmsis_os.h"
|
||||||
|
|
||||||
|
/** Rpc interface. Used for opening session only. */
|
||||||
typedef struct Rpc Rpc;
|
typedef struct Rpc Rpc;
|
||||||
|
/** Rpc session interface */
|
||||||
typedef struct RpcSession RpcSession;
|
typedef struct RpcSession RpcSession;
|
||||||
|
|
||||||
|
/** Callback to send to client any data (e.g. response to command) */
|
||||||
typedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes_len);
|
typedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes_len);
|
||||||
|
/** Callback to notify transport layer that close_session command
|
||||||
|
* is received. Any other actions lays on transport layer.
|
||||||
|
* No destruction or session close preformed. */
|
||||||
|
typedef void (*RpcSessionClosedCallback)(void* context);
|
||||||
|
|
||||||
RpcSession* rpc_open_session(Rpc* rpc);
|
/** Open RPC session
|
||||||
void rpc_close_session(RpcSession* session);
|
*
|
||||||
/* WARN: can't call RPC API within RpcSendBytesCallback */
|
* USAGE:
|
||||||
void rpc_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback, void* context);
|
* 1) rpc_session_open();
|
||||||
size_t
|
* 2) rpc_session_set_context();
|
||||||
rpc_feed_bytes(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout);
|
* 3) rpc_session_set_send_bytes_callback();
|
||||||
|
* 4) rpc_session_set_close_callback();
|
||||||
|
* 5) while(1) {
|
||||||
|
* rpc_session_feed();
|
||||||
|
* }
|
||||||
|
* 6) rpc_session_close();
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param rpc instance
|
||||||
|
* @return pointer to RpcSession descriptor, or
|
||||||
|
* NULL if RPC is busy and can't open session now
|
||||||
|
*/
|
||||||
|
RpcSession* rpc_session_open(Rpc* rpc);
|
||||||
|
|
||||||
|
/** Close RPC session
|
||||||
|
* It is guaranteed that no callbacks will be called
|
||||||
|
* as soon as session is closed. So no need in setting
|
||||||
|
* callbacks to NULL after session close.
|
||||||
|
*
|
||||||
|
* @param session pointer to RpcSession descriptor
|
||||||
|
*/
|
||||||
|
void rpc_session_close(RpcSession* session);
|
||||||
|
|
||||||
|
/** Set session context for callbacks to pass
|
||||||
|
*
|
||||||
|
* @param session pointer to RpcSession descriptor
|
||||||
|
* @param context context to pass to callbacks
|
||||||
|
*/
|
||||||
|
void rpc_session_set_context(RpcSession* session, void* context);
|
||||||
|
|
||||||
|
/** Set callback to send bytes to client
|
||||||
|
* WARN: It's forbidden to call RPC API within RpcSendBytesCallback
|
||||||
|
*
|
||||||
|
* @param session pointer to RpcSession descriptor
|
||||||
|
* @param callback callback to send bytes to client (can be NULL)
|
||||||
|
*/
|
||||||
|
void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback);
|
||||||
|
|
||||||
|
/** Set callback to be called when RPC command to close session is received
|
||||||
|
* WARN: It's forbidden to call RPC API within RpcSessionClosedCallback
|
||||||
|
*
|
||||||
|
* @param session pointer to RpcSession descriptor
|
||||||
|
* @param callback callback to inform about RPC close session command (can be NULL)
|
||||||
|
*/
|
||||||
|
void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback);
|
||||||
|
|
||||||
|
/** Give bytes to RPC service to decode them and perform command
|
||||||
|
*
|
||||||
|
* @param session pointer to RpcSession descriptor
|
||||||
|
* @param buffer buffer to provide to RPC service
|
||||||
|
* @param size size of buffer
|
||||||
|
* @param timeout max timeout to wait till all buffer will be consumed
|
||||||
|
*
|
||||||
|
* @return actually consumed bytes
|
||||||
|
*/
|
||||||
|
size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, TickType_t timeout);
|
||||||
|
|||||||
@ -34,7 +34,7 @@ void rpc_system_app_start_process(const PB_Main* request, void* context) {
|
|||||||
|
|
||||||
furi_record_close("loader");
|
furi_record_close("loader");
|
||||||
|
|
||||||
rpc_encode_and_send_empty(rpc, request->command_id, result);
|
rpc_send_and_release_empty(rpc, request->command_id, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpc_system_app_lock_status_process(const PB_Main* request, void* context) {
|
void rpc_system_app_lock_status_process(const PB_Main* request, void* context) {
|
||||||
@ -56,7 +56,8 @@ void rpc_system_app_lock_status_process(const PB_Main* request, void* context) {
|
|||||||
|
|
||||||
furi_record_close("loader");
|
furi_record_close("loader");
|
||||||
|
|
||||||
rpc_encode_and_send(rpc, &response);
|
rpc_send_and_release(rpc, &response);
|
||||||
|
pb_release(&PB_Main_msg, &response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* rpc_system_app_alloc(Rpc* rpc) {
|
void* rpc_system_app_alloc(Rpc* rpc) {
|
||||||
|
|||||||
59
applications/rpc/rpc_cli.c
Normal file
59
applications/rpc/rpc_cli.c
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#include <cli/cli.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <rpc/rpc.h>
|
||||||
|
#include <furi-hal-vcp.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Cli* cli;
|
||||||
|
bool session_close_request;
|
||||||
|
} CliRpc;
|
||||||
|
|
||||||
|
#define CLI_READ_BUFFER_SIZE 100
|
||||||
|
|
||||||
|
static void rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) {
|
||||||
|
furi_assert(context);
|
||||||
|
furi_assert(bytes);
|
||||||
|
furi_assert(bytes_len);
|
||||||
|
CliRpc* cli_rpc = context;
|
||||||
|
|
||||||
|
cli_write(cli_rpc->cli, bytes, bytes_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_session_close_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
CliRpc* cli_rpc = context;
|
||||||
|
|
||||||
|
cli_rpc->session_close_request = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) {
|
||||||
|
Rpc* rpc = context;
|
||||||
|
|
||||||
|
RpcSession* rpc_session = rpc_session_open(rpc);
|
||||||
|
if(rpc_session == NULL) {
|
||||||
|
printf("Another session is in progress\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CliRpc cli_rpc = {.cli = cli, .session_close_request = false};
|
||||||
|
rpc_session_set_context(rpc_session, &cli_rpc);
|
||||||
|
rpc_session_set_send_bytes_callback(rpc_session, rpc_send_bytes_callback);
|
||||||
|
rpc_session_set_close_callback(rpc_session, rpc_session_close_callback);
|
||||||
|
|
||||||
|
uint8_t* buffer = furi_alloc(CLI_READ_BUFFER_SIZE);
|
||||||
|
size_t size_received = 0;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
size_received = furi_hal_vcp_rx_with_timeout(buffer, CLI_READ_BUFFER_SIZE, 50);
|
||||||
|
if(!furi_hal_vcp_is_connected() || cli_rpc.session_close_request) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(size_received) {
|
||||||
|
rpc_session_feed(rpc_session, buffer, size_received, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc_session_close(rpc_session);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "rpc.h"
|
#include "rpc.h"
|
||||||
#include "pb.h"
|
#include <pb.h>
|
||||||
#include "pb_decode.h"
|
#include <pb_decode.h>
|
||||||
#include "pb_encode.h"
|
#include <pb_encode.h>
|
||||||
#include "flipper.pb.h"
|
#include <flipper.pb.h>
|
||||||
|
#include <cli/cli.h>
|
||||||
|
|
||||||
typedef void* (*RpcSystemAlloc)(Rpc*);
|
typedef void* (*RpcSystemAlloc)(Rpc*);
|
||||||
typedef void (*RpcSystemFree)(void*);
|
typedef void (*RpcSystemFree)(void*);
|
||||||
@ -15,8 +16,8 @@ typedef struct {
|
|||||||
void* context;
|
void* context;
|
||||||
} RpcHandler;
|
} RpcHandler;
|
||||||
|
|
||||||
void rpc_encode_and_send(Rpc* rpc, PB_Main* main_message);
|
void rpc_send_and_release(Rpc* rpc, PB_Main* main_message);
|
||||||
void rpc_encode_and_send_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status);
|
void rpc_send_and_release_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status);
|
||||||
void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler);
|
void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler);
|
||||||
|
|
||||||
void* rpc_system_status_alloc(Rpc* rpc);
|
void* rpc_system_status_alloc(Rpc* rpc);
|
||||||
@ -25,3 +26,4 @@ void rpc_system_storage_free(void* ctx);
|
|||||||
void* rpc_system_app_alloc(Rpc* rpc);
|
void* rpc_system_app_alloc(Rpc* rpc);
|
||||||
|
|
||||||
void rpc_print_message(const PB_Main* message);
|
void rpc_print_message(const PB_Main* message);
|
||||||
|
void rpc_cli_command_start_session(Cli* cli, string_t args, void* context);
|
||||||
|
|||||||
@ -9,7 +9,8 @@ void rpc_system_status_ping_process(const PB_Main* msg_request, void* context) {
|
|||||||
msg_response.command_id = msg_request->command_id;
|
msg_response.command_id = msg_request->command_id;
|
||||||
msg_response.which_content = PB_Main_ping_response_tag;
|
msg_response.which_content = PB_Main_ping_response_tag;
|
||||||
|
|
||||||
rpc_encode_and_send(context, &msg_response);
|
rpc_send_and_release(context, &msg_response);
|
||||||
|
pb_release(&PB_Main_msg, &msg_response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* rpc_system_status_alloc(Rpc* rpc) {
|
void* rpc_system_status_alloc(Rpc* rpc) {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ static void rpc_system_storage_reset_state(RpcStorageSystem* rpc_storage, bool s
|
|||||||
|
|
||||||
if(rpc_storage->state != RpcStorageStateIdle) {
|
if(rpc_storage->state != RpcStorageStateIdle) {
|
||||||
if(send_error) {
|
if(send_error) {
|
||||||
rpc_encode_and_send_empty(
|
rpc_send_and_release_empty(
|
||||||
rpc_storage->rpc,
|
rpc_storage->rpc,
|
||||||
rpc_storage->current_command_id,
|
rpc_storage->current_command_id,
|
||||||
PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED);
|
PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED);
|
||||||
@ -96,6 +96,31 @@ static PB_CommandStatus rpc_system_storage_get_file_error(File* file) {
|
|||||||
return rpc_system_storage_get_error(storage_file_get_error(file));
|
return rpc_system_storage_get_error(storage_file_get_error(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rpc_system_storage_list_root(const PB_Main* request, void* context) {
|
||||||
|
RpcStorageSystem* rpc_storage = context;
|
||||||
|
const char* hard_coded_dirs[] = {"any", "int", "ext"};
|
||||||
|
|
||||||
|
PB_Main response = {
|
||||||
|
.has_next = false,
|
||||||
|
.command_id = request->command_id,
|
||||||
|
.command_status = PB_CommandStatus_OK,
|
||||||
|
.which_content = PB_Main_storage_list_response_tag,
|
||||||
|
};
|
||||||
|
furi_assert(COUNT_OF(hard_coded_dirs) < COUNT_OF(response.content.storage_list_response.file));
|
||||||
|
|
||||||
|
for(int i = 0; i < COUNT_OF(hard_coded_dirs); ++i) {
|
||||||
|
++response.content.storage_list_response.file_count;
|
||||||
|
response.content.storage_list_response.file[i].data = NULL;
|
||||||
|
response.content.storage_list_response.file[i].size = 0;
|
||||||
|
response.content.storage_list_response.file[i].type = PB_Storage_File_FileType_DIR;
|
||||||
|
char* str = furi_alloc(strlen(hard_coded_dirs[i]) + 1);
|
||||||
|
strcpy(str, hard_coded_dirs[i]);
|
||||||
|
response.content.storage_list_response.file[i].name = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc_send_and_release(rpc_storage->rpc, &response);
|
||||||
|
}
|
||||||
|
|
||||||
static void rpc_system_storage_list_process(const PB_Main* request, void* context) {
|
static void rpc_system_storage_list_process(const PB_Main* request, void* context) {
|
||||||
furi_assert(request);
|
furi_assert(request);
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
@ -104,6 +129,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
|
|||||||
RpcStorageSystem* rpc_storage = context;
|
RpcStorageSystem* rpc_storage = context;
|
||||||
rpc_system_storage_reset_state(rpc_storage, true);
|
rpc_system_storage_reset_state(rpc_storage, true);
|
||||||
|
|
||||||
|
if(!strcmp(request->content.storage_list_request.path, "/")) {
|
||||||
|
rpc_system_storage_list_root(request, context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Storage* fs_api = furi_record_open("storage");
|
Storage* fs_api = furi_record_open("storage");
|
||||||
File* dir = storage_file_alloc(fs_api);
|
File* dir = storage_file_alloc(fs_api);
|
||||||
|
|
||||||
@ -132,8 +162,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
|
|||||||
if(i == COUNT_OF(list->file)) {
|
if(i == COUNT_OF(list->file)) {
|
||||||
list->file_count = i;
|
list->file_count = i;
|
||||||
response.has_next = true;
|
response.has_next = true;
|
||||||
rpc_encode_and_send(rpc_storage->rpc, &response);
|
rpc_send_and_release(rpc_storage->rpc, &response);
|
||||||
pb_release(&PB_Main_msg, &response);
|
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? PB_Storage_File_FileType_DIR :
|
list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? PB_Storage_File_FileType_DIR :
|
||||||
@ -150,8 +179,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
response.has_next = false;
|
response.has_next = false;
|
||||||
rpc_encode_and_send(rpc_storage->rpc, &response);
|
rpc_send_and_release(rpc_storage->rpc, &response);
|
||||||
pb_release(&PB_Main_msg, &response);
|
|
||||||
|
|
||||||
storage_dir_close(dir);
|
storage_dir_close(dir);
|
||||||
storage_file_free(dir);
|
storage_file_free(dir);
|
||||||
@ -168,9 +196,6 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex
|
|||||||
|
|
||||||
/* use same message memory to send reponse */
|
/* use same message memory to send reponse */
|
||||||
PB_Main* response = furi_alloc(sizeof(PB_Main));
|
PB_Main* response = furi_alloc(sizeof(PB_Main));
|
||||||
response->command_id = request->command_id;
|
|
||||||
response->which_content = PB_Main_storage_read_response_tag;
|
|
||||||
response->command_status = PB_CommandStatus_OK;
|
|
||||||
const char* path = request->content.storage_read_request.path;
|
const char* path = request->content.storage_read_request.path;
|
||||||
Storage* fs_api = furi_record_open("storage");
|
Storage* fs_api = furi_record_open("storage");
|
||||||
File* file = storage_file_alloc(fs_api);
|
File* file = storage_file_alloc(fs_api);
|
||||||
@ -178,10 +203,13 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex
|
|||||||
|
|
||||||
if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
size_t size_left = storage_file_size(file);
|
size_t size_left = storage_file_size(file);
|
||||||
response->content.storage_read_response.has_file = true;
|
|
||||||
response->content.storage_read_response.file.data =
|
|
||||||
furi_alloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE)));
|
|
||||||
do {
|
do {
|
||||||
|
response->command_id = request->command_id;
|
||||||
|
response->which_content = PB_Main_storage_read_response_tag;
|
||||||
|
response->command_status = PB_CommandStatus_OK;
|
||||||
|
response->content.storage_read_response.has_file = true;
|
||||||
|
response->content.storage_read_response.file.data =
|
||||||
|
furi_alloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE)));
|
||||||
uint8_t* buffer = response->content.storage_read_response.file.data->bytes;
|
uint8_t* buffer = response->content.storage_read_response.file.data->bytes;
|
||||||
uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size;
|
uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size;
|
||||||
|
|
||||||
@ -192,21 +220,19 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex
|
|||||||
|
|
||||||
if(result) {
|
if(result) {
|
||||||
response->has_next = (size_left > 0);
|
response->has_next = (size_left > 0);
|
||||||
rpc_encode_and_send(rpc_storage->rpc, response);
|
rpc_send_and_release(rpc_storage->rpc, response);
|
||||||
// no pb_release(...);
|
|
||||||
}
|
}
|
||||||
} while((size_left != 0) && result);
|
} while((size_left != 0) && result);
|
||||||
|
|
||||||
if(!result) {
|
if(!result) {
|
||||||
rpc_encode_and_send_empty(
|
rpc_send_and_release_empty(
|
||||||
rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file));
|
rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rpc_encode_and_send_empty(
|
rpc_send_and_release_empty(
|
||||||
rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file));
|
rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
pb_release(&PB_Main_msg, response);
|
|
||||||
free(response);
|
free(response);
|
||||||
storage_file_close(file);
|
storage_file_close(file);
|
||||||
storage_file_free(file);
|
storage_file_free(file);
|
||||||
@ -245,14 +271,14 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte
|
|||||||
result = (written_size == buffer_size);
|
result = (written_size == buffer_size);
|
||||||
|
|
||||||
if(result && !request->has_next) {
|
if(result && !request->has_next) {
|
||||||
rpc_encode_and_send_empty(
|
rpc_send_and_release_empty(
|
||||||
rpc_storage->rpc, rpc_storage->current_command_id, PB_CommandStatus_OK);
|
rpc_storage->rpc, rpc_storage->current_command_id, PB_CommandStatus_OK);
|
||||||
rpc_system_storage_reset_state(rpc_storage, false);
|
rpc_system_storage_reset_state(rpc_storage, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!result) {
|
if(!result) {
|
||||||
rpc_encode_and_send_empty(
|
rpc_send_and_release_empty(
|
||||||
rpc_storage->rpc,
|
rpc_storage->rpc,
|
||||||
rpc_storage->current_command_id,
|
rpc_storage->current_command_id,
|
||||||
rpc_system_storage_get_file_error(file));
|
rpc_system_storage_get_file_error(file));
|
||||||
@ -260,23 +286,57 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool rpc_system_storage_is_dir_is_empty(Storage* fs_api, const char* path) {
|
||||||
|
FileInfo fileinfo;
|
||||||
|
bool is_dir_is_empty = false;
|
||||||
|
FS_Error error = storage_common_stat(fs_api, path, &fileinfo);
|
||||||
|
if((error == FSE_OK) && (fileinfo.flags & FSF_DIRECTORY)) {
|
||||||
|
File* dir = storage_file_alloc(fs_api);
|
||||||
|
if(storage_dir_open(dir, path)) {
|
||||||
|
char* name = furi_alloc(MAX_NAME_LENGTH);
|
||||||
|
is_dir_is_empty = !storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH);
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
storage_dir_close(dir);
|
||||||
|
storage_file_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_dir_is_empty;
|
||||||
|
}
|
||||||
|
|
||||||
static void rpc_system_storage_delete_process(const PB_Main* request, void* context) {
|
static void rpc_system_storage_delete_process(const PB_Main* request, void* context) {
|
||||||
furi_assert(request);
|
furi_assert(request);
|
||||||
furi_assert(request->which_content == PB_Main_storage_delete_request_tag);
|
furi_assert(request->which_content == PB_Main_storage_delete_request_tag);
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
RpcStorageSystem* rpc_storage = context;
|
RpcStorageSystem* rpc_storage = context;
|
||||||
PB_CommandStatus status;
|
PB_CommandStatus status = PB_CommandStatus_ERROR;
|
||||||
rpc_system_storage_reset_state(rpc_storage, true);
|
rpc_system_storage_reset_state(rpc_storage, true);
|
||||||
|
|
||||||
Storage* fs_api = furi_record_open("storage");
|
Storage* fs_api = furi_record_open("storage");
|
||||||
char* path = request->content.storage_mkdir_request.path;
|
|
||||||
if(path) {
|
char* path = request->content.storage_delete_request.path;
|
||||||
FS_Error error = storage_common_remove(fs_api, path);
|
if(!path) {
|
||||||
status = rpc_system_storage_get_error(error);
|
|
||||||
} else {
|
|
||||||
status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
|
status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
|
||||||
|
} else {
|
||||||
|
FS_Error error_remove = storage_common_remove(fs_api, path);
|
||||||
|
// FSE_DENIED is for empty directory, but not only for this
|
||||||
|
// that's why we have to check it
|
||||||
|
if((error_remove == FSE_DENIED) && !rpc_system_storage_is_dir_is_empty(fs_api, path)) {
|
||||||
|
if(request->content.storage_delete_request.recursive) {
|
||||||
|
bool deleted = storage_simply_remove_recursive(fs_api, path);
|
||||||
|
status = deleted ? PB_CommandStatus_OK : PB_CommandStatus_ERROR;
|
||||||
|
} else {
|
||||||
|
status = PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY;
|
||||||
|
}
|
||||||
|
} else if(error_remove == FSE_NOT_EXIST) {
|
||||||
|
status = PB_CommandStatus_OK;
|
||||||
|
} else {
|
||||||
|
status = rpc_system_storage_get_error(error_remove);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rpc_encode_and_send_empty(rpc_storage->rpc, request->command_id, status);
|
|
||||||
|
furi_record_close("storage");
|
||||||
|
rpc_send_and_release_empty(rpc_storage->rpc, request->command_id, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rpc_system_storage_mkdir_process(const PB_Main* request, void* context) {
|
static void rpc_system_storage_mkdir_process(const PB_Main* request, void* context) {
|
||||||
@ -295,7 +355,7 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte
|
|||||||
} else {
|
} else {
|
||||||
status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
|
status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
|
||||||
}
|
}
|
||||||
rpc_encode_and_send_empty(rpc_storage->rpc, request->command_id, status);
|
rpc_send_and_release_empty(rpc_storage->rpc, request->command_id, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rpc_system_storage_md5sum_process(const PB_Main* request, void* context) {
|
static void rpc_system_storage_md5sum_process(const PB_Main* request, void* context) {
|
||||||
@ -307,7 +367,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont
|
|||||||
|
|
||||||
const char* filename = request->content.storage_md5sum_request.path;
|
const char* filename = request->content.storage_md5sum_request.path;
|
||||||
if(!filename) {
|
if(!filename) {
|
||||||
rpc_encode_and_send_empty(
|
rpc_send_and_release_empty(
|
||||||
rpc_storage->rpc, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);
|
rpc_storage->rpc, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -349,9 +409,9 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont
|
|||||||
free(hash);
|
free(hash);
|
||||||
free(data);
|
free(data);
|
||||||
storage_file_close(file);
|
storage_file_close(file);
|
||||||
rpc_encode_and_send(rpc_storage->rpc, &response);
|
rpc_send_and_release(rpc_storage->rpc, &response);
|
||||||
} else {
|
} else {
|
||||||
rpc_encode_and_send_empty(
|
rpc_send_and_release_empty(
|
||||||
rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file));
|
rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
|
#include <furi/record.h>
|
||||||
|
#include <m-string.h>
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "storage-i.h"
|
#include "storage-i.h"
|
||||||
#include "storage-message.h"
|
#include "storage-message.h"
|
||||||
|
|
||||||
|
#define MAX_NAME_LENGTH 256
|
||||||
|
|
||||||
#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);
|
||||||
@ -382,6 +386,67 @@ void storage_file_free(File* file) {
|
|||||||
free(file);
|
free(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool storage_simply_remove_recursive(Storage* storage, const char* path) {
|
||||||
|
furi_assert(storage);
|
||||||
|
furi_assert(path);
|
||||||
|
FileInfo fileinfo;
|
||||||
|
bool result = false;
|
||||||
|
string_t fullname;
|
||||||
|
string_t cur_dir;
|
||||||
|
|
||||||
|
if(storage_simply_remove(storage, path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* name = furi_alloc(MAX_NAME_LENGTH + 1);
|
||||||
|
File* dir = storage_file_alloc(storage);
|
||||||
|
string_init_set_str(cur_dir, path);
|
||||||
|
bool go_deeper = false;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
if(!storage_dir_open(dir, string_get_cstr(cur_dir))) {
|
||||||
|
storage_dir_close(dir);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
|
||||||
|
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||||
|
string_cat_printf(cur_dir, "/%s", name);
|
||||||
|
go_deeper = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_init_printf(fullname, "%s/%s", string_get_cstr(cur_dir), name);
|
||||||
|
FS_Error error = storage_common_remove(storage, string_get_cstr(fullname));
|
||||||
|
furi_assert(error == FSE_OK);
|
||||||
|
string_clear(fullname);
|
||||||
|
}
|
||||||
|
storage_dir_close(dir);
|
||||||
|
|
||||||
|
if(go_deeper) {
|
||||||
|
go_deeper = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FS_Error error = storage_common_remove(storage, string_get_cstr(cur_dir));
|
||||||
|
furi_assert(error == FSE_OK);
|
||||||
|
|
||||||
|
if(string_cmp(cur_dir, path)) {
|
||||||
|
size_t last_char = string_search_rchar(cur_dir, '/');
|
||||||
|
furi_assert(last_char != STRING_FAILURE);
|
||||||
|
string_left(cur_dir, last_char);
|
||||||
|
} else {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storage_file_free(dir);
|
||||||
|
string_clear(cur_dir);
|
||||||
|
free(name);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool storage_simply_remove(Storage* storage, const char* path) {
|
bool storage_simply_remove(Storage* storage, const char* path) {
|
||||||
FS_Error result;
|
FS_Error result;
|
||||||
result = storage_common_remove(storage, path);
|
result = storage_common_remove(storage, path);
|
||||||
|
|||||||
@ -240,6 +240,14 @@ FS_Error storage_sd_status(Storage* api);
|
|||||||
*/
|
*/
|
||||||
bool storage_simply_remove(Storage* storage, const char* path);
|
bool storage_simply_remove(Storage* storage, const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a file/directory from the repository, the directory can be not empty
|
||||||
|
* @param storage pointer to the api
|
||||||
|
* @param path
|
||||||
|
* @return true on success or if file/dir is not exist
|
||||||
|
*/
|
||||||
|
bool storage_simply_remove_recursive(Storage* storage, const char* path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a directory
|
* Creates a directory
|
||||||
* @param storage
|
* @param storage
|
||||||
|
|||||||
@ -163,15 +163,15 @@ static LFSData* storage_int_lfs_data_alloc() {
|
|||||||
|
|
||||||
static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) {
|
static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) {
|
||||||
int err;
|
int err;
|
||||||
FuriHalBootFlag boot_flags = furi_hal_boot_get_flags();
|
FuriHalBootloaderFlag bootloader_flags = furi_hal_bootloader_get_flags();
|
||||||
lfs_t* lfs = &lfs_data->lfs;
|
lfs_t* lfs = &lfs_data->lfs;
|
||||||
|
|
||||||
if(boot_flags & FuriHalBootFlagFactoryReset) {
|
if(bootloader_flags & FuriHalBootloaderFlagFactoryReset) {
|
||||||
// Factory reset
|
// Factory reset
|
||||||
err = lfs_format(lfs, &lfs_data->config);
|
err = lfs_format(lfs, &lfs_data->config);
|
||||||
if(err == 0) {
|
if(err == 0) {
|
||||||
FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount");
|
FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount");
|
||||||
furi_hal_boot_set_flags(boot_flags & ~FuriHalBootFlagFactoryReset);
|
furi_hal_bootloader_set_flags(bootloader_flags & ~FuriHalBootloaderFlagFactoryReset);
|
||||||
err = lfs_mount(lfs, &lfs_data->config);
|
err = lfs_mount(lfs, &lfs_data->config);
|
||||||
if(err == 0) {
|
if(err == 0) {
|
||||||
FURI_LOG_I(TAG, "Factory reset: Mounted");
|
FURI_LOG_I(TAG, "Factory reset: Mounted");
|
||||||
|
|||||||
34
applications/subghz/helpers/subghz_custom_event.h
Normal file
34
applications/subghz/helpers/subghz_custom_event.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubghzCustomEventManagerNoSet = 0,
|
||||||
|
SubghzCustomEventManagerSet,
|
||||||
|
|
||||||
|
SubghzCustomEventSceneDeleteSuccess = 100,
|
||||||
|
SubghzCustomEventSceneDelete,
|
||||||
|
SubghzCustomEventSceneReceiverInfoTxStart,
|
||||||
|
SubghzCustomEventSceneReceiverInfoTxStop,
|
||||||
|
SubghzCustomEventSceneReceiverInfoSave,
|
||||||
|
SubghzCustomEventSceneSaveName,
|
||||||
|
SubghzCustomEventSceneSaveSuccess,
|
||||||
|
SubghzCustomEventSceneShowError,
|
||||||
|
SubghzCustomEventSceneShowOnlyRX,
|
||||||
|
|
||||||
|
SubghzCustomEventSceneNeedSavingNo,
|
||||||
|
SubghzCustomEventSceneNeedSavingYes,
|
||||||
|
|
||||||
|
SubghzCustomEventViewReceverOK,
|
||||||
|
SubghzCustomEventViewReceverConfig,
|
||||||
|
SubghzCustomEventViewReceverBack,
|
||||||
|
|
||||||
|
SubghzCustomEventViewReadRAWBack,
|
||||||
|
SubghzCustomEventViewReadRAWIDLE,
|
||||||
|
SubghzCustomEventViewReadRAWREC,
|
||||||
|
SubghzCustomEventViewReadRAWConfig,
|
||||||
|
SubghzCustomEventViewReadRAWMore,
|
||||||
|
|
||||||
|
SubghzCustomEventViewTransmitterBack,
|
||||||
|
SubghzCustomEventViewTransmitterSendStart,
|
||||||
|
SubghzCustomEventViewTransmitterSendStop,
|
||||||
|
SubghzCustomEventViewTransmitterError,
|
||||||
|
} SubghzCustomEvent;
|
||||||
@ -17,3 +17,6 @@ ADD_SCENE(subghz, test_carrier, TestCarrier)
|
|||||||
ADD_SCENE(subghz, test_packet, TestPacket)
|
ADD_SCENE(subghz, test_packet, TestPacket)
|
||||||
ADD_SCENE(subghz, set_type, SetType)
|
ADD_SCENE(subghz, set_type, SetType)
|
||||||
ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
|
ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
|
||||||
|
ADD_SCENE(subghz, read_raw, ReadRAW)
|
||||||
|
ADD_SCENE(subghz, read_raw_menu, ReadRAWMenu)
|
||||||
|
ADD_SCENE(subghz, need_saving, NeedSaving)
|
||||||
@ -1,15 +1,11 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
typedef enum {
|
|
||||||
SubGhzSceneDeleteInfoCustomEventDelete,
|
|
||||||
} SubGhzSceneDeleteInfoCustomEvent;
|
|
||||||
|
|
||||||
void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) {
|
void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
|
if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
|
||||||
view_dispatcher_send_custom_event(
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneDelete);
|
||||||
subghz->view_dispatcher, SubGhzSceneDeleteInfoCustomEventDelete);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +49,7 @@ void subghz_scene_delete_on_enter(void* context) {
|
|||||||
bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SubGhzSceneDeleteInfoCustomEventDelete) {
|
if(event.event == SubghzCustomEventSceneDelete) {
|
||||||
memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name));
|
memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name));
|
||||||
if(subghz_delete_file(subghz)) {
|
if(subghz_delete_file(subghz)) {
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
#define SCENE_DELETE_SUCCESS_CUSTOM_EVENT (0UL)
|
|
||||||
|
|
||||||
void subghz_scene_delete_success_popup_callback(void* context) {
|
void subghz_scene_delete_success_popup_callback(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_DELETE_SUCCESS_CUSTOM_EVENT);
|
view_dispatcher_send_custom_event(
|
||||||
|
subghz->view_dispatcher, SubghzCustomEventSceneDeleteSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_delete_success_on_enter(void* context) {
|
void subghz_scene_delete_success_on_enter(void* context) {
|
||||||
@ -25,7 +25,7 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event
|
|||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SCENE_DELETE_SUCCESS_CUSTOM_EVENT) {
|
if(event.event == SubghzCustomEventSceneDeleteSuccess) {
|
||||||
return scene_manager_search_and_switch_to_previous_scene(
|
return scene_manager_search_and_switch_to_previous_scene(
|
||||||
subghz->scene_manager, SubGhzSceneStart);
|
subghz->scene_manager, SubGhzSceneStart);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
#include "../views/subghz_frequency_analyzer.h"
|
#include "../views/subghz_frequency_analyzer.h"
|
||||||
|
|
||||||
void subghz_scene_frequency_analyzer_callback(SubghzFrequencyAnalyzerEvent event, void* context) {
|
void subghz_scene_frequency_analyzer_callback(SubghzCustomEvent event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
||||||
@ -15,13 +15,7 @@ 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 == SubghzFrequencyAnalyzerEventOnlyRx) {
|
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
63
applications/subghz/scenes/subghz_scene_need_saving.c
Normal file
63
applications/subghz/scenes/subghz_scene_need_saving.c
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "../subghz_i.h"
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
|
|
||||||
|
void subghz_scene_need_saving_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
|
if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
subghz->view_dispatcher, SubghzCustomEventSceneNeedSavingYes);
|
||||||
|
} else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
subghz->view_dispatcher, SubghzCustomEventSceneNeedSavingNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_scene_need_saving_on_enter(void* context) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
|
widget_add_string_multiline_element(
|
||||||
|
subghz->widget,
|
||||||
|
64,
|
||||||
|
25,
|
||||||
|
AlignCenter,
|
||||||
|
AlignCenter,
|
||||||
|
FontSecondary,
|
||||||
|
"There is an unsaved data.\nDo you want to save it?");
|
||||||
|
|
||||||
|
widget_add_button_element(
|
||||||
|
subghz->widget, GuiButtonTypeRight, "Save", subghz_scene_need_saving_callback, subghz);
|
||||||
|
widget_add_button_element(
|
||||||
|
subghz->widget, GuiButtonTypeLeft, "Delete", subghz_scene_need_saving_callback, subghz);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubghzCustomEventSceneNeedSavingYes) {
|
||||||
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateNeedSave;
|
||||||
|
scene_manager_previous_scene(subghz->scene_manager);
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubghzCustomEventSceneNeedSavingNo) {
|
||||||
|
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) {
|
||||||
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
subghz->scene_manager, SubGhzSceneStart);
|
||||||
|
} else {
|
||||||
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||||
|
scene_manager_previous_scene(subghz->scene_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_scene_need_saving_on_exit(void* context) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
widget_clear(subghz->widget);
|
||||||
|
}
|
||||||
190
applications/subghz/scenes/subghz_scene_read_raw.c
Normal file
190
applications/subghz/scenes/subghz_scene_read_raw.c
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#include "../subghz_i.h"
|
||||||
|
#include "../views/subghz_read_raw.h"
|
||||||
|
#include <lib/subghz/protocols/subghz_protocol_raw.h>
|
||||||
|
#include <lib/subghz/subghz_parser.h>
|
||||||
|
|
||||||
|
static void subghz_scene_read_raw_update_statusbar(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
char frequency_str[20];
|
||||||
|
char preset_str[10];
|
||||||
|
|
||||||
|
snprintf(
|
||||||
|
frequency_str,
|
||||||
|
sizeof(frequency_str),
|
||||||
|
"%03ld.%02ld",
|
||||||
|
subghz->txrx->frequency / 1000000 % 1000,
|
||||||
|
subghz->txrx->frequency / 10000 % 100);
|
||||||
|
if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async ||
|
||||||
|
subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) {
|
||||||
|
snprintf(preset_str, sizeof(preset_str), "AM");
|
||||||
|
} else if(
|
||||||
|
subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev238Async ||
|
||||||
|
subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev476Async) {
|
||||||
|
snprintf(preset_str, sizeof(preset_str), "FM");
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
subghz_read_raw_add_data_statusbar(subghz->subghz_read_raw, frequency_str, preset_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_scene_read_raw_callback(SubghzCustomEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_scene_read_raw_on_enter(void* context) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
|
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateNeedSave) {
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
subghz->view_dispatcher, SubghzCustomEventViewReadRAWMore);
|
||||||
|
} else {
|
||||||
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
subghz_scene_read_raw_update_statusbar(subghz);
|
||||||
|
subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz);
|
||||||
|
|
||||||
|
subghz->txrx->protocol_result = subghz_parser_get_by_name(subghz->txrx->parser, "RAW");
|
||||||
|
furi_assert(subghz->txrx->protocol_result);
|
||||||
|
|
||||||
|
subghz_worker_set_pair_callback(
|
||||||
|
subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_raw_parse);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReadRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
switch(event.event) {
|
||||||
|
case SubghzCustomEventViewReadRAWBack:
|
||||||
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
|
subghz_rx_end(subghz);
|
||||||
|
subghz_sleep(subghz);
|
||||||
|
};
|
||||||
|
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
|
||||||
|
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
|
||||||
|
subghz_protocol_raw_save_to_file_stop(
|
||||||
|
(SubGhzProtocolRAW*)subghz->txrx->protocol_result);
|
||||||
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
|
|
||||||
|
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) {
|
||||||
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateExit;
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
|
||||||
|
} else {
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
subghz->scene_manager, SubGhzSceneStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case SubghzCustomEventViewReadRAWConfig:
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet);
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case SubghzCustomEventViewReadRAWIDLE:
|
||||||
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
|
subghz_rx_end(subghz);
|
||||||
|
subghz_sleep(subghz);
|
||||||
|
};
|
||||||
|
subghz_protocol_raw_save_to_file_stop(
|
||||||
|
(SubGhzProtocolRAW*)subghz->txrx->protocol_result);
|
||||||
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
|
|
||||||
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case SubghzCustomEventViewReadRAWREC:
|
||||||
|
|
||||||
|
if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) {
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
|
||||||
|
} else {
|
||||||
|
if(subghz_protocol_raw_save_to_file_init(
|
||||||
|
(SubGhzProtocolRAW*)subghz->txrx->protocol_result,
|
||||||
|
"Raw_temp",
|
||||||
|
subghz->txrx->frequency,
|
||||||
|
subghz->txrx->preset)) {
|
||||||
|
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
|
||||||
|
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
|
||||||
|
subghz_begin(subghz, subghz->txrx->preset);
|
||||||
|
subghz_rx(subghz, subghz->txrx->frequency);
|
||||||
|
}
|
||||||
|
subghz->state_notifications = NOTIFICATION_RX_STATE;
|
||||||
|
} else {
|
||||||
|
string_set(subghz->error_str, "No SD card");
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case SubghzCustomEventViewReadRAWMore:
|
||||||
|
if(strcmp(
|
||||||
|
subghz_protocol_get_last_file_name(
|
||||||
|
(SubGhzProtocolRAW*)subghz->txrx->protocol_result),
|
||||||
|
"")) {
|
||||||
|
strlcpy(
|
||||||
|
subghz->file_name,
|
||||||
|
subghz_protocol_get_last_file_name(
|
||||||
|
(SubGhzProtocolRAW*)subghz->txrx->protocol_result),
|
||||||
|
strlen(subghz_protocol_get_last_file_name(
|
||||||
|
(SubGhzProtocolRAW*)subghz->txrx->protocol_result)) +
|
||||||
|
1);
|
||||||
|
//set the path to read the file
|
||||||
|
string_t temp_str;
|
||||||
|
string_init_printf(
|
||||||
|
temp_str,
|
||||||
|
"%s/%s%s",
|
||||||
|
SUBGHZ_APP_PATH_FOLDER,
|
||||||
|
subghz->file_name,
|
||||||
|
SUBGHZ_APP_EXTENSION);
|
||||||
|
subghz_protocol_set_last_file_name(
|
||||||
|
(SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str));
|
||||||
|
string_clear(temp_str);
|
||||||
|
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAWMenu);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeTick) {
|
||||||
|
switch(subghz->state_notifications) {
|
||||||
|
case NOTIFICATION_RX_STATE:
|
||||||
|
notification_message(subghz->notifications, &sequence_blink_blue_10);
|
||||||
|
subghz_read_raw_update_sample_write(
|
||||||
|
subghz->subghz_read_raw,
|
||||||
|
subghz_protocol_raw_get_sample_write(
|
||||||
|
(SubGhzProtocolRAW*)subghz->txrx->protocol_result));
|
||||||
|
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_scene_read_raw_on_exit(void* context) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
|
//Stop CC1101
|
||||||
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
|
subghz_rx_end(subghz);
|
||||||
|
subghz_sleep(subghz);
|
||||||
|
};
|
||||||
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
|
|
||||||
|
//Сallback restoration
|
||||||
|
subghz_worker_set_pair_callback(
|
||||||
|
subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_parse);
|
||||||
|
}
|
||||||
72
applications/subghz/scenes/subghz_scene_read_raw_menu.c
Normal file
72
applications/subghz/scenes/subghz_scene_read_raw_menu.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#include "../subghz_i.h"
|
||||||
|
|
||||||
|
enum SubmenuIndex {
|
||||||
|
SubmenuIndexEmulate,
|
||||||
|
SubmenuIndexEdit,
|
||||||
|
SubmenuIndexDelete,
|
||||||
|
};
|
||||||
|
|
||||||
|
void subghz_scene_read_raw_menu_submenu_callback(void* context, uint32_t index) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_scene_read_raw_menu_on_enter(void* context) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
submenu_add_item(
|
||||||
|
subghz->submenu,
|
||||||
|
"Emulate",
|
||||||
|
SubmenuIndexEmulate,
|
||||||
|
subghz_scene_read_raw_menu_submenu_callback,
|
||||||
|
subghz);
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
subghz->submenu,
|
||||||
|
"Save",
|
||||||
|
SubmenuIndexEdit,
|
||||||
|
subghz_scene_read_raw_menu_submenu_callback,
|
||||||
|
subghz);
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
subghz->submenu,
|
||||||
|
"Delete",
|
||||||
|
SubmenuIndexDelete,
|
||||||
|
subghz_scene_read_raw_menu_submenu_callback,
|
||||||
|
subghz);
|
||||||
|
|
||||||
|
submenu_set_selected_item(
|
||||||
|
subghz->submenu,
|
||||||
|
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu));
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_scene_read_raw_menu_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubmenuIndexEmulate) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneReadRAWMenu, SubmenuIndexEmulate);
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubmenuIndexDelete) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneReadRAWMenu, SubmenuIndexDelete);
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDelete);
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubmenuIndexEdit) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneReadRAWMenu, SubghzCustomEventManagerSet);
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_scene_read_raw_menu_on_exit(void* context) {
|
||||||
|
SubGhz* subghz = context;
|
||||||
|
submenu_clean(subghz->submenu);
|
||||||
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||||
|
}
|
||||||
@ -34,7 +34,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
|
|||||||
string_clear(history_stat_str);
|
string_clear(history_stat_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_receiver_callback(SubghzReceverEvent event, void* context) {
|
void subghz_scene_receiver_callback(SubghzCustomEvent event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
||||||
@ -87,7 +87,7 @@ void subghz_scene_receiver_on_enter(void* context) {
|
|||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
subghz_rx_end(subghz);
|
subghz_rx_end(subghz);
|
||||||
};
|
};
|
||||||
if((subghz->txrx->txrx_state == SubGhzTxRxStateIdle) ||
|
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
|
||||||
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
|
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
|
||||||
subghz_begin(subghz, subghz->txrx->preset);
|
subghz_begin(subghz, subghz->txrx->preset);
|
||||||
subghz_rx(subghz, subghz->txrx->frequency);
|
subghz_rx(subghz, subghz->txrx->frequency);
|
||||||
@ -102,8 +102,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
|||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case SubghzReceverEventBack:
|
case SubghzCustomEventViewReceverBack:
|
||||||
// Stop CC1101 Rx
|
// Stop CC1101 Rx
|
||||||
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
subghz_rx_end(subghz);
|
subghz_rx_end(subghz);
|
||||||
subghz_sleep(subghz);
|
subghz_sleep(subghz);
|
||||||
@ -118,12 +119,12 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
|||||||
subghz->scene_manager, SubGhzSceneStart);
|
subghz->scene_manager, SubGhzSceneStart);
|
||||||
return true;
|
return true;
|
||||||
break;
|
break;
|
||||||
case SubghzReceverEventOK:
|
case SubghzCustomEventViewReceverOK:
|
||||||
subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver);
|
subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver);
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
|
||||||
return true;
|
return true;
|
||||||
break;
|
break;
|
||||||
case SubghzReceverEventConfig:
|
case SubghzCustomEventViewReceverConfig:
|
||||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver);
|
subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver);
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
|
||||||
|
|||||||
@ -66,6 +66,8 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) {
|
|||||||
if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) {
|
if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) {
|
||||||
variable_item_set_current_value_text(item, subghz_frequencies_text[index]);
|
variable_item_set_current_value_text(item, subghz_frequencies_text[index]);
|
||||||
subghz->txrx->frequency = subghz_frequencies[index];
|
subghz->txrx->frequency = subghz_frequencies[index];
|
||||||
|
} else {
|
||||||
|
variable_item_set_current_value_index(item, subghz_frequencies_433_92);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,22 +90,24 @@ static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item)
|
|||||||
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
||||||
subghz_frequencies_text[subghz_frequencies_433_92]);
|
subghz_frequencies_text[subghz_frequencies_433_92]);
|
||||||
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
|
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
|
||||||
|
variable_item_set_current_value_index(
|
||||||
|
(VariableItem*)scene_manager_get_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
||||||
|
subghz_frequencies_433_92);
|
||||||
} else {
|
} else {
|
||||||
variable_item_set_current_value_text(
|
variable_item_set_current_value_text(
|
||||||
(VariableItem*)scene_manager_get_scene_state(
|
(VariableItem*)scene_manager_get_scene_state(
|
||||||
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
||||||
" -----");
|
" -----");
|
||||||
|
variable_item_set_current_value_index(
|
||||||
|
(VariableItem*)scene_manager_get_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
||||||
|
subghz_frequencies_433_92);
|
||||||
}
|
}
|
||||||
|
|
||||||
subghz->txrx->hopper_state = hopping_value[index];
|
subghz->txrx->hopper_state = hopping_value[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_receiver_config_callback(SubghzReceverEvent event, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
SubGhz* subghz = context;
|
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void subghz_scene_receiver_config_on_enter(void* context) {
|
void subghz_scene_receiver_config_on_enter(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
VariableItem* item;
|
VariableItem* item;
|
||||||
@ -122,16 +126,19 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
|||||||
variable_item_set_current_value_index(item, value_index);
|
variable_item_set_current_value_index(item, value_index);
|
||||||
variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]);
|
variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]);
|
||||||
|
|
||||||
item = variable_item_list_add(
|
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||||
subghz->variable_item_list,
|
SubghzCustomEventManagerSet) {
|
||||||
"Hopping:",
|
item = variable_item_list_add(
|
||||||
HOPPING_COUNT,
|
subghz->variable_item_list,
|
||||||
subghz_scene_receiver_config_set_hopping_runing,
|
"Hopping:",
|
||||||
subghz);
|
HOPPING_COUNT,
|
||||||
value_index = subghz_scene_receiver_config_hopper_value_index(
|
subghz_scene_receiver_config_set_hopping_runing,
|
||||||
subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz);
|
subghz);
|
||||||
variable_item_set_current_value_index(item, value_index);
|
value_index = subghz_scene_receiver_config_hopper_value_index(
|
||||||
variable_item_set_current_value_text(item, hopping_text[value_index]);
|
subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz);
|
||||||
|
variable_item_set_current_value_index(item, value_index);
|
||||||
|
variable_item_set_current_value_text(item, hopping_text[value_index]);
|
||||||
|
}
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
subghz->variable_item_list,
|
subghz->variable_item_list,
|
||||||
@ -155,4 +162,6 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even
|
|||||||
void subghz_scene_receiver_config_on_exit(void* context) {
|
void subghz_scene_receiver_config_on_exit(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
variable_item_list_clean(subghz->variable_item_list);
|
variable_item_list_clean(subghz->variable_item_list);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
typedef enum {
|
|
||||||
SubGhzSceneReceiverInfoCustomEventTxStart,
|
|
||||||
SubGhzSceneReceiverInfoCustomEventTxStop,
|
|
||||||
SubGhzSceneReceiverInfoCustomEventSave,
|
|
||||||
} SubGhzSceneReceiverInfoCustomEvent;
|
|
||||||
|
|
||||||
void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
|
void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
@ -12,13 +7,13 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v
|
|||||||
|
|
||||||
if((result == GuiButtonTypeCenter) && (type == InputTypePress)) {
|
if((result == GuiButtonTypeCenter) && (type == InputTypePress)) {
|
||||||
view_dispatcher_send_custom_event(
|
view_dispatcher_send_custom_event(
|
||||||
subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventTxStart);
|
subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoTxStart);
|
||||||
} else if((result == GuiButtonTypeCenter) && (type == InputTypeRelease)) {
|
} else if((result == GuiButtonTypeCenter) && (type == InputTypeRelease)) {
|
||||||
view_dispatcher_send_custom_event(
|
view_dispatcher_send_custom_event(
|
||||||
subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventTxStop);
|
subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoTxStop);
|
||||||
} else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
|
} else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
|
||||||
view_dispatcher_send_custom_event(
|
view_dispatcher_send_custom_event(
|
||||||
subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventSave);
|
subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoSave);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +96,7 @@ void subghz_scene_receiver_info_on_enter(void* context) {
|
|||||||
bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {
|
bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SubGhzSceneReceiverInfoCustomEventTxStart) {
|
if(event.event == SubghzCustomEventSceneReceiverInfoTxStart) {
|
||||||
//CC1101 Stop RX -> Start TX
|
//CC1101 Stop RX -> Start TX
|
||||||
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
|
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
|
||||||
subghz->txrx->hopper_state = SubGhzHopperStatePause;
|
subghz->txrx->hopper_state = SubGhzHopperStatePause;
|
||||||
@ -112,7 +107,8 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
|||||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE ||
|
||||||
|
subghz->txrx->txrx_state == SubGhzTxRxStateSleep) {
|
||||||
if(!subghz_tx_start(subghz)) {
|
if(!subghz_tx_start(subghz)) {
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
||||||
} else {
|
} else {
|
||||||
@ -120,13 +116,13 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if(event.event == SubGhzSceneReceiverInfoCustomEventTxStop) {
|
} else if(event.event == SubghzCustomEventSceneReceiverInfoTxStop) {
|
||||||
//CC1101 Stop Tx -> Start RX
|
//CC1101 Stop Tx -> Start RX
|
||||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||||
subghz_tx_stop(subghz);
|
subghz_tx_stop(subghz);
|
||||||
}
|
}
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) {
|
||||||
subghz_begin(subghz, subghz->txrx->preset);
|
subghz_begin(subghz, subghz->txrx->preset);
|
||||||
subghz_rx(subghz, subghz->txrx->frequency);
|
subghz_rx(subghz, subghz->txrx->frequency);
|
||||||
}
|
}
|
||||||
@ -135,7 +131,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
|||||||
}
|
}
|
||||||
subghz->state_notifications = NOTIFICATION_RX_STATE;
|
subghz->state_notifications = NOTIFICATION_RX_STATE;
|
||||||
return true;
|
return true;
|
||||||
} else if(event.event == SubGhzSceneReceiverInfoCustomEventSave) {
|
} else if(event.event == SubghzCustomEventSceneReceiverInfoSave) {
|
||||||
//CC1101 Stop RX -> Save
|
//CC1101 Stop RX -> Save
|
||||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
|
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
#include <lib/toolbox/random_name.h>
|
#include <lib/toolbox/random_name.h>
|
||||||
#include "file-worker.h"
|
#include "file-worker.h"
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
#define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL)
|
|
||||||
|
|
||||||
void subghz_scene_save_name_text_input_callback(void* context) {
|
void subghz_scene_save_name_text_input_callback(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_SAVE_NAME_CUSTOM_EVENT);
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneSaveName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_save_name_on_enter(void* context) {
|
void subghz_scene_save_name_on_enter(void* context) {
|
||||||
@ -21,6 +20,10 @@ void subghz_scene_save_name_on_enter(void* context) {
|
|||||||
dev_name_empty = true;
|
dev_name_empty = true;
|
||||||
} else {
|
} else {
|
||||||
memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name));
|
memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name));
|
||||||
|
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAWMenu) ==
|
||||||
|
SubghzCustomEventManagerSet) {
|
||||||
|
subghz_get_next_name_file(subghz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
text_input_set_header_text(text_input, "Name signal");
|
text_input_set_header_text(text_input, "Name signal");
|
||||||
@ -38,12 +41,14 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
|||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
|
if(event.event == SubghzCustomEventSceneSaveName) {
|
||||||
if(strcmp(subghz->file_name, "") &&
|
if(strcmp(subghz->file_name, "")) {
|
||||||
subghz_save_protocol_to_file(subghz, subghz->file_name)) {
|
|
||||||
if(strcmp(subghz->file_name_tmp, "")) {
|
if(strcmp(subghz->file_name_tmp, "")) {
|
||||||
subghz_delete_file(subghz);
|
subghz_rename_file(subghz);
|
||||||
|
} else {
|
||||||
|
subghz_save_protocol_to_file(subghz, subghz->file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
subghz_file_name_clear(subghz);
|
subghz_file_name_clear(subghz);
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
|
||||||
return true;
|
return true;
|
||||||
@ -62,4 +67,6 @@ void subghz_scene_save_name_on_exit(void* context) {
|
|||||||
|
|
||||||
// Clear view
|
// Clear view
|
||||||
text_input_clean(subghz->text_input);
|
text_input_clean(subghz->text_input);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneReadRAWMenu, SubghzCustomEventManagerNoSet);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
#define SCENE_SAVE_SUCCESS_CUSTOM_EVENT (0UL)
|
|
||||||
|
|
||||||
void subghz_scene_save_success_popup_callback(void* context) {
|
void subghz_scene_save_success_popup_callback(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT);
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneSaveSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_save_success_on_enter(void* context) {
|
void subghz_scene_save_success_on_enter(void* context) {
|
||||||
@ -24,7 +23,7 @@ void subghz_scene_save_success_on_enter(void* context) {
|
|||||||
bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) {
|
bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
|
if(event.event == SubghzCustomEventSceneSaveSuccess) {
|
||||||
if(!scene_manager_search_and_switch_to_previous_scene(
|
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||||
subghz->scene_manager, SubGhzSceneReceiver)) {
|
subghz->scene_manager, SubGhzSceneReceiver)) {
|
||||||
scene_manager_search_and_switch_to_previous_scene(
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
|||||||
@ -19,12 +19,14 @@ void subghz_scene_saved_menu_on_enter(void* context) {
|
|||||||
SubmenuIndexEmulate,
|
SubmenuIndexEmulate,
|
||||||
subghz_scene_saved_menu_submenu_callback,
|
subghz_scene_saved_menu_submenu_callback,
|
||||||
subghz);
|
subghz);
|
||||||
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
subghz->submenu,
|
subghz->submenu,
|
||||||
"Edit name",
|
"Edit name",
|
||||||
SubmenuIndexEdit,
|
SubmenuIndexEdit,
|
||||||
subghz_scene_saved_menu_submenu_callback,
|
subghz_scene_saved_menu_submenu_callback,
|
||||||
subghz);
|
subghz);
|
||||||
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
subghz->submenu,
|
subghz->submenu,
|
||||||
"Delete",
|
"Delete",
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
#define SCENE_NO_MAN_CUSTOM_EVENT (11UL)
|
|
||||||
|
|
||||||
void subghz_scene_show_error_popup_callback(void* context) {
|
void subghz_scene_show_error_popup_callback(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT);
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneShowError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_show_error_on_enter(void* context) {
|
void subghz_scene_show_error_on_enter(void* context) {
|
||||||
@ -24,7 +23,7 @@ void subghz_scene_show_error_on_enter(void* context) {
|
|||||||
bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
|
bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) {
|
if(event.event == SubghzCustomEventSceneShowError) {
|
||||||
scene_manager_search_and_switch_to_previous_scene(
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
subghz->scene_manager, SubGhzSceneStart);
|
subghz->scene_manager, SubGhzSceneStart);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
#define SCENE_NO_MAN_CUSTOM_EVENT (11UL)
|
|
||||||
|
|
||||||
void subghz_scene_show_only_rx_popup_callback(void* context) {
|
void subghz_scene_show_only_rx_popup_callback(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT);
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneShowOnlyRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_show_only_rx_on_enter(void* context) {
|
void subghz_scene_show_only_rx_on_enter(void* context) {
|
||||||
@ -30,7 +29,7 @@ void subghz_scene_show_only_rx_on_enter(void* context) {
|
|||||||
const bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) {
|
const bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) {
|
if(event.event == SubghzCustomEventSceneShowOnlyRX) {
|
||||||
scene_manager_previous_scene(subghz->scene_manager);
|
scene_manager_previous_scene(subghz->scene_manager);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
|
||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexRead,
|
SubmenuIndexRead = 10,
|
||||||
SubmenuIndexSaved,
|
SubmenuIndexSaved,
|
||||||
SubmenuIndexTest,
|
SubmenuIndexTest,
|
||||||
SubmenuIndexAddManualy,
|
SubmenuIndexAddManualy,
|
||||||
SubmenuIndexFrequencyAnalyzer,
|
SubmenuIndexFrequencyAnalyzer,
|
||||||
|
SubmenuIndexReadRAW,
|
||||||
};
|
};
|
||||||
|
|
||||||
void subghz_scene_start_submenu_callback(void* context, uint32_t index) {
|
void subghz_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||||
@ -20,6 +21,12 @@ void subghz_scene_start_on_enter(void* context) {
|
|||||||
}
|
}
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz);
|
subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz);
|
||||||
|
submenu_add_item(
|
||||||
|
subghz->submenu,
|
||||||
|
"Read Raw",
|
||||||
|
SubmenuIndexReadRAW,
|
||||||
|
subghz_scene_start_submenu_callback,
|
||||||
|
subghz);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
subghz->submenu, "Saved", SubmenuIndexSaved, subghz_scene_start_submenu_callback, subghz);
|
subghz->submenu, "Saved", SubmenuIndexSaved, subghz_scene_start_submenu_callback, subghz);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
@ -47,7 +54,12 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SubmenuIndexRead) {
|
if(event.event == SubmenuIndexReadRAW) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
|
||||||
|
return true;
|
||||||
|
} else if(event.event == SubmenuIndexRead) {
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);
|
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
|
||||||
|
|||||||
@ -2,13 +2,13 @@
|
|||||||
#include "../views/subghz_transmitter.h"
|
#include "../views/subghz_transmitter.h"
|
||||||
#include <lib/subghz/protocols/subghz_protocol_keeloq.h>
|
#include <lib/subghz/protocols/subghz_protocol_keeloq.h>
|
||||||
|
|
||||||
void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* context) {
|
void subghz_scene_transmitter_callback(SubghzCustomEvent event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void subghz_scene_transmitter_update_data_show(void* context) {
|
bool subghz_scene_transmitter_update_data_show(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
|
|
||||||
if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->get_upload_protocol) {
|
if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->get_upload_protocol) {
|
||||||
@ -51,17 +51,22 @@ static void subghz_scene_transmitter_update_data_show(void* context) {
|
|||||||
preset_str,
|
preset_str,
|
||||||
show_button);
|
show_button);
|
||||||
string_clear(key_str);
|
string_clear(key_str);
|
||||||
} else {
|
|
||||||
string_set(subghz->error_str, "Protocol not found");
|
return true;
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_scene_transmitter_on_enter(void* context) {
|
void subghz_scene_transmitter_on_enter(void* context) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
|
if(!subghz_scene_transmitter_update_data_show(subghz)) {
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
subghz->view_dispatcher, SubghzCustomEventViewTransmitterError);
|
||||||
|
}
|
||||||
|
|
||||||
subghz_transmitter_set_callback(
|
subghz_transmitter_set_callback(
|
||||||
subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz);
|
subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz);
|
||||||
subghz_scene_transmitter_update_data_show(subghz);
|
|
||||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter);
|
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter);
|
||||||
}
|
}
|
||||||
@ -69,12 +74,12 @@ void subghz_scene_transmitter_on_enter(void* context) {
|
|||||||
bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SubghzTransmitterEventSendStart) {
|
if(event.event == SubghzCustomEventViewTransmitterSendStart) {
|
||||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
subghz_rx_end(subghz);
|
subghz_rx_end(subghz);
|
||||||
}
|
}
|
||||||
if((subghz->txrx->txrx_state == SubGhzTxRxStateIdle) ||
|
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
|
||||||
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
|
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
|
||||||
if(!subghz_tx_start(subghz)) {
|
if(!subghz_tx_start(subghz)) {
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
|
||||||
@ -84,18 +89,21 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if(event.event == SubghzTransmitterEventSendStop) {
|
} else if(event.event == SubghzCustomEventViewTransmitterSendStop) {
|
||||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||||
subghz_tx_stop(subghz);
|
subghz_tx_stop(subghz);
|
||||||
subghz_sleep(subghz);
|
subghz_sleep(subghz);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if(event.event == SubghzTransmitterEventBack) {
|
} else if(event.event == SubghzCustomEventViewTransmitterBack) {
|
||||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||||
scene_manager_search_and_switch_to_previous_scene(
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
subghz->scene_manager, SubGhzSceneStart);
|
subghz->scene_manager, SubGhzSceneStart);
|
||||||
return true;
|
return true;
|
||||||
|
} else if(event.event == SubghzCustomEventViewTransmitterError) {
|
||||||
|
string_set(subghz->error_str, "Protocol not found");
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||||
}
|
}
|
||||||
} else if(event.type == SceneManagerEventTypeTick) {
|
} else if(event.type == SceneManagerEventTypeTick) {
|
||||||
if(subghz->state_notifications == NOTIFICATION_TX_STATE) {
|
if(subghz->state_notifications == NOTIFICATION_TX_STATE) {
|
||||||
|
|||||||
@ -140,6 +140,13 @@ SubGhz* subghz_alloc() {
|
|||||||
SubGhzViewFrequencyAnalyzer,
|
SubGhzViewFrequencyAnalyzer,
|
||||||
subghz_frequency_analyzer_get_view(subghz->subghz_frequency_analyzer));
|
subghz_frequency_analyzer_get_view(subghz->subghz_frequency_analyzer));
|
||||||
|
|
||||||
|
// Read RAW
|
||||||
|
subghz->subghz_read_raw = subghz_read_raw_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
subghz->view_dispatcher,
|
||||||
|
SubGhzViewReadRAW,
|
||||||
|
subghz_read_raw_get_view(subghz->subghz_read_raw));
|
||||||
|
|
||||||
// Carrier Test Module
|
// Carrier Test Module
|
||||||
subghz->subghz_test_carrier = subghz_test_carrier_alloc();
|
subghz->subghz_test_carrier = subghz_test_carrier_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
@ -167,6 +174,7 @@ SubGhz* subghz_alloc() {
|
|||||||
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
|
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
|
||||||
subghz->txrx->txrx_state = SubGhzTxRxStateSleep;
|
subghz->txrx->txrx_state = SubGhzTxRxStateSleep;
|
||||||
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
|
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
|
||||||
|
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||||
subghz->txrx->history = subghz_history_alloc();
|
subghz->txrx->history = subghz_history_alloc();
|
||||||
subghz->txrx->worker = subghz_worker_alloc();
|
subghz->txrx->worker = subghz_worker_alloc();
|
||||||
subghz->txrx->parser = subghz_parser_alloc();
|
subghz->txrx->parser = subghz_parser_alloc();
|
||||||
@ -181,6 +189,7 @@ SubGhz* subghz_alloc() {
|
|||||||
|
|
||||||
subghz_parser_load_keeloq_file(subghz->txrx->parser, "/ext/subghz/keeloq_mfcodes");
|
subghz_parser_load_keeloq_file(subghz->txrx->parser, "/ext/subghz/keeloq_mfcodes");
|
||||||
subghz_parser_load_nice_flor_s_file(subghz->txrx->parser, "/ext/subghz/nice_floor_s_rx");
|
subghz_parser_load_nice_flor_s_file(subghz->txrx->parser, "/ext/subghz/nice_floor_s_rx");
|
||||||
|
subghz_parser_load_came_atomo_file(subghz->txrx->parser, "/ext/subghz/came_atomo");
|
||||||
|
|
||||||
//subghz_parser_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
|
//subghz_parser_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
|
||||||
|
|
||||||
@ -226,6 +235,10 @@ void subghz_free(SubGhz* subghz) {
|
|||||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewFrequencyAnalyzer);
|
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewFrequencyAnalyzer);
|
||||||
subghz_frequency_analyzer_free(subghz->subghz_frequency_analyzer);
|
subghz_frequency_analyzer_free(subghz->subghz_frequency_analyzer);
|
||||||
|
|
||||||
|
// Read RAW
|
||||||
|
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewReadRAW);
|
||||||
|
subghz_read_raw_free(subghz->subghz_read_raw);
|
||||||
|
|
||||||
// Submenu
|
// Submenu
|
||||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu);
|
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu);
|
||||||
submenu_free(subghz->submenu);
|
submenu_free(subghz->submenu);
|
||||||
|
|||||||
@ -16,7 +16,7 @@ void subghz_begin(SubGhz* subghz, FuriHalSubGhzPreset preset) {
|
|||||||
furi_hal_subghz_idle();
|
furi_hal_subghz_idle();
|
||||||
furi_hal_subghz_load_preset(preset);
|
furi_hal_subghz_load_preset(preset);
|
||||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) {
|
uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) {
|
||||||
@ -59,7 +59,7 @@ void subghz_idle(SubGhz* subghz) {
|
|||||||
furi_assert(subghz);
|
furi_assert(subghz);
|
||||||
furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
|
furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
|
||||||
furi_hal_subghz_idle();
|
furi_hal_subghz_idle();
|
||||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_rx_end(SubGhz* subghz) {
|
void subghz_rx_end(SubGhz* subghz) {
|
||||||
@ -70,7 +70,7 @@ void subghz_rx_end(SubGhz* subghz) {
|
|||||||
furi_hal_subghz_stop_async_rx();
|
furi_hal_subghz_stop_async_rx();
|
||||||
}
|
}
|
||||||
furi_hal_subghz_idle();
|
furi_hal_subghz_idle();
|
||||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_sleep(SubGhz* subghz) {
|
void subghz_sleep(SubGhz* subghz) {
|
||||||
@ -191,7 +191,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
|
if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
|
||||||
file_worker, subghz->txrx->protocol_result)) {
|
file_worker, subghz->txrx->protocol_result, string_get_cstr(path))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
loaded = true;
|
loaded = true;
|
||||||
@ -208,6 +208,30 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
|
|||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool subghz_get_next_name_file(SubGhz* subghz) {
|
||||||
|
furi_assert(subghz);
|
||||||
|
|
||||||
|
FileWorker* file_worker = file_worker_alloc(false);
|
||||||
|
string_t temp_str;
|
||||||
|
string_init(temp_str);
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
if(strcmp(subghz->file_name, "")) {
|
||||||
|
//get the name of the next free file
|
||||||
|
file_worker_get_next_filename(
|
||||||
|
file_worker, SUBGHZ_RAW_PATH_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION, temp_str);
|
||||||
|
|
||||||
|
memcpy(subghz->file_name, string_get_cstr(temp_str), strlen(string_get_cstr(temp_str)));
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_clear(temp_str);
|
||||||
|
file_worker_close(file_worker);
|
||||||
|
file_worker_free(file_worker);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name) {
|
bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name) {
|
||||||
furi_assert(subghz);
|
furi_assert(subghz);
|
||||||
furi_assert(subghz->txrx->protocol_result);
|
furi_assert(subghz->txrx->protocol_result);
|
||||||
@ -334,8 +358,10 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
|
|||||||
if(subghz->txrx->protocol_result == NULL) {
|
if(subghz->txrx->protocol_result == NULL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
|
|
||||||
file_worker, subghz->txrx->protocol_result)) {
|
if(subghz->txrx->protocol_result->to_load_protocol_from_file == NULL ||
|
||||||
|
!subghz->txrx->protocol_result->to_load_protocol_from_file(
|
||||||
|
file_worker, subghz->txrx->protocol_result, string_get_cstr(protocol_file_name))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
res = true;
|
res = true;
|
||||||
@ -354,6 +380,28 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool subghz_rename_file(SubGhz* subghz) {
|
||||||
|
furi_assert(subghz);
|
||||||
|
bool ret = false;
|
||||||
|
string_t old_path;
|
||||||
|
string_t new_path;
|
||||||
|
|
||||||
|
FileWorker* file_worker = file_worker_alloc(false);
|
||||||
|
|
||||||
|
string_init_printf(
|
||||||
|
old_path, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, subghz->file_name_tmp, SUBGHZ_APP_EXTENSION);
|
||||||
|
|
||||||
|
string_init_printf(
|
||||||
|
new_path, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION);
|
||||||
|
|
||||||
|
ret = file_worker_rename(file_worker, string_get_cstr(old_path), string_get_cstr(new_path));
|
||||||
|
string_clear(old_path);
|
||||||
|
string_clear(new_path);
|
||||||
|
file_worker_close(file_worker);
|
||||||
|
file_worker_free(file_worker);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool subghz_delete_file(SubGhz* subghz) {
|
bool subghz_delete_file(SubGhz* subghz) {
|
||||||
furi_assert(subghz);
|
furi_assert(subghz);
|
||||||
|
|
||||||
@ -442,7 +490,7 @@ void subghz_hopper_update(SubGhz* subghz) {
|
|||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
subghz_rx_end(subghz);
|
subghz_rx_end(subghz);
|
||||||
};
|
};
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) {
|
||||||
subghz_parser_reset(subghz->txrx->parser);
|
subghz_parser_reset(subghz->txrx->parser);
|
||||||
subghz->txrx->frequency = subghz_hopper_frequencies[subghz->txrx->hopper_idx_frequency];
|
subghz->txrx->frequency = subghz_hopper_frequencies[subghz->txrx->hopper_idx_frequency];
|
||||||
subghz_rx(subghz, subghz->txrx->frequency);
|
subghz_rx(subghz, subghz->txrx->frequency);
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "views/subghz_receiver.h"
|
#include "views/subghz_receiver.h"
|
||||||
#include "views/subghz_transmitter.h"
|
#include "views/subghz_transmitter.h"
|
||||||
#include "views/subghz_frequency_analyzer.h"
|
#include "views/subghz_frequency_analyzer.h"
|
||||||
|
#include "views/subghz_read_raw.h"
|
||||||
|
|
||||||
#include "views/subghz_test_static.h"
|
#include "views/subghz_test_static.h"
|
||||||
#include "views/subghz_test_carrier.h"
|
#include "views/subghz_test_carrier.h"
|
||||||
@ -46,7 +47,7 @@ extern const uint32_t subghz_frequencies_433_92;
|
|||||||
|
|
||||||
/** SubGhzTxRx state */
|
/** SubGhzTxRx state */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SubGhzTxRxStateIdle,
|
SubGhzTxRxStateIDLE,
|
||||||
SubGhzTxRxStateRx,
|
SubGhzTxRxStateRx,
|
||||||
SubGhzTxRxStateTx,
|
SubGhzTxRxStateTx,
|
||||||
SubGhzTxRxStateSleep,
|
SubGhzTxRxStateSleep,
|
||||||
@ -60,6 +61,15 @@ typedef enum {
|
|||||||
SubGhzHopperStateRSSITimeOut,
|
SubGhzHopperStateRSSITimeOut,
|
||||||
} SubGhzHopperState;
|
} SubGhzHopperState;
|
||||||
|
|
||||||
|
/** SubGhzRxKeyState state */
|
||||||
|
typedef enum {
|
||||||
|
SubGhzRxKeyStateIDLE,
|
||||||
|
SubGhzRxKeyStateNoSave,
|
||||||
|
SubGhzRxKeyStateNeedSave,
|
||||||
|
SubGhzRxKeyStateAddKey,
|
||||||
|
SubGhzRxKeyStateExit,
|
||||||
|
} SubGhzRxKeyState;
|
||||||
|
|
||||||
struct SubGhzTxRx {
|
struct SubGhzTxRx {
|
||||||
SubGhzWorker* worker;
|
SubGhzWorker* worker;
|
||||||
SubGhzParser* parser;
|
SubGhzParser* parser;
|
||||||
@ -70,10 +80,10 @@ struct SubGhzTxRx {
|
|||||||
SubGhzHistory* history;
|
SubGhzHistory* history;
|
||||||
uint16_t idx_menu_chosen;
|
uint16_t idx_menu_chosen;
|
||||||
SubGhzTxRxState txrx_state;
|
SubGhzTxRxState txrx_state;
|
||||||
//bool hopper_runing;
|
|
||||||
SubGhzHopperState hopper_state;
|
SubGhzHopperState hopper_state;
|
||||||
uint8_t hopper_timeout;
|
uint8_t hopper_timeout;
|
||||||
uint8_t hopper_idx_frequency;
|
uint8_t hopper_idx_frequency;
|
||||||
|
SubGhzRxKeyState rx_key_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct SubGhzTxRx SubGhzTxRx;
|
typedef struct SubGhzTxRx SubGhzTxRx;
|
||||||
@ -100,6 +110,7 @@ struct SubGhz {
|
|||||||
VariableItemList* variable_item_list;
|
VariableItemList* variable_item_list;
|
||||||
|
|
||||||
SubghzFrequencyAnalyzer* subghz_frequency_analyzer;
|
SubghzFrequencyAnalyzer* subghz_frequency_analyzer;
|
||||||
|
SubghzReadRAW* subghz_read_raw;
|
||||||
SubghzTestStatic* subghz_test_static;
|
SubghzTestStatic* subghz_test_static;
|
||||||
SubghzTestCarrier* subghz_test_carrier;
|
SubghzTestCarrier* subghz_test_carrier;
|
||||||
SubghzTestPacket* subghz_test_packet;
|
SubghzTestPacket* subghz_test_packet;
|
||||||
@ -116,6 +127,7 @@ typedef enum {
|
|||||||
SubGhzViewTransmitter,
|
SubGhzViewTransmitter,
|
||||||
SubGhzViewVariableItemList,
|
SubGhzViewVariableItemList,
|
||||||
SubGhzViewFrequencyAnalyzer,
|
SubGhzViewFrequencyAnalyzer,
|
||||||
|
SubGhzViewReadRAW,
|
||||||
SubGhzViewStatic,
|
SubGhzViewStatic,
|
||||||
SubGhzViewTestCarrier,
|
SubGhzViewTestCarrier,
|
||||||
SubGhzViewTestPacket,
|
SubGhzViewTestPacket,
|
||||||
@ -128,8 +140,10 @@ void subghz_sleep(SubGhz* subghz);
|
|||||||
bool subghz_tx_start(SubGhz* subghz);
|
bool subghz_tx_start(SubGhz* subghz);
|
||||||
void subghz_tx_stop(SubGhz* subghz);
|
void subghz_tx_stop(SubGhz* subghz);
|
||||||
bool subghz_key_load(SubGhz* subghz, const char* file_path);
|
bool subghz_key_load(SubGhz* subghz, const char* file_path);
|
||||||
|
bool subghz_get_next_name_file(SubGhz* subghz);
|
||||||
bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name);
|
bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name);
|
||||||
bool subghz_load_protocol_from_file(SubGhz* subghz);
|
bool subghz_load_protocol_from_file(SubGhz* subghz);
|
||||||
|
bool subghz_rename_file(SubGhz* subghz);
|
||||||
bool subghz_delete_file(SubGhz* subghz);
|
bool subghz_delete_file(SubGhz* subghz);
|
||||||
void subghz_file_name_clear(SubGhz* subghz);
|
void subghz_file_name_clear(SubGhz* subghz);
|
||||||
uint32_t subghz_random_serial(void);
|
uint32_t subghz_random_serial(void);
|
||||||
|
|||||||
@ -1,14 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gui/view.h>
|
#include <gui/view.h>
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
typedef enum {
|
|
||||||
SubghzFrequencyAnalyzerEventOnlyRx,
|
|
||||||
} SubghzFrequencyAnalyzerEvent;
|
|
||||||
|
|
||||||
typedef struct SubghzFrequencyAnalyzer SubghzFrequencyAnalyzer;
|
typedef struct SubghzFrequencyAnalyzer SubghzFrequencyAnalyzer;
|
||||||
|
|
||||||
typedef void (*SubghzFrequencyAnalyzerCallback)(SubghzFrequencyAnalyzerEvent event, void* context);
|
typedef void (*SubghzFrequencyAnalyzerCallback)(SubghzCustomEvent event, void* context);
|
||||||
|
|
||||||
void subghz_frequency_analyzer_set_callback(
|
void subghz_frequency_analyzer_set_callback(
|
||||||
SubghzFrequencyAnalyzer* subghz_frequency_analyzer,
|
SubghzFrequencyAnalyzer* subghz_frequency_analyzer,
|
||||||
|
|||||||
274
applications/subghz/views/subghz_read_raw.c
Normal file
274
applications/subghz/views/subghz_read_raw.c
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
#include "subghz_read_raw.h"
|
||||||
|
#include "../subghz_i.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi-hal.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <lib/subghz/protocols/subghz_protocol_princeton.h>
|
||||||
|
|
||||||
|
#include <assets_icons.h>
|
||||||
|
#define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubghzReadRAWStatusStart,
|
||||||
|
SubghzReadRAWStatusIDLE,
|
||||||
|
SubghzReadRAWStatusREC,
|
||||||
|
//SubghzReadRAWStatusShowName,
|
||||||
|
} SubghzReadRAWStatus;
|
||||||
|
|
||||||
|
struct SubghzReadRAW {
|
||||||
|
View* view;
|
||||||
|
SubghzReadRAWCallback callback;
|
||||||
|
void* context;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
string_t frequency_str;
|
||||||
|
string_t preset_str;
|
||||||
|
string_t sample_write;
|
||||||
|
uint8_t* rssi_history;
|
||||||
|
bool rssi_history_end;
|
||||||
|
uint8_t ind_write;
|
||||||
|
SubghzReadRAWStatus satus;
|
||||||
|
} SubghzReadRAWModel;
|
||||||
|
|
||||||
|
void subghz_read_raw_set_callback(
|
||||||
|
SubghzReadRAW* subghz_read_raw,
|
||||||
|
SubghzReadRAWCallback callback,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(subghz_read_raw);
|
||||||
|
furi_assert(callback);
|
||||||
|
subghz_read_raw->callback = callback;
|
||||||
|
subghz_read_raw->context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_read_raw_add_data_statusbar(
|
||||||
|
SubghzReadRAW* instance,
|
||||||
|
const char* frequency_str,
|
||||||
|
const char* preset_str) {
|
||||||
|
furi_assert(instance);
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
string_set(model->frequency_str, frequency_str);
|
||||||
|
string_set(model->preset_str, preset_str);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_read_raw_add_data_rssi(SubghzReadRAW* instance, float rssi) {
|
||||||
|
furi_assert(instance);
|
||||||
|
uint8_t u_rssi = 0;
|
||||||
|
|
||||||
|
if(rssi < -90) {
|
||||||
|
u_rssi = 0;
|
||||||
|
} else {
|
||||||
|
u_rssi = (uint8_t)((rssi + 90) / 2.7);
|
||||||
|
}
|
||||||
|
//if(u_rssi > 34) u_rssi = 34;
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
model->rssi_history[model->ind_write++] = u_rssi;
|
||||||
|
if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) {
|
||||||
|
model->rssi_history_end = true;
|
||||||
|
model->ind_write = 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_read_raw_update_sample_write(SubghzReadRAW* instance, size_t sample) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
string_printf(model->sample_write, "%d spl.", sample);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_read_raw_draw_rssi(Canvas* canvas, SubghzReadRAWModel* model) {
|
||||||
|
int ind = 0;
|
||||||
|
int base = 0;
|
||||||
|
if(model->rssi_history_end == false) {
|
||||||
|
for(int i = model->ind_write; i >= 0; i--) {
|
||||||
|
canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]);
|
||||||
|
}
|
||||||
|
if(model->ind_write > 3) {
|
||||||
|
canvas_draw_line(canvas, model->ind_write, 47, model->ind_write, 13);
|
||||||
|
canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12);
|
||||||
|
canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write;
|
||||||
|
for(int i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i >= 0; i--) {
|
||||||
|
ind = i - base;
|
||||||
|
if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE;
|
||||||
|
canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]);
|
||||||
|
}
|
||||||
|
canvas_draw_line(
|
||||||
|
canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 13);
|
||||||
|
canvas_draw_line(
|
||||||
|
canvas,
|
||||||
|
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2,
|
||||||
|
12,
|
||||||
|
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 2,
|
||||||
|
12);
|
||||||
|
canvas_draw_line(
|
||||||
|
canvas,
|
||||||
|
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
|
||||||
|
13,
|
||||||
|
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
|
||||||
|
13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_read_raw_draw(Canvas* canvas, SubghzReadRAWModel* model) {
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 5, 8, string_get_cstr(model->frequency_str));
|
||||||
|
canvas_draw_str(canvas, 40, 8, string_get_cstr(model->preset_str));
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write));
|
||||||
|
|
||||||
|
canvas_draw_line(canvas, 0, 14, 115, 14);
|
||||||
|
subghz_read_raw_draw_rssi(canvas, model);
|
||||||
|
canvas_draw_line(canvas, 0, 48, 115, 48);
|
||||||
|
canvas_draw_line(canvas, 115, 14, 115, 48);
|
||||||
|
|
||||||
|
if(model->satus == SubghzReadRAWStatusIDLE) {
|
||||||
|
elements_button_left(canvas, "Config");
|
||||||
|
elements_button_center(canvas, "REC");
|
||||||
|
elements_button_right(canvas, "More");
|
||||||
|
} else if(model->satus == SubghzReadRAWStatusStart) {
|
||||||
|
elements_button_left(canvas, "Config");
|
||||||
|
elements_button_center(canvas, "REC");
|
||||||
|
} else {
|
||||||
|
elements_button_center(canvas, "Stop");
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_set_font_direction(canvas, 3);
|
||||||
|
canvas_draw_str(canvas, 126, 40, "RSSI");
|
||||||
|
canvas_set_font_direction(canvas, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_read_raw_input(InputEvent* event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubghzReadRAW* instance = context;
|
||||||
|
|
||||||
|
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||||
|
instance->callback(SubghzCustomEventViewReadRAWBack, instance->context);
|
||||||
|
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
if(model->satus == SubghzReadRAWStatusIDLE ||
|
||||||
|
model->satus == SubghzReadRAWStatusStart) {
|
||||||
|
instance->callback(SubghzCustomEventViewReadRAWConfig, instance->context);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else if(event->key == InputKeyRight && event->type == InputTypeShort) {
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
if(model->satus == SubghzReadRAWStatusIDLE) {
|
||||||
|
instance->callback(SubghzCustomEventViewReadRAWMore, instance->context);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
if(model->satus == SubghzReadRAWStatusIDLE ||
|
||||||
|
model->satus == SubghzReadRAWStatusStart) {
|
||||||
|
instance->callback(SubghzCustomEventViewReadRAWREC, instance->context);
|
||||||
|
model->satus = SubghzReadRAWStatusREC;
|
||||||
|
model->ind_write = 0;
|
||||||
|
model->rssi_history_end = false;
|
||||||
|
} else {
|
||||||
|
instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
|
||||||
|
model->satus = SubghzReadRAWStatusIDLE;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_read_raw_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubghzReadRAW* instance = context;
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
model->satus = SubghzReadRAWStatusStart;
|
||||||
|
model->rssi_history = furi_alloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t));
|
||||||
|
model->rssi_history_end = false;
|
||||||
|
model->ind_write = 0;
|
||||||
|
string_set(model->sample_write, "0 spl.");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_read_raw_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubghzReadRAW* instance = context;
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
if(model->satus != SubghzReadRAWStatusIDLE &&
|
||||||
|
model->satus != SubghzReadRAWStatusStart) {
|
||||||
|
instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
|
||||||
|
model->satus = SubghzReadRAWStatusStart;
|
||||||
|
}
|
||||||
|
string_clean(model->frequency_str);
|
||||||
|
string_clean(model->preset_str);
|
||||||
|
string_clean(model->sample_write);
|
||||||
|
free(model->rssi_history);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SubghzReadRAW* subghz_read_raw_alloc() {
|
||||||
|
SubghzReadRAW* instance = furi_alloc(sizeof(SubghzReadRAW));
|
||||||
|
|
||||||
|
// View allocation and configuration
|
||||||
|
instance->view = view_alloc();
|
||||||
|
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubghzReadRAWModel));
|
||||||
|
view_set_context(instance->view, instance);
|
||||||
|
view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_read_raw_draw);
|
||||||
|
view_set_input_callback(instance->view, subghz_read_raw_input);
|
||||||
|
view_set_enter_callback(instance->view, subghz_read_raw_enter);
|
||||||
|
view_set_exit_callback(instance->view, subghz_read_raw_exit);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
string_init(model->frequency_str);
|
||||||
|
string_init(model->preset_str);
|
||||||
|
string_init(model->sample_write);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_read_raw_free(SubghzReadRAW* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubghzReadRAWModel * model) {
|
||||||
|
string_clear(model->frequency_str);
|
||||||
|
string_clear(model->preset_str);
|
||||||
|
string_clear(model->sample_write);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
view_free(instance->view);
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
View* subghz_read_raw_get_view(SubghzReadRAW* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
return instance->view;
|
||||||
|
}
|
||||||
28
applications/subghz/views/subghz_read_raw.h
Normal file
28
applications/subghz/views/subghz_read_raw.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
|
|
||||||
|
typedef struct SubghzReadRAW SubghzReadRAW;
|
||||||
|
|
||||||
|
typedef void (*SubghzReadRAWCallback)(SubghzCustomEvent event, void* context);
|
||||||
|
|
||||||
|
void subghz_read_raw_set_callback(
|
||||||
|
SubghzReadRAW* subghz_read_raw,
|
||||||
|
SubghzReadRAWCallback callback,
|
||||||
|
void* context);
|
||||||
|
|
||||||
|
SubghzReadRAW* subghz_read_raw_alloc();
|
||||||
|
|
||||||
|
void subghz_read_raw_free(SubghzReadRAW* subghz_static);
|
||||||
|
|
||||||
|
void subghz_read_raw_add_data_statusbar(
|
||||||
|
SubghzReadRAW* instance,
|
||||||
|
const char* frequency_str,
|
||||||
|
const char* preset_str);
|
||||||
|
|
||||||
|
void subghz_read_raw_update_sample_write(SubghzReadRAW* instance, size_t sample);
|
||||||
|
|
||||||
|
void subghz_read_raw_add_data_rssi(SubghzReadRAW* instance, float rssi);
|
||||||
|
|
||||||
|
View* subghz_read_raw_get_view(SubghzReadRAW* subghz_static);
|
||||||
@ -181,7 +181,7 @@ bool subghz_receiver_input(InputEvent* event, void* context) {
|
|||||||
SubghzReceiver* subghz_receiver = context;
|
SubghzReceiver* subghz_receiver = context;
|
||||||
|
|
||||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||||
subghz_receiver->callback(SubghzReceverEventBack, subghz_receiver->context);
|
subghz_receiver->callback(SubghzCustomEventViewReceverBack, subghz_receiver->context);
|
||||||
} else if(
|
} else if(
|
||||||
event->key == InputKeyUp &&
|
event->key == InputKeyUp &&
|
||||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||||
@ -199,12 +199,13 @@ bool subghz_receiver_input(InputEvent* event, void* context) {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||||
subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context);
|
subghz_receiver->callback(SubghzCustomEventViewReceverConfig, subghz_receiver->context);
|
||||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||||
if(model->history_item != 0) {
|
if(model->history_item != 0) {
|
||||||
subghz_receiver->callback(SubghzReceverEventOK, subghz_receiver->context);
|
subghz_receiver->callback(
|
||||||
|
SubghzCustomEventViewReceverOK, subghz_receiver->context);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,16 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gui/view.h>
|
#include <gui/view.h>
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
typedef enum {
|
|
||||||
SubghzReceverEventOK,
|
|
||||||
SubghzReceverEventConfig,
|
|
||||||
SubghzReceverEventBack,
|
|
||||||
} SubghzReceverEvent;
|
|
||||||
|
|
||||||
typedef struct SubghzReceiver SubghzReceiver;
|
typedef struct SubghzReceiver SubghzReceiver;
|
||||||
|
|
||||||
typedef void (*SubghzReceiverCallback)(SubghzReceverEvent event, void* context);
|
typedef void (*SubghzReceiverCallback)(SubghzCustomEvent event, void* context);
|
||||||
|
|
||||||
void subghz_receiver_set_callback(
|
void subghz_receiver_set_callback(
|
||||||
SubghzReceiver* subghz_receiver,
|
SubghzReceiver* subghz_receiver,
|
||||||
|
|||||||
@ -111,10 +111,12 @@ bool subghz_transmitter_input(InputEvent* event, void* context) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
|
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
|
||||||
subghz_transmitter->callback(SubghzTransmitterEventSendStart, subghz_transmitter->context);
|
subghz_transmitter->callback(
|
||||||
|
SubghzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
|
||||||
return true;
|
return true;
|
||||||
} else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) {
|
} else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) {
|
||||||
subghz_transmitter->callback(SubghzTransmitterEventSendStop, subghz_transmitter->context);
|
subghz_transmitter->callback(
|
||||||
|
SubghzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gui/view.h>
|
#include <gui/view.h>
|
||||||
|
#include "../helpers/subghz_custom_event.h"
|
||||||
typedef enum {
|
|
||||||
SubghzTransmitterEventSendStart,
|
|
||||||
SubghzTransmitterEventSendStop,
|
|
||||||
SubghzTransmitterEventBack,
|
|
||||||
SubghzTransmitterEventNoMan,
|
|
||||||
} SubghzTransmitterEvent;
|
|
||||||
|
|
||||||
typedef struct SubghzTransmitter SubghzTransmitter;
|
typedef struct SubghzTransmitter SubghzTransmitter;
|
||||||
|
|
||||||
typedef void (*SubghzTransmitterCallback)(SubghzTransmitterEvent event, void* context);
|
typedef void (*SubghzTransmitterCallback)(SubghzCustomEvent event, void* context);
|
||||||
|
|
||||||
void subghz_transmitter_set_callback(
|
void subghz_transmitter_set_callback(
|
||||||
SubghzTransmitter* subghz_transmitter,
|
SubghzTransmitter* subghz_transmitter,
|
||||||
|
|||||||
@ -11,7 +11,10 @@ void test_furi_create_open() {
|
|||||||
// 2. Open it
|
// 2. Open it
|
||||||
void* record = furi_record_open("test/holding");
|
void* record = furi_record_open("test/holding");
|
||||||
mu_assert_pointers_eq(record, &test_data);
|
mu_assert_pointers_eq(record, &test_data);
|
||||||
|
|
||||||
|
// 3. Close it
|
||||||
furi_record_close("test/holding");
|
furi_record_close("test/holding");
|
||||||
|
|
||||||
|
// 4. Clean up
|
||||||
furi_record_destroy("test/holding");
|
furi_record_destroy("test/holding");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -325,7 +325,6 @@ MU_TEST_SUITE(test_irda_decoder_encoder) {
|
|||||||
|
|
||||||
int run_minunit_test_irda_decoder_encoder() {
|
int run_minunit_test_irda_decoder_encoder() {
|
||||||
MU_RUN_SUITE(test_irda_decoder_encoder);
|
MU_RUN_SUITE(test_irda_decoder_encoder);
|
||||||
MU_REPORT();
|
|
||||||
|
|
||||||
return MU_EXIT_CODE;
|
return MU_EXIT_CODE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,6 +79,9 @@ extern "C" {
|
|||||||
__attribute__((unused)) static void (*minunit_setup)(void) = NULL;
|
__attribute__((unused)) static void (*minunit_setup)(void) = NULL;
|
||||||
__attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
__attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
||||||
|
|
||||||
|
void minunit_print_progress(void);
|
||||||
|
void minunit_print_fail(const char* error);
|
||||||
|
|
||||||
/* Definitions */
|
/* Definitions */
|
||||||
#define MU_TEST(method_name) static void method_name(void)
|
#define MU_TEST(method_name) static void method_name(void)
|
||||||
#define MU_TEST_SUITE(suite_name) static void suite_name(void)
|
#define MU_TEST_SUITE(suite_name) static void suite_name(void)
|
||||||
@ -108,8 +111,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_run++; \
|
minunit_run++; \
|
||||||
if(minunit_status) { \
|
if(minunit_status) { \
|
||||||
minunit_fail++; \
|
minunit_fail++; \
|
||||||
printf("F"); \
|
minunit_print_fail(minunit_last_message); \
|
||||||
printf("\n%s\n", minunit_last_message); \
|
|
||||||
} fflush(stdout); \
|
} fflush(stdout); \
|
||||||
if(minunit_teardown)(*minunit_teardown)();)
|
if(minunit_teardown)(*minunit_teardown)();)
|
||||||
|
|
||||||
@ -142,7 +144,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
#test); \
|
#test); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_fail(message) \
|
#define mu_fail(message) \
|
||||||
MU__SAFE_BLOCK(minunit_assert++; snprintf( \
|
MU__SAFE_BLOCK(minunit_assert++; snprintf( \
|
||||||
@ -169,7 +171,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
message); \
|
message); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_int_eq(expected, result) \
|
#define mu_assert_int_eq(expected, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -187,7 +189,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_r); \
|
minunit_tmp_r); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_int_not_eq(expected, result) \
|
#define mu_assert_int_not_eq(expected, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -204,7 +206,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_e); \
|
minunit_tmp_e); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_int_greater_than(val, result) \
|
#define mu_assert_int_greater_than(val, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -222,7 +224,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_e); \
|
minunit_tmp_e); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_int_less_than(val, result) \
|
#define mu_assert_int_less_than(val, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -240,7 +242,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_e); \
|
minunit_tmp_e); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_int_between(expected_lower, expected_upper, result) \
|
#define mu_assert_int_between(expected_lower, expected_upper, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -261,7 +263,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_m); \
|
minunit_tmp_m); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_int_in(expected, array_length, result) \
|
#define mu_assert_int_in(expected, array_length, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -288,7 +290,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_r); \
|
minunit_tmp_r); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_double_eq(expected, result) \
|
#define mu_assert_double_eq(expected, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -309,7 +311,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_r); \
|
minunit_tmp_r); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_double_greater_than(val, result) \
|
#define mu_assert_double_greater_than(val, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -327,7 +329,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_e); \
|
minunit_tmp_e); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_double_less_than(val, result) \
|
#define mu_assert_double_less_than(val, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -345,7 +347,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_e); \
|
minunit_tmp_e); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_double_between(expected_lower, expected_upper, result) \
|
#define mu_assert_double_between(expected_lower, expected_upper, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -366,7 +368,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_m); \
|
minunit_tmp_m); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_string_eq(expected, result) \
|
#define mu_assert_string_eq(expected, result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
@ -386,39 +388,39 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
minunit_tmp_r); \
|
minunit_tmp_r); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
} else { printf("."); })
|
} else { minunit_print_progress(); })
|
||||||
|
|
||||||
#define mu_assert_null(result) \
|
#define mu_assert_null(result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
minunit_assert++; if(result == NULL) { printf("."); } else { \
|
minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \
|
||||||
snprintf( \
|
snprintf( \
|
||||||
minunit_last_message, \
|
minunit_last_message, \
|
||||||
MINUNIT_MESSAGE_LEN, \
|
MINUNIT_MESSAGE_LEN, \
|
||||||
"%s failed:\n\t%s:%d: Expected result was not NULL", \
|
"%s failed:\n\t%s:%d: Expected result was not NULL", \
|
||||||
__func__, \
|
__func__, \
|
||||||
__FILE__, \
|
__FILE__, \
|
||||||
__LINE__); \
|
__LINE__); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define mu_assert_not_null(result) \
|
#define mu_assert_not_null(result) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
minunit_assert++; if(result != NULL) { printf("."); } else { \
|
minunit_assert++; if(result != NULL) { minunit_print_progress(); } else { \
|
||||||
snprintf( \
|
snprintf( \
|
||||||
minunit_last_message, \
|
minunit_last_message, \
|
||||||
MINUNIT_MESSAGE_LEN, \
|
MINUNIT_MESSAGE_LEN, \
|
||||||
"%s failed:\n\t%s:%d: Expected result was not NULL", \
|
"%s failed:\n\t%s:%d: Expected result was not NULL", \
|
||||||
__func__, \
|
__func__, \
|
||||||
__FILE__, \
|
__FILE__, \
|
||||||
__LINE__); \
|
__LINE__); \
|
||||||
minunit_status = 1; \
|
minunit_status = 1; \
|
||||||
return; \
|
return; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define mu_assert_pointers_eq(pointer1, pointer2) \
|
#define mu_assert_pointers_eq(pointer1, pointer2) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
minunit_assert++; if(pointer1 == pointer2) { printf("."); } else { \
|
minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \
|
||||||
snprintf( \
|
snprintf( \
|
||||||
minunit_last_message, \
|
minunit_last_message, \
|
||||||
MINUNIT_MESSAGE_LEN, \
|
MINUNIT_MESSAGE_LEN, \
|
||||||
@ -432,7 +434,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
|||||||
|
|
||||||
#define mu_assert_pointers_not_eq(pointer1, pointer2) \
|
#define mu_assert_pointers_not_eq(pointer1, pointer2) \
|
||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
minunit_assert++; if(pointer1 != pointer2) { printf("."); } else { \
|
minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \
|
||||||
snprintf( \
|
snprintf( \
|
||||||
minunit_last_message, \
|
minunit_last_message, \
|
||||||
MINUNIT_MESSAGE_LEN, \
|
MINUNIT_MESSAGE_LEN, \
|
||||||
|
|||||||
@ -62,7 +62,6 @@ MU_TEST_SUITE(test_suite) {
|
|||||||
|
|
||||||
int run_minunit() {
|
int run_minunit() {
|
||||||
MU_RUN_SUITE(test_suite);
|
MU_RUN_SUITE(test_suite);
|
||||||
MU_REPORT();
|
|
||||||
|
|
||||||
return MU_EXIT_CODE;
|
return MU_EXIT_CODE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,25 +56,40 @@ static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected);
|
|||||||
static void test_rpc_decode_and_compare(MsgList_t expected_msg_list);
|
static void test_rpc_decode_and_compare(MsgList_t expected_msg_list);
|
||||||
static void test_rpc_free_msg_list(MsgList_t msg_list);
|
static void test_rpc_free_msg_list(MsgList_t msg_list);
|
||||||
|
|
||||||
static void test_rpc_storage_setup(void) {
|
static void test_rpc_setup(void) {
|
||||||
furi_assert(!rpc);
|
furi_assert(!rpc);
|
||||||
furi_assert(!session);
|
furi_assert(!session);
|
||||||
furi_assert(!output_stream);
|
furi_assert(!output_stream);
|
||||||
|
|
||||||
rpc = furi_record_open("rpc");
|
rpc = furi_record_open("rpc");
|
||||||
for(int i = 0; !session && (i < 10000); ++i) {
|
for(int i = 0; !session && (i < 10000); ++i) {
|
||||||
session = rpc_open_session(rpc);
|
session = rpc_session_open(rpc);
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
furi_assert(session);
|
furi_assert(session);
|
||||||
|
|
||||||
|
output_stream = xStreamBufferCreate(1000, 1);
|
||||||
|
mu_assert(session, "failed to start session");
|
||||||
|
rpc_session_set_send_bytes_callback(session, output_bytes_callback);
|
||||||
|
rpc_session_set_context(session, output_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rpc_teardown(void) {
|
||||||
|
rpc_session_close(session);
|
||||||
|
furi_record_close("rpc");
|
||||||
|
vStreamBufferDelete(output_stream);
|
||||||
|
++command_id;
|
||||||
|
output_stream = NULL;
|
||||||
|
rpc = NULL;
|
||||||
|
session = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rpc_storage_setup(void) {
|
||||||
|
test_rpc_setup();
|
||||||
|
|
||||||
Storage* fs_api = furi_record_open("storage");
|
Storage* fs_api = furi_record_open("storage");
|
||||||
clean_directory(fs_api, TEST_DIR_NAME);
|
clean_directory(fs_api, TEST_DIR_NAME);
|
||||||
furi_record_close("storage");
|
furi_record_close("storage");
|
||||||
|
|
||||||
output_stream = xStreamBufferCreate(1000, 1);
|
|
||||||
mu_assert(session, "failed to start session");
|
|
||||||
rpc_set_send_bytes_callback(session, output_bytes_callback, output_stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_rpc_storage_teardown(void) {
|
static void test_rpc_storage_teardown(void) {
|
||||||
@ -82,13 +97,7 @@ static void test_rpc_storage_teardown(void) {
|
|||||||
clean_directory(fs_api, TEST_DIR_NAME);
|
clean_directory(fs_api, TEST_DIR_NAME);
|
||||||
furi_record_close("storage");
|
furi_record_close("storage");
|
||||||
|
|
||||||
rpc_close_session(session);
|
test_rpc_teardown();
|
||||||
furi_record_close("rpc");
|
|
||||||
vStreamBufferDelete(output_stream);
|
|
||||||
++command_id;
|
|
||||||
output_stream = NULL;
|
|
||||||
rpc = NULL;
|
|
||||||
session = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clean_directory(Storage* fs_api, const char* clean_dir) {
|
static void clean_directory(Storage* fs_api, const char* clean_dir) {
|
||||||
@ -197,10 +206,12 @@ static void test_rpc_create_simple_message(
|
|||||||
const char* str,
|
const char* str,
|
||||||
uint32_t command_id) {
|
uint32_t command_id) {
|
||||||
furi_assert(message);
|
furi_assert(message);
|
||||||
furi_assert(str);
|
|
||||||
|
|
||||||
char* str_copy = furi_alloc(strlen(str) + 1);
|
char* str_copy = NULL;
|
||||||
strcpy(str_copy, str);
|
if(str) {
|
||||||
|
str_copy = furi_alloc(strlen(str) + 1);
|
||||||
|
strcpy(str_copy, str);
|
||||||
|
}
|
||||||
message->command_id = command_id;
|
message->command_id = command_id;
|
||||||
message->command_status = PB_CommandStatus_OK;
|
message->command_status = PB_CommandStatus_OK;
|
||||||
message->cb_content.funcs.encode = NULL;
|
message->cb_content.funcs.encode = NULL;
|
||||||
@ -292,7 +303,7 @@ static void test_rpc_encode_and_feed_one(PB_Main* request) {
|
|||||||
size_t bytes_left = ostream.bytes_written;
|
size_t bytes_left = ostream.bytes_written;
|
||||||
uint8_t* buffer_ptr = buffer;
|
uint8_t* buffer_ptr = buffer;
|
||||||
do {
|
do {
|
||||||
size_t bytes_sent = rpc_feed_bytes(session, buffer_ptr, bytes_left, 1000);
|
size_t bytes_sent = rpc_session_feed(session, buffer_ptr, bytes_left, 1000);
|
||||||
mu_check(bytes_sent > 0);
|
mu_check(bytes_sent > 0);
|
||||||
|
|
||||||
bytes_left -= bytes_sent;
|
bytes_left -= bytes_sent;
|
||||||
@ -402,6 +413,38 @@ static bool test_rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_
|
|||||||
return (count == bytes_received);
|
return (count == bytes_received);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_rpc_storage_list_create_expected_list_root(MsgList_t msg_list, uint32_t command_id) {
|
||||||
|
PB_Main* message = MsgList_push_new(msg_list);
|
||||||
|
message->has_next = false;
|
||||||
|
message->cb_content.funcs.encode = NULL;
|
||||||
|
message->command_id = command_id;
|
||||||
|
message->which_content = PB_Main_storage_list_response_tag;
|
||||||
|
|
||||||
|
message->content.storage_list_response.file_count = 3;
|
||||||
|
message->content.storage_list_response.file[0].data = NULL;
|
||||||
|
message->content.storage_list_response.file[1].data = NULL;
|
||||||
|
message->content.storage_list_response.file[2].data = NULL;
|
||||||
|
|
||||||
|
message->content.storage_list_response.file[0].size = 0;
|
||||||
|
message->content.storage_list_response.file[1].size = 0;
|
||||||
|
message->content.storage_list_response.file[2].size = 0;
|
||||||
|
|
||||||
|
message->content.storage_list_response.file[0].type = PB_Storage_File_FileType_DIR;
|
||||||
|
message->content.storage_list_response.file[1].type = PB_Storage_File_FileType_DIR;
|
||||||
|
message->content.storage_list_response.file[2].type = PB_Storage_File_FileType_DIR;
|
||||||
|
|
||||||
|
char* str = furi_alloc(4);
|
||||||
|
strcpy(str, "any");
|
||||||
|
message->content.storage_list_response.file[0].name = str;
|
||||||
|
str = furi_alloc(4);
|
||||||
|
strcpy(str, "int");
|
||||||
|
message->content.storage_list_response.file[1].name = str;
|
||||||
|
str = furi_alloc(4);
|
||||||
|
strcpy(str, "ext");
|
||||||
|
message->content.storage_list_response.file[2].name = str;
|
||||||
|
}
|
||||||
|
|
||||||
static void test_rpc_storage_list_create_expected_list(
|
static void test_rpc_storage_list_create_expected_list(
|
||||||
MsgList_t msg_list,
|
MsgList_t msg_list,
|
||||||
const char* path,
|
const char* path,
|
||||||
@ -505,7 +548,11 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) {
|
|||||||
MsgList_init(expected_msg_list);
|
MsgList_init(expected_msg_list);
|
||||||
|
|
||||||
test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id);
|
test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id);
|
||||||
test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id);
|
if(!strcmp(path, "/")) {
|
||||||
|
test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id);
|
||||||
|
} else {
|
||||||
|
test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id);
|
||||||
|
}
|
||||||
test_rpc_encode_and_feed_one(&request);
|
test_rpc_encode_and_feed_one(&request);
|
||||||
test_rpc_decode_and_compare(expected_msg_list);
|
test_rpc_decode_and_compare(expected_msg_list);
|
||||||
|
|
||||||
@ -514,6 +561,7 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(test_storage_list) {
|
MU_TEST(test_storage_list) {
|
||||||
|
test_rpc_storage_list_run("/", ++command_id);
|
||||||
test_rpc_storage_list_run("/ext/nfc", ++command_id);
|
test_rpc_storage_list_run("/ext/nfc", ++command_id);
|
||||||
|
|
||||||
test_rpc_storage_list_run("/int", ++command_id);
|
test_rpc_storage_list_run("/int", ++command_id);
|
||||||
@ -597,12 +645,23 @@ static void test_storage_read_run(const char* path, uint32_t command_id) {
|
|||||||
test_rpc_free_msg_list(expected_msg_list);
|
test_rpc_free_msg_list(expected_msg_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool test_is_exists(const char* path) {
|
||||||
|
Storage* fs_api = furi_record_open("storage");
|
||||||
|
FileInfo fileinfo;
|
||||||
|
FS_Error result = storage_common_stat(fs_api, path, &fileinfo);
|
||||||
|
|
||||||
|
furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST));
|
||||||
|
|
||||||
|
return result == FSE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static void test_create_dir(const char* path) {
|
static void test_create_dir(const char* path) {
|
||||||
Storage* fs_api = furi_record_open("storage");
|
Storage* fs_api = furi_record_open("storage");
|
||||||
FS_Error error = storage_common_mkdir(fs_api, path);
|
FS_Error error = storage_common_mkdir(fs_api, path);
|
||||||
(void)error;
|
(void)error;
|
||||||
furi_assert((error == FSE_OK) || (error == FSE_EXIST));
|
furi_assert((error == FSE_OK) || (error == FSE_EXIST));
|
||||||
furi_record_close("storage");
|
furi_record_close("storage");
|
||||||
|
furi_check(test_is_exists(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_create_file(const char* path, size_t size) {
|
static void test_create_file(const char* path, size_t size) {
|
||||||
@ -625,6 +684,7 @@ static void test_create_file(const char* path, size_t size) {
|
|||||||
storage_file_free(file);
|
storage_file_free(file);
|
||||||
|
|
||||||
furi_record_close("storage");
|
furi_record_close("storage");
|
||||||
|
furi_check(test_is_exists(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(test_storage_read) {
|
MU_TEST(test_storage_read) {
|
||||||
@ -829,12 +889,17 @@ MU_TEST(test_storage_interrupt_continuous_another_system) {
|
|||||||
test_rpc_free_msg_list(expected_msg_list);
|
test_rpc_free_msg_list(expected_msg_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_storage_delete_run(const char* path, size_t command_id, PB_CommandStatus status) {
|
static void test_storage_delete_run(
|
||||||
|
const char* path,
|
||||||
|
size_t command_id,
|
||||||
|
PB_CommandStatus status,
|
||||||
|
bool recursive) {
|
||||||
PB_Main request;
|
PB_Main request;
|
||||||
MsgList_t expected_msg_list;
|
MsgList_t expected_msg_list;
|
||||||
MsgList_init(expected_msg_list);
|
MsgList_init(expected_msg_list);
|
||||||
|
|
||||||
test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id);
|
test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id);
|
||||||
|
request.content.storage_delete_request.recursive = recursive;
|
||||||
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
|
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
|
||||||
|
|
||||||
test_rpc_encode_and_feed_one(&request);
|
test_rpc_encode_and_feed_one(&request);
|
||||||
@ -844,16 +909,69 @@ static void test_storage_delete_run(const char* path, size_t command_id, PB_Comm
|
|||||||
test_rpc_free_msg_list(expected_msg_list);
|
test_rpc_free_msg_list(expected_msg_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(test_storage_delete) {
|
#define TEST_DIR_RMRF_NAME TEST_DIR "rmrf_test"
|
||||||
test_create_file(TEST_DIR "empty.txt", 0);
|
#define TEST_DIR_RMRF TEST_DIR_RMRF_NAME "/"
|
||||||
test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK);
|
MU_TEST(test_storage_delete_recursive) {
|
||||||
test_storage_delete_run(
|
test_create_dir(TEST_DIR_RMRF_NAME);
|
||||||
TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);
|
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir1");
|
||||||
|
test_create_file(TEST_DIR_RMRF "dir1/file1", 1);
|
||||||
|
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir1/dir1");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir1/dir2");
|
||||||
|
test_create_file(TEST_DIR_RMRF "dir1/dir2/file1", 1);
|
||||||
|
test_create_file(TEST_DIR_RMRF "dir1/dir2/file2", 1);
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir1/dir3");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1/dir1");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1/dir1/dir1");
|
||||||
|
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir2");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir2/dir1");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir2/dir2");
|
||||||
|
test_create_file(TEST_DIR_RMRF "dir2/dir2/file1", 1);
|
||||||
|
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1/dir1");
|
||||||
|
test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1/dir1/dir1");
|
||||||
|
test_create_file(TEST_DIR_RMRF "dir2/dir2/dir1/dir1/dir1/file1", 1);
|
||||||
|
|
||||||
test_create_dir(TEST_DIR "dir1");
|
|
||||||
test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK);
|
|
||||||
test_storage_delete_run(
|
test_storage_delete_run(
|
||||||
TEST_DIR "dir1", ++command_id, PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);
|
TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY, false);
|
||||||
|
mu_check(test_is_exists(TEST_DIR_RMRF_NAME));
|
||||||
|
test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true);
|
||||||
|
mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
|
||||||
|
test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, false);
|
||||||
|
mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
|
||||||
|
|
||||||
|
test_create_dir(TEST_DIR_RMRF_NAME);
|
||||||
|
test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true);
|
||||||
|
mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
|
||||||
|
|
||||||
|
test_create_dir(TEST_DIR "file1");
|
||||||
|
test_storage_delete_run(TEST_DIR "file1", ++command_id, PB_CommandStatus_OK, true);
|
||||||
|
mu_check(!test_is_exists(TEST_DIR "file1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(test_storage_delete) {
|
||||||
|
test_storage_delete_run(NULL, ++command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS, false);
|
||||||
|
|
||||||
|
furi_check(!test_is_exists(TEST_DIR "empty.txt"));
|
||||||
|
test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK, false);
|
||||||
|
mu_check(!test_is_exists(TEST_DIR "empty.txt"));
|
||||||
|
|
||||||
|
test_create_file(TEST_DIR "empty.txt", 0);
|
||||||
|
test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK, false);
|
||||||
|
mu_check(!test_is_exists(TEST_DIR "empty.txt"));
|
||||||
|
|
||||||
|
furi_check(!test_is_exists(TEST_DIR "dir1"));
|
||||||
|
test_create_dir(TEST_DIR "dir1");
|
||||||
|
test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK, false);
|
||||||
|
mu_check(!test_is_exists(TEST_DIR "dir1"));
|
||||||
|
|
||||||
|
test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK, false);
|
||||||
|
mu_check(!test_is_exists(TEST_DIR "dir1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_storage_mkdir_run(const char* path, size_t command_id, PB_CommandStatus status) {
|
static void test_storage_mkdir_run(const char* path, size_t command_id, PB_CommandStatus status) {
|
||||||
@ -872,18 +990,17 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma
|
|||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(test_storage_mkdir) {
|
MU_TEST(test_storage_mkdir) {
|
||||||
|
furi_check(!test_is_exists(TEST_DIR "dir1"));
|
||||||
test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK);
|
test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK);
|
||||||
|
mu_check(test_is_exists(TEST_DIR "dir1"));
|
||||||
|
|
||||||
test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);
|
test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);
|
||||||
|
mu_check(test_is_exists(TEST_DIR "dir1"));
|
||||||
|
|
||||||
|
furi_check(!test_is_exists(TEST_DIR "dir2"));
|
||||||
test_create_dir(TEST_DIR "dir2");
|
test_create_dir(TEST_DIR "dir2");
|
||||||
test_storage_mkdir_run(TEST_DIR "dir2", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);
|
test_storage_mkdir_run(TEST_DIR "dir2", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);
|
||||||
|
mu_check(test_is_exists(TEST_DIR "dir2"));
|
||||||
Storage* fs_api = furi_record_open("storage");
|
|
||||||
FS_Error error = storage_common_remove(fs_api, TEST_DIR "dir1");
|
|
||||||
(void)error;
|
|
||||||
furi_assert(error == FSE_OK);
|
|
||||||
furi_record_close("storage");
|
|
||||||
|
|
||||||
test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_storage_calculate_md5sum(const char* path, char* md5sum) {
|
static void test_storage_calculate_md5sum(const char* path, char* md5sum) {
|
||||||
@ -1013,7 +1130,7 @@ MU_TEST(test_ping) {
|
|||||||
// 4) test for fill buffer till end (great varint) and close connection
|
// 4) test for fill buffer till end (great varint) and close connection
|
||||||
|
|
||||||
MU_TEST_SUITE(test_rpc_status) {
|
MU_TEST_SUITE(test_rpc_status) {
|
||||||
MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown);
|
MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown);
|
||||||
|
|
||||||
MU_RUN_TEST(test_ping);
|
MU_RUN_TEST(test_ping);
|
||||||
}
|
}
|
||||||
@ -1026,6 +1143,7 @@ MU_TEST_SUITE(test_rpc_storage) {
|
|||||||
MU_RUN_TEST(test_storage_write_read);
|
MU_RUN_TEST(test_storage_write_read);
|
||||||
MU_RUN_TEST(test_storage_write);
|
MU_RUN_TEST(test_storage_write);
|
||||||
MU_RUN_TEST(test_storage_delete);
|
MU_RUN_TEST(test_storage_delete);
|
||||||
|
MU_RUN_TEST(test_storage_delete_recursive);
|
||||||
MU_RUN_TEST(test_storage_mkdir);
|
MU_RUN_TEST(test_storage_mkdir);
|
||||||
MU_RUN_TEST(test_storage_md5sum);
|
MU_RUN_TEST(test_storage_md5sum);
|
||||||
MU_RUN_TEST(test_storage_interrupt_continuous_same_system);
|
MU_RUN_TEST(test_storage_interrupt_continuous_same_system);
|
||||||
@ -1112,20 +1230,19 @@ MU_TEST(test_app_start_and_lock_status) {
|
|||||||
"skynet_destroy_world_app", NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
|
"skynet_destroy_world_app", NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
|
||||||
test_app_get_status_lock_run(false, ++command_id);
|
test_app_get_status_lock_run(false, ++command_id);
|
||||||
|
|
||||||
test_app_start_run("Delay Test App", "0", PB_CommandStatus_OK, ++command_id);
|
test_app_start_run("Delay Test", "0", PB_CommandStatus_OK, ++command_id);
|
||||||
delay(100);
|
delay(100);
|
||||||
test_app_get_status_lock_run(false, ++command_id);
|
test_app_get_status_lock_run(false, ++command_id);
|
||||||
|
|
||||||
test_app_start_run("Delay Test App", "200", PB_CommandStatus_OK, ++command_id);
|
test_app_start_run("Delay Test", "200", PB_CommandStatus_OK, ++command_id);
|
||||||
test_app_get_status_lock_run(true, ++command_id);
|
test_app_get_status_lock_run(true, ++command_id);
|
||||||
delay(100);
|
delay(100);
|
||||||
test_app_get_status_lock_run(true, ++command_id);
|
test_app_get_status_lock_run(true, ++command_id);
|
||||||
test_app_start_run(
|
test_app_start_run("Delay Test", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);
|
||||||
"Delay Test App", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);
|
|
||||||
delay(200);
|
delay(200);
|
||||||
test_app_get_status_lock_run(false, ++command_id);
|
test_app_get_status_lock_run(false, ++command_id);
|
||||||
|
|
||||||
test_app_start_run("Delay Test App", "500", PB_CommandStatus_OK, ++command_id);
|
test_app_start_run("Delay Test", "500", PB_CommandStatus_OK, ++command_id);
|
||||||
delay(100);
|
delay(100);
|
||||||
test_app_get_status_lock_run(true, ++command_id);
|
test_app_get_status_lock_run(true, ++command_id);
|
||||||
test_app_start_run("Infrared", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);
|
test_app_start_run("Infrared", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);
|
||||||
@ -1140,16 +1257,22 @@ MU_TEST(test_app_start_and_lock_status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST_SUITE(test_rpc_app) {
|
MU_TEST_SUITE(test_rpc_app) {
|
||||||
MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown);
|
MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown);
|
||||||
|
|
||||||
MU_RUN_TEST(test_app_start_and_lock_status);
|
MU_RUN_TEST(test_app_start_and_lock_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
int run_minunit_test_rpc() {
|
int run_minunit_test_rpc() {
|
||||||
MU_RUN_SUITE(test_rpc_storage);
|
Storage* storage = furi_record_open("storage");
|
||||||
|
furi_record_close("storage");
|
||||||
|
if(storage_sd_status(storage) != FSE_OK) {
|
||||||
|
FURI_LOG_E("UNIT_TESTS", "SD card not mounted - skip storage tests");
|
||||||
|
} else {
|
||||||
|
MU_RUN_SUITE(test_rpc_storage);
|
||||||
|
}
|
||||||
|
|
||||||
MU_RUN_SUITE(test_rpc_status);
|
MU_RUN_SUITE(test_rpc_status);
|
||||||
MU_RUN_SUITE(test_rpc_app);
|
MU_RUN_SUITE(test_rpc_app);
|
||||||
MU_REPORT();
|
|
||||||
|
|
||||||
return MU_EXIT_CODE;
|
return MU_EXIT_CODE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,27 @@
|
|||||||
#include <cli/cli.h>
|
#include <cli/cli.h>
|
||||||
#include <loader/loader.h>
|
#include <loader/loader.h>
|
||||||
|
|
||||||
|
#define TESTS_TAG "UNIT_TESTS"
|
||||||
|
|
||||||
int run_minunit();
|
int run_minunit();
|
||||||
int run_minunit_test_irda_decoder_encoder();
|
int run_minunit_test_irda_decoder_encoder();
|
||||||
int run_minunit_test_rpc();
|
int run_minunit_test_rpc();
|
||||||
|
|
||||||
|
void minunit_print_progress(void) {
|
||||||
|
static char progress[] = {'\\', '|', '/', '-'};
|
||||||
|
static uint8_t progress_counter = 0;
|
||||||
|
static TickType_t last_tick = 0;
|
||||||
|
TickType_t current_tick = xTaskGetTickCount();
|
||||||
|
if(current_tick - last_tick > 20) {
|
||||||
|
last_tick = current_tick;
|
||||||
|
printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void minunit_print_fail(const char* str) {
|
||||||
|
printf("%s\n", str);
|
||||||
|
}
|
||||||
|
|
||||||
void unit_tests_cli(Cli* cli, string_t args, void* context) {
|
void unit_tests_cli(Cli* cli, string_t args, void* context) {
|
||||||
uint32_t test_result = 0;
|
uint32_t test_result = 0;
|
||||||
minunit_run = 0;
|
minunit_run = 0;
|
||||||
@ -25,21 +42,30 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) {
|
|||||||
furi_record_close("notification");
|
furi_record_close("notification");
|
||||||
|
|
||||||
if(loader_is_locked(loader)) {
|
if(loader_is_locked(loader)) {
|
||||||
FURI_LOG_E("UNIT_TESTS", "RPC: stop all applications to run tests");
|
FURI_LOG_E(TESTS_TAG, "RPC: stop all applications to run tests");
|
||||||
notification_message(notification, &sequence_blink_magenta_100);
|
notification_message(notification, &sequence_blink_magenta_100);
|
||||||
} else {
|
} else {
|
||||||
notification_message_block(notification, &sequence_set_only_blue_255);
|
notification_message_block(notification, &sequence_set_only_blue_255);
|
||||||
|
|
||||||
|
uint32_t heap_before = memmgr_get_free_heap();
|
||||||
|
|
||||||
test_result |= run_minunit();
|
test_result |= run_minunit();
|
||||||
test_result |= run_minunit_test_irda_decoder_encoder();
|
test_result |= run_minunit_test_irda_decoder_encoder();
|
||||||
test_result |= run_minunit_test_rpc();
|
test_result |= run_minunit_test_rpc();
|
||||||
|
|
||||||
if(test_result == 0) {
|
if(test_result == 0) {
|
||||||
|
delay(200); /* wait for tested services and apps to deallocate */
|
||||||
|
uint32_t heap_after = memmgr_get_free_heap();
|
||||||
notification_message(notification, &sequence_success);
|
notification_message(notification, &sequence_success);
|
||||||
FURI_LOG_I("UNIT_TESTS", "PASSED");
|
if(heap_after != heap_before) {
|
||||||
|
FURI_LOG_E(TESTS_TAG, "Leaked: %d", heap_before - heap_after);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_I(TESTS_TAG, "No leaks");
|
||||||
|
}
|
||||||
|
FURI_LOG_I(TESTS_TAG, "PASSED");
|
||||||
} else {
|
} else {
|
||||||
notification_message(notification, &sequence_error);
|
notification_message(notification, &sequence_error);
|
||||||
FURI_LOG_E("UNIT_TESTS", "FAILED");
|
FURI_LOG_E(TESTS_TAG, "FAILED");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,9 @@
|
|||||||
PB_BIND(PB_Empty, PB_Empty, AUTO)
|
PB_BIND(PB_Empty, PB_Empty, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(PB_StopSession, PB_StopSession, AUTO)
|
||||||
|
|
||||||
|
|
||||||
PB_BIND(PB_Main, PB_Main, AUTO)
|
PB_BIND(PB_Main, PB_Main, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,8 @@ typedef enum _PB_CommandStatus {
|
|||||||
PB_CommandStatus_ERROR_STORAGE_INTERNAL = 11, /* *< Internal error */
|
PB_CommandStatus_ERROR_STORAGE_INTERNAL = 11, /* *< Internal error */
|
||||||
PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED = 12, /* *< Functon not implemented */
|
PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED = 12, /* *< Functon not implemented */
|
||||||
PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN = 13, /* *< File/Dir already opened */
|
PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN = 13, /* *< File/Dir already opened */
|
||||||
PB_CommandStatus_ERROR_APP_CANT_START = 16, /* *< Can't start app - or internal error */
|
PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY = 18, /* *< Directory, you're going to remove is not empty */
|
||||||
|
PB_CommandStatus_ERROR_APP_CANT_START = 16, /* *< Can't start app - internal error */
|
||||||
PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED = 17 /* *< Another app is running */
|
PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED = 17 /* *< Another app is running */
|
||||||
} PB_CommandStatus;
|
} PB_CommandStatus;
|
||||||
|
|
||||||
@ -42,6 +43,10 @@ typedef struct _PB_Empty {
|
|||||||
char dummy_field;
|
char dummy_field;
|
||||||
} PB_Empty;
|
} PB_Empty;
|
||||||
|
|
||||||
|
typedef struct _PB_StopSession {
|
||||||
|
char dummy_field;
|
||||||
|
} PB_StopSession;
|
||||||
|
|
||||||
typedef struct _PB_Main {
|
typedef struct _PB_Main {
|
||||||
uint32_t command_id;
|
uint32_t command_id;
|
||||||
PB_CommandStatus command_status;
|
PB_CommandStatus command_status;
|
||||||
@ -64,14 +69,15 @@ typedef struct _PB_Main {
|
|||||||
PB_App_Start app_start;
|
PB_App_Start app_start;
|
||||||
PB_App_LockStatusRequest app_lock_status_request;
|
PB_App_LockStatusRequest app_lock_status_request;
|
||||||
PB_App_LockStatusResponse app_lock_status_response;
|
PB_App_LockStatusResponse app_lock_status_response;
|
||||||
|
PB_StopSession stop_session;
|
||||||
} content;
|
} content;
|
||||||
} PB_Main;
|
} PB_Main;
|
||||||
|
|
||||||
|
|
||||||
/* Helper constants for enums */
|
/* Helper constants for enums */
|
||||||
#define _PB_CommandStatus_MIN PB_CommandStatus_OK
|
#define _PB_CommandStatus_MIN PB_CommandStatus_OK
|
||||||
#define _PB_CommandStatus_MAX PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED
|
#define _PB_CommandStatus_MAX PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY
|
||||||
#define _PB_CommandStatus_ARRAYSIZE ((PB_CommandStatus)(PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED+1))
|
#define _PB_CommandStatus_ARRAYSIZE ((PB_CommandStatus)(PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY+1))
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -80,8 +86,10 @@ extern "C" {
|
|||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define PB_Empty_init_default {0}
|
#define PB_Empty_init_default {0}
|
||||||
|
#define PB_StopSession_init_default {0}
|
||||||
#define PB_Main_init_default {0, _PB_CommandStatus_MIN, 0, {{NULL}, NULL}, 0, {PB_Empty_init_default}}
|
#define PB_Main_init_default {0, _PB_CommandStatus_MIN, 0, {{NULL}, NULL}, 0, {PB_Empty_init_default}}
|
||||||
#define PB_Empty_init_zero {0}
|
#define PB_Empty_init_zero {0}
|
||||||
|
#define PB_StopSession_init_zero {0}
|
||||||
#define PB_Main_init_zero {0, _PB_CommandStatus_MIN, 0, {{NULL}, NULL}, 0, {PB_Empty_init_zero}}
|
#define PB_Main_init_zero {0, _PB_CommandStatus_MIN, 0, {{NULL}, NULL}, 0, {PB_Empty_init_zero}}
|
||||||
|
|
||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
@ -103,6 +111,7 @@ extern "C" {
|
|||||||
#define PB_Main_app_start_tag 16
|
#define PB_Main_app_start_tag 16
|
||||||
#define PB_Main_app_lock_status_request_tag 17
|
#define PB_Main_app_lock_status_request_tag 17
|
||||||
#define PB_Main_app_lock_status_response_tag 18
|
#define PB_Main_app_lock_status_response_tag 18
|
||||||
|
#define PB_Main_stop_session_tag 19
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
#define PB_Empty_FIELDLIST(X, a) \
|
#define PB_Empty_FIELDLIST(X, a) \
|
||||||
@ -110,6 +119,11 @@ extern "C" {
|
|||||||
#define PB_Empty_CALLBACK NULL
|
#define PB_Empty_CALLBACK NULL
|
||||||
#define PB_Empty_DEFAULT NULL
|
#define PB_Empty_DEFAULT NULL
|
||||||
|
|
||||||
|
#define PB_StopSession_FIELDLIST(X, a) \
|
||||||
|
|
||||||
|
#define PB_StopSession_CALLBACK NULL
|
||||||
|
#define PB_StopSession_DEFAULT NULL
|
||||||
|
|
||||||
#define PB_Main_FIELDLIST(X, a) \
|
#define PB_Main_FIELDLIST(X, a) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, command_id, 1) \
|
X(a, STATIC, SINGULAR, UINT32, command_id, 1) \
|
||||||
X(a, STATIC, SINGULAR, UENUM, command_status, 2) \
|
X(a, STATIC, SINGULAR, UENUM, command_status, 2) \
|
||||||
@ -128,7 +142,8 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_request,content.stora
|
|||||||
X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_response,content.storage_md5sum_response), 15) \
|
X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_response,content.storage_md5sum_response), 15) \
|
||||||
X(a, STATIC, ONEOF, MSG_W_CB, (content,app_start,content.app_start), 16) \
|
X(a, STATIC, ONEOF, MSG_W_CB, (content,app_start,content.app_start), 16) \
|
||||||
X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_request,content.app_lock_status_request), 17) \
|
X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_request,content.app_lock_status_request), 17) \
|
||||||
X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_response,content.app_lock_status_response), 18)
|
X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_response,content.app_lock_status_response), 18) \
|
||||||
|
X(a, STATIC, ONEOF, MSG_W_CB, (content,stop_session,content.stop_session), 19)
|
||||||
#define PB_Main_CALLBACK NULL
|
#define PB_Main_CALLBACK NULL
|
||||||
#define PB_Main_DEFAULT NULL
|
#define PB_Main_DEFAULT NULL
|
||||||
#define PB_Main_content_empty_MSGTYPE PB_Empty
|
#define PB_Main_content_empty_MSGTYPE PB_Empty
|
||||||
@ -146,16 +161,20 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_response,content.app
|
|||||||
#define PB_Main_content_app_start_MSGTYPE PB_App_Start
|
#define PB_Main_content_app_start_MSGTYPE PB_App_Start
|
||||||
#define PB_Main_content_app_lock_status_request_MSGTYPE PB_App_LockStatusRequest
|
#define PB_Main_content_app_lock_status_request_MSGTYPE PB_App_LockStatusRequest
|
||||||
#define PB_Main_content_app_lock_status_response_MSGTYPE PB_App_LockStatusResponse
|
#define PB_Main_content_app_lock_status_response_MSGTYPE PB_App_LockStatusResponse
|
||||||
|
#define PB_Main_content_stop_session_MSGTYPE PB_StopSession
|
||||||
|
|
||||||
extern const pb_msgdesc_t PB_Empty_msg;
|
extern const pb_msgdesc_t PB_Empty_msg;
|
||||||
|
extern const pb_msgdesc_t PB_StopSession_msg;
|
||||||
extern const pb_msgdesc_t PB_Main_msg;
|
extern const pb_msgdesc_t PB_Main_msg;
|
||||||
|
|
||||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
#define PB_Empty_fields &PB_Empty_msg
|
#define PB_Empty_fields &PB_Empty_msg
|
||||||
|
#define PB_StopSession_fields &PB_StopSession_msg
|
||||||
#define PB_Main_fields &PB_Main_msg
|
#define PB_Main_fields &PB_Main_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define PB_Empty_size 0
|
#define PB_Empty_size 0
|
||||||
|
#define PB_StopSession_size 0
|
||||||
#if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_Start_size)
|
#if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_Start_size)
|
||||||
#define PB_Main_size (10 + sizeof(union PB_Main_content_size_union))
|
#define PB_Main_size (10 + sizeof(union PB_Main_content_size_union))
|
||||||
union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_Start_size)]; char f0[36];};
|
union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_Start_size)]; char f0[36];};
|
||||||
|
|||||||
@ -16,10 +16,6 @@ typedef enum _PB_Storage_File_FileType {
|
|||||||
} PB_Storage_File_FileType;
|
} PB_Storage_File_FileType;
|
||||||
|
|
||||||
/* Struct definitions */
|
/* Struct definitions */
|
||||||
typedef struct _PB_Storage_DeleteRequest {
|
|
||||||
char *path;
|
|
||||||
} PB_Storage_DeleteRequest;
|
|
||||||
|
|
||||||
typedef struct _PB_Storage_ListRequest {
|
typedef struct _PB_Storage_ListRequest {
|
||||||
char *path;
|
char *path;
|
||||||
} PB_Storage_ListRequest;
|
} PB_Storage_ListRequest;
|
||||||
@ -36,6 +32,11 @@ typedef struct _PB_Storage_ReadRequest {
|
|||||||
char *path;
|
char *path;
|
||||||
} PB_Storage_ReadRequest;
|
} PB_Storage_ReadRequest;
|
||||||
|
|
||||||
|
typedef struct _PB_Storage_DeleteRequest {
|
||||||
|
char *path;
|
||||||
|
bool recursive;
|
||||||
|
} PB_Storage_DeleteRequest;
|
||||||
|
|
||||||
typedef struct _PB_Storage_File {
|
typedef struct _PB_Storage_File {
|
||||||
PB_Storage_File_FileType type;
|
PB_Storage_File_FileType type;
|
||||||
char *name;
|
char *name;
|
||||||
@ -81,7 +82,7 @@ extern "C" {
|
|||||||
#define PB_Storage_ReadRequest_init_default {NULL}
|
#define PB_Storage_ReadRequest_init_default {NULL}
|
||||||
#define PB_Storage_ReadResponse_init_default {false, PB_Storage_File_init_default}
|
#define PB_Storage_ReadResponse_init_default {false, PB_Storage_File_init_default}
|
||||||
#define PB_Storage_WriteRequest_init_default {NULL, false, PB_Storage_File_init_default}
|
#define PB_Storage_WriteRequest_init_default {NULL, false, PB_Storage_File_init_default}
|
||||||
#define PB_Storage_DeleteRequest_init_default {NULL}
|
#define PB_Storage_DeleteRequest_init_default {NULL, 0}
|
||||||
#define PB_Storage_MkdirRequest_init_default {NULL}
|
#define PB_Storage_MkdirRequest_init_default {NULL}
|
||||||
#define PB_Storage_Md5sumRequest_init_default {NULL}
|
#define PB_Storage_Md5sumRequest_init_default {NULL}
|
||||||
#define PB_Storage_Md5sumResponse_init_default {""}
|
#define PB_Storage_Md5sumResponse_init_default {""}
|
||||||
@ -91,17 +92,18 @@ extern "C" {
|
|||||||
#define PB_Storage_ReadRequest_init_zero {NULL}
|
#define PB_Storage_ReadRequest_init_zero {NULL}
|
||||||
#define PB_Storage_ReadResponse_init_zero {false, PB_Storage_File_init_zero}
|
#define PB_Storage_ReadResponse_init_zero {false, PB_Storage_File_init_zero}
|
||||||
#define PB_Storage_WriteRequest_init_zero {NULL, false, PB_Storage_File_init_zero}
|
#define PB_Storage_WriteRequest_init_zero {NULL, false, PB_Storage_File_init_zero}
|
||||||
#define PB_Storage_DeleteRequest_init_zero {NULL}
|
#define PB_Storage_DeleteRequest_init_zero {NULL, 0}
|
||||||
#define PB_Storage_MkdirRequest_init_zero {NULL}
|
#define PB_Storage_MkdirRequest_init_zero {NULL}
|
||||||
#define PB_Storage_Md5sumRequest_init_zero {NULL}
|
#define PB_Storage_Md5sumRequest_init_zero {NULL}
|
||||||
#define PB_Storage_Md5sumResponse_init_zero {""}
|
#define PB_Storage_Md5sumResponse_init_zero {""}
|
||||||
|
|
||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define PB_Storage_DeleteRequest_path_tag 1
|
|
||||||
#define PB_Storage_ListRequest_path_tag 1
|
#define PB_Storage_ListRequest_path_tag 1
|
||||||
#define PB_Storage_Md5sumRequest_path_tag 1
|
#define PB_Storage_Md5sumRequest_path_tag 1
|
||||||
#define PB_Storage_MkdirRequest_path_tag 1
|
#define PB_Storage_MkdirRequest_path_tag 1
|
||||||
#define PB_Storage_ReadRequest_path_tag 1
|
#define PB_Storage_ReadRequest_path_tag 1
|
||||||
|
#define PB_Storage_DeleteRequest_path_tag 1
|
||||||
|
#define PB_Storage_DeleteRequest_recursive_tag 2
|
||||||
#define PB_Storage_File_type_tag 1
|
#define PB_Storage_File_type_tag 1
|
||||||
#define PB_Storage_File_name_tag 2
|
#define PB_Storage_File_name_tag 2
|
||||||
#define PB_Storage_File_size_tag 3
|
#define PB_Storage_File_size_tag 3
|
||||||
@ -151,7 +153,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, file, 2)
|
|||||||
#define PB_Storage_WriteRequest_file_MSGTYPE PB_Storage_File
|
#define PB_Storage_WriteRequest_file_MSGTYPE PB_Storage_File
|
||||||
|
|
||||||
#define PB_Storage_DeleteRequest_FIELDLIST(X, a) \
|
#define PB_Storage_DeleteRequest_FIELDLIST(X, a) \
|
||||||
X(a, POINTER, SINGULAR, STRING, path, 1)
|
X(a, POINTER, SINGULAR, STRING, path, 1) \
|
||||||
|
X(a, STATIC, SINGULAR, BOOL, recursive, 2)
|
||||||
#define PB_Storage_DeleteRequest_CALLBACK NULL
|
#define PB_Storage_DeleteRequest_CALLBACK NULL
|
||||||
#define PB_Storage_DeleteRequest_DEFAULT NULL
|
#define PB_Storage_DeleteRequest_DEFAULT NULL
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit 8e6db414beed5aff0902f2cca2f4146a0dffb7a1
|
Subproject commit 021ba48abb64d25c7094da13b752fe37d4bf6007
|
||||||
@ -272,7 +272,7 @@ const struct Version* furi_hal_version_get_firmware_version(void) {
|
|||||||
return version_get();
|
return version_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct Version* furi_hal_version_get_boot_version(void) {
|
const struct Version* furi_hal_version_get_bootloader_version(void) {
|
||||||
#ifdef NO_BOOTLOADER
|
#ifdef NO_BOOTLOADER
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -272,7 +272,7 @@ const struct Version* furi_hal_version_get_firmware_version(void) {
|
|||||||
return version_get();
|
return version_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct Version* furi_hal_version_get_boot_version(void) {
|
const struct Version* furi_hal_version_get_bootloader_version(void) {
|
||||||
#ifdef NO_BOOTLOADER
|
#ifdef NO_BOOTLOADER
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -148,7 +148,7 @@ const uint8_t* furi_hal_version_get_ble_mac();
|
|||||||
*
|
*
|
||||||
* @return Address of boot version structure.
|
* @return Address of boot version structure.
|
||||||
*/
|
*/
|
||||||
const struct Version* furi_hal_version_get_boot_version();
|
const struct Version* furi_hal_version_get_bootloader_version();
|
||||||
|
|
||||||
/** Get address of version structure of firmware.
|
/** Get address of version structure of firmware.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -25,7 +25,7 @@ static void flipper_print_version(const char* target, const Version* version) {
|
|||||||
void flipper_init() {
|
void flipper_init() {
|
||||||
const Version* version;
|
const Version* version;
|
||||||
|
|
||||||
version = (const Version*)furi_hal_version_get_boot_version();
|
version = (const Version*)furi_hal_version_get_bootloader_version();
|
||||||
flipper_print_version("Bootloader", version);
|
flipper_print_version("Bootloader", version);
|
||||||
|
|
||||||
version = (const Version*)furi_hal_version_get_firmware_version();
|
version = (const Version*)furi_hal_version_get_firmware_version();
|
||||||
|
|||||||
@ -85,6 +85,7 @@ bool furi_record_destroy(const char* name) {
|
|||||||
furi_assert(record_data);
|
furi_assert(record_data);
|
||||||
if(record_data->holders_count == 0) {
|
if(record_data->holders_count == 0) {
|
||||||
FuriRecordDataDict_erase(furi_record->records, name_str);
|
FuriRecordDataDict_erase(furi_record->records, name_str);
|
||||||
|
furi_check(osOK == osEventFlagsDelete(record_data->flags));
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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