diff --git a/.drone.yml b/.drone.yml index 9f8991c665..9b4765a8fb 100644 --- a/.drone.yml +++ b/.drone.yml @@ -40,9 +40,9 @@ steps: image: hfdj/fztools pull: never commands: - - git clone https://github.com/xMasterX/unleashed-extra-pack.git - - cp -R unleashed-extra-pack/apps/* assets/resources/apps/ - - rm -rf unleashed-extra-pack + - git clone https://github.com/xMasterX/all-the-plugins.git + - cp -R all-the-plugins/apps/* assets/resources/apps/ + - rm -rf all-the-plugins - export DIST_SUFFIX=${DRONE_TAG}e - export WORKFLOW_BRANCH_OR_TAG=release-cfw - export FORCE_NO_DIRTY=yes @@ -201,7 +201,7 @@ steps: [-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md) - [-Download latest extra apps pack-](https://github.com/xMasterX/unleashed-extra-pack/archive/refs/heads/main.zip) + [-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) [-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=release-cfw&version=${DRONE_TAG}) @@ -223,7 +223,7 @@ steps: commands: - wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - chmod +x ./discord.sh - - ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/unleashed-extra-pack/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' + - ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' - name: "Send extra pack build to telegram" image: appleboy/drone-telegram @@ -281,9 +281,9 @@ steps: image: hfdj/fztools pull: never commands: - - git clone https://github.com/xMasterX/unleashed-extra-pack.git - - cp -R unleashed-extra-pack/apps/* assets/resources/apps/ - - rm -rf unleashed-extra-pack + - git clone --branch dev https://github.com/xMasterX/all-the-plugins.git + - cp -R all-the-plugins/apps/* assets/resources/apps/ + - rm -rf all-the-plugins - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e - export WORKFLOW_BRANCH_OR_TAG=dev-cfw - export FORCE_NO_DIRTY=yes diff --git a/.gitmodules b/.gitmodules index 5f97063b36..e6f3e7be1a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,4 +33,7 @@ url = https://github.com/ataradov/free-dap.git [submodule "applications/external/subbrute"] path = applications/external/subbrute - url = https://github.com/derskythe/flipperzero-subbrute.git \ No newline at end of file + url = https://github.com/derskythe/flipperzero-subbrute.git +[submodule "lib/heatshrink"] + path = lib/heatshrink + url = https://github.com/flipperdevices/heatshrink.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 564b5a3ed0..20dd09d503 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,20 @@ ### New changes -* Plugins: Fix minesweeper freeze bugs, do some refactoring -* Plugins: Update WAV Player, 16 bit support by @LTVA1 -* Infrared: Update universal remote assets (by @amec0e | PR #394) -* OFW PR: Fixed writing gen1a magic tags with invalid BCC [(PR 2511 by AloneLiberty)](https://github.com/AloneLiberty/flipperzero-firmware/tree/nfc-magic-write-fix) -* OFW: BadUSB: Script interpreter refactoring -* OFW: SubGhz: fix Incorrect comparison in subghz_setting_get_hopper_frequency -* OFW: Add one_wire lib to f18, separate ibutton -* OFW: Improved debugging experience for external apps -* OFW: SD Driver: reinit sd card on error -* OFW: OTP programmer: return exit code based on error type - -#### [🎲 Download latest extra apps pack](https://github.com/xMasterX/unleashed-extra-pack/archive/refs/heads/main.zip) +* If you have copied apps into `apps` folder - remove `apps` folder on your microSD before installing this release to avoid issues! +* Dev Builds: Add extra pack dev branch to avoid "bug" reports with `API mismatch` +* App Loader: Add option to ignore api mismatch (warning! some apps WILL not work, please update them to avoid any issues) -> (by @Willy-JL | PR #395) +* SubGHz: Add manually -> GSN protocol support +* SubGHz: Add 318 and 418 MHz back to hopping list +* SubGHz: Fix hopper stuck at 433.42 due to wide range signals - +When we using 433.92 remote flipper in hopping mode will stuck at 433.42 and may loose signal because of that, need to avoid using close freqs in hopping, only freqs with bigger difference like 310 -> 315 +* Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) -> BadBT Support +* OFW: Screen streaming improvements +* OFW: 1-Wire Overdrive Mode -> **Breaking API change, api was changed from 19.x to 20.x** +* OFW: Disable UART IRQs by default +* OFW: BadUSB: implement boot protocol +* OFW: Remove hmac_sha256 from public API -> **Breaking API change, api was changed from 18.x to 19.x** +**(this will make your manually copied plugins not work, update them in same way you installed them, or delete `apps` folder and then install firmware, if you using extra pack builds (with `e` in version) all apps in _Extra will be updated automatically)** + +#### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) [-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md) diff --git a/ReadMe.md b/ReadMe.md index 68c78239af..edaa9b1524 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -195,7 +195,7 @@ Games: ### **Plugins** -## [- 🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/unleashed-extra-pack) +## [- 🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/all-the-plugins) ## [- TOTP (Authenticator) config description](https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md) diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c index b3bc7cd990..4241cb59d9 100644 --- a/applications/examples/example_thermo/example_thermo.c +++ b/applications/examples/example_thermo/example_thermo.c @@ -19,9 +19,12 @@ #include #include +#include + #define UPDATE_PERIOD_MS 1000UL #define TEXT_STORE_SIZE 64U +#define DS18B20_CMD_SKIP_ROM 0xccU #define DS18B20_CMD_CONVERT 0x44U #define DS18B20_CMD_READ_SCRATCHPAD 0xbeU @@ -92,7 +95,7 @@ static void example_thermo_request_temperature(ExampleThermoContext* context) { /* After the reset, a ROM operation must follow. If there is only one device connected, the "Skip ROM" command is most appropriate (it can also be used to address all of the connected devices in some cases).*/ - onewire_host_skip(onewire); + onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM); /* After the ROM operation, a device-specific command is issued. In this case, it's a request to start measuring the temperature. */ onewire_host_write(onewire, DS18B20_CMD_CONVERT); @@ -133,7 +136,7 @@ static void example_thermo_read_temperature(ExampleThermoContext* context) { /* After the reset, a ROM operation must follow. If there is only one device connected, the "Skip ROM" command is most appropriate (it can also be used to address all of the connected devices in some cases).*/ - onewire_host_skip(onewire); + onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM); /* After the ROM operation, a device-specific command is issued. This time, it will be the "Read Scratchpad" command which will @@ -267,6 +270,9 @@ static void example_thermo_input_callback(InputEvent* event, void* ctx) { /* Starts the reader thread and handles the input */ static void example_thermo_run(ExampleThermoContext* context) { + /* Enable power on external pins */ + furi_hal_power_enable_otg(); + /* Configure the hardware in host mode */ onewire_host_start(context->onewire); @@ -299,6 +305,9 @@ static void example_thermo_run(ExampleThermoContext* context) { /* Reset the hardware */ onewire_host_stop(context->onewire); + + /* Disable power on external pins */ + furi_hal_power_disable_otg(); } /******************** Initialisation & startup *****************************/ diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 600ed6ca3f..3c7ba149ed 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -9,10 +9,14 @@ App( "dialogs", "storage", "input", - "notification" + "notification", + "bt" ], stack_size=2 * 1024, order=20, + fap_author="Alexander Kopachov (@akopachov)", + fap_description="Software-based TOTP authenticator for Flipper Zero device", + fap_weburl="https://github.com/akopachov/flipper-zero_authenticator", fap_category="Misc", fap_icon_assets="images", fap_icon="totp_10px.png", diff --git a/applications/external/totp/cli/cli.c b/applications/external/totp/cli/cli.c index 28d1737662..ce25308042 100644 --- a/applications/external/totp/cli/cli.c +++ b/applications/external/totp/cli/cli.c @@ -12,6 +12,7 @@ #include "commands/pin/pin.h" #include "commands/notification/notification.h" #include "commands/reset/reset.h" +#include "commands/automation/automation.h" static void totp_cli_print_unknown_command(const FuriString* unknown_command) { TOTP_CLI_PRINTF_ERROR( @@ -57,6 +58,8 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { totp_cli_command_pin_handle(plugin_state, args, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_NOTIFICATION) == 0) { totp_cli_command_notification_handle(plugin_state, args, cli); + } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) { + totp_cli_command_automation_handle(plugin_state, args, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) { totp_cli_command_reset_handle(cli, cli_context->event_queue); } else { diff --git a/applications/external/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c index fbcd58530c..91f9256b29 100644 --- a/applications/external/totp/cli/commands/add/add.c +++ b/applications/external/totp/cli/commands/add/add.c @@ -62,7 +62,7 @@ void totp_cli_command_add_docopt_usage() { } void totp_cli_command_add_docopt_arguments() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD_ARG_NAME " Token name\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD_ARG_NAME " Token name\r\n"); } void totp_cli_command_add_docopt_options() { diff --git a/applications/external/totp/cli/commands/automation/automation.c b/applications/external/totp/cli/commands/automation/automation.c new file mode 100644 index 0000000000..1fd87f456b --- /dev/null +++ b/applications/external/totp/cli/commands/automation/automation.c @@ -0,0 +1,133 @@ +#include "automation.h" +#include +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" +#include "../../cli_helpers.h" + +#define TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD "automation" +#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE "none" +#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "usb" +#ifdef TOTP_BADBT_TYPE_ENABLED +#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "bt" +#endif + +void totp_cli_command_automation_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation method\r\n"); +} + +void totp_cli_command_automation_docopt_usage() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL( + DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n"); +} + +void totp_cli_command_automation_docopt_arguments() { + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD + " Automation method to be set. Must be one of [" TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE + ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB +#ifdef TOTP_BADBT_TYPE_ENABLED + ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT +#endif + "]\r\n"); +} + +static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) { +#ifdef TOTP_BADBT_TYPE_ENABLED + bool has_previous_method = false; +#endif + if(method & AutomationMethodBadUsb) { + TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "\""); +#ifdef TOTP_BADBT_TYPE_ENABLED + has_previous_method = true; +#endif + } + +#ifdef TOTP_BADBT_TYPE_ENABLED + if(method & AutomationMethodBadBt) { + if(has_previous_method) { + TOTP_CLI_PRINTF_COLORFUL(color, " and "); + } + + TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "\""); + } +#endif + + if(method == AutomationMethodNone) { + TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE "\""); + } +} + +void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + FuriString* temp_str = furi_string_alloc(); + bool new_method_provided = false; + AutomationMethod new_method = AutomationMethodNone; + bool args_valid = true; + while(args_read_string_and_trim(args, temp_str)) { + if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE) == 0) { + new_method_provided = true; + new_method = AutomationMethodNone; + } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB) == 0) { + new_method_provided = true; + new_method |= AutomationMethodBadUsb; + } +#ifdef TOTP_BADBT_TYPE_ENABLED + else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT) == 0) { + new_method_provided = true; + new_method |= AutomationMethodBadBt; + } +#endif + else { + args_valid = false; + break; + } + } + + do { + if(!args_valid) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + break; + } + + if(new_method_provided) { + Scene previous_scene = TotpSceneNone; + if(plugin_state->current_scene == TotpSceneGenerateToken || + plugin_state->current_scene == TotpSceneAppSettings) { + previous_scene = plugin_state->current_scene; + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + } + + plugin_state->automation_method = new_method; + if(totp_config_file_update_automation_method(new_method) == + TotpConfigFileUpdateSuccess) { + TOTP_CLI_PRINTF_SUCCESS("Automation method is set to "); + totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); + cli_nl(); + } else { + TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + } + +#ifdef TOTP_BADBT_TYPE_ENABLED + if(!(new_method & AutomationMethodBadBt) && + plugin_state->bt_type_code_worker_context != NULL) { + totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); + plugin_state->bt_type_code_worker_context = NULL; + } +#endif + + if(previous_scene != TotpSceneNone) { + totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); + } + } else { + TOTP_CLI_PRINTF_INFO("Current automation method is "); + totp_cli_command_automation_print_method( + plugin_state->automation_method, TOTP_CLI_COLOR_INFO); + cli_nl(); + } + } while(false); + + furi_string_free(temp_str); +} \ No newline at end of file diff --git a/applications/external/totp/cli/commands/automation/automation.h b/applications/external/totp/cli/commands/automation/automation.h new file mode 100644 index 0000000000..fb62e638ef --- /dev/null +++ b/applications/external/totp/cli/commands/automation/automation.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_AUTOMATION "automation" + +void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_automation_docopt_commands(); +void totp_cli_command_automation_docopt_usage(); +void totp_cli_command_automation_docopt_arguments(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c index 8fe72e2209..04cc815a44 100644 --- a/applications/external/totp/cli/commands/delete/delete.c +++ b/applications/external/totp/cli/commands/delete/delete.c @@ -24,7 +24,7 @@ void totp_cli_command_delete_docopt_usage() { } void totp_cli_command_delete_docopt_arguments() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE_ARG_INDEX " Token index in the list\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE_ARG_INDEX " Token index in the list\r\n"); } void totp_cli_command_delete_docopt_options() { diff --git a/applications/external/totp/cli/commands/help/help.c b/applications/external/totp/cli/commands/help/help.c index 104b39e473..34b44debd2 100644 --- a/applications/external/totp/cli/commands/help/help.c +++ b/applications/external/totp/cli/commands/help/help.c @@ -8,6 +8,7 @@ #include "../pin/pin.h" #include "../notification/notification.h" #include "../reset/reset.h" +#include "../automation/automation.h" void totp_cli_command_help_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT @@ -31,6 +32,7 @@ void totp_cli_command_help_handle() { totp_cli_command_pin_docopt_usage(); totp_cli_command_notification_docopt_usage(); totp_cli_command_reset_docopt_usage(); + totp_cli_command_automation_docopt_usage(); cli_nl(); TOTP_CLI_PRINTF("Commands:\r\n"); totp_cli_command_help_docopt_commands(); @@ -42,12 +44,14 @@ void totp_cli_command_help_handle() { totp_cli_command_pin_docopt_commands(); totp_cli_command_notification_docopt_commands(); totp_cli_command_reset_docopt_commands(); + totp_cli_command_automation_docopt_commands(); cli_nl(); TOTP_CLI_PRINTF("Arguments:\r\n"); totp_cli_command_add_docopt_arguments(); totp_cli_command_delete_docopt_arguments(); totp_cli_command_timezone_docopt_arguments(); totp_cli_command_notification_docopt_arguments(); + totp_cli_command_automation_docopt_arguments(); cli_nl(); TOTP_CLI_PRINTF("Options:\r\n"); totp_cli_command_add_docopt_options(); diff --git a/applications/external/totp/cli/commands/notification/notification.c b/applications/external/totp/cli/commands/notification/notification.c index bbbd52703b..016b83d0d6 100644 --- a/applications/external/totp/cli/commands/notification/notification.c +++ b/applications/external/totp/cli/commands/notification/notification.c @@ -4,7 +4,7 @@ #include "../../../ui/scene_director.h" #include "../../cli_helpers.h" -#define TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD "method" +#define TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD "notification" #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE "none" #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "sound" #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "vibro" @@ -23,7 +23,7 @@ void totp_cli_command_notification_docopt_usage() { void totp_cli_command_notification_docopt_arguments() { TOTP_CLI_PRINTF( " " TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD - " Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE + " Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n"); } diff --git a/applications/external/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c index 9eb0cb5f64..265d80e533 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.c +++ b/applications/external/totp/cli/commands/timezone/timezone.c @@ -20,7 +20,7 @@ void totp_cli_command_timezone_docopt_usage() { void totp_cli_command_timezone_docopt_arguments() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE - " Timezone offset in hours to be set\r\n"); + " Timezone offset in hours to be set\r\n"); } void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h new file mode 100644 index 0000000000..d3b30aee01 --- /dev/null +++ b/applications/external/totp/features_config.h @@ -0,0 +1,2 @@ +#define TOTP_BADBT_TYPE_ENABLED +#define TOTP_BADBT_TYPE_ICON_ENABLED \ No newline at end of file diff --git a/applications/external/totp/images/hid_ble_10x7.png b/applications/external/totp/images/hid_ble_10x7.png new file mode 100644 index 0000000000..3cd1ff95c8 Binary files /dev/null and b/applications/external/totp/images/hid_ble_10x7.png differ diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index 034ed925e2..66c07ebfa8 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -4,6 +4,7 @@ #include "../list/list.h" #include "../../types/common.h" #include "../../types/token_info.h" +#include "../../features_config.h" #include "migrations/config_migration_v1_to_v2.h" #include "migrations/config_migration_v2_to_v3.h" @@ -136,6 +137,14 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); + tmp_uint32 = AutomationMethodBadUsb; + flipper_format_write_comment_cstr(fff_data_file, " "); + flipper_format_write_comment_cstr( + fff_data_file, + "Automation method (0 - None, 1 - BadUSB, 2 - BadBT, 3 - BadUSB and BadBT)"); + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1); + FuriString* temp_str = furi_string_alloc(); flipper_format_write_comment_cstr(fff_data_file, " "); @@ -329,6 +338,33 @@ TotpConfigFileUpdateResult return update_result; } +TotpConfigFileUpdateResult + totp_config_file_update_automation_method(AutomationMethod new_automation_method) { + Storage* cfg_storage = totp_open_storage(); + FlipperFormat* file; + TotpConfigFileUpdateResult update_result; + + if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { + do { + uint32_t tmp_uint32 = new_automation_method; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { + update_result = TotpConfigFileUpdateError; + break; + } + + update_result = TotpConfigFileUpdateSuccess; + } while(false); + + totp_close_config_file(file); + } else { + update_result = TotpConfigFileUpdateError; + } + + totp_close_storage(); + return update_result; +} + TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) { Storage* cfg_storage = totp_open_storage(); FlipperFormat* file; @@ -347,6 +383,13 @@ TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginSta break; } + tmp_uint32 = plugin_state->automation_method; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { + update_result = TotpConfigFileUpdateError; + break; + } + update_result = TotpConfigFileUpdateSuccess; } while(false); @@ -409,6 +452,13 @@ TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const p break; } + tmp_uint32 = plugin_state->automation_method; + if(!flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { + result = TotpConfigFileUpdateError; + break; + } + bool tokens_written = true; TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { const TokenInfo* token_info = node->data; @@ -594,6 +644,15 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } plugin_state->notification_method = tmp_uint32; + + flipper_format_rewind(fff_data_file); + + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { + tmp_uint32 = AutomationMethodBadUsb; + } + + plugin_state->automation_method = tmp_uint32; } while(false); furi_string_free(temp_str); diff --git a/applications/external/totp/services/config/config.h b/applications/external/totp/services/config/config.h index c630810a65..3d325368d4 100644 --- a/applications/external/totp/services/config/config.h +++ b/applications/external/totp/services/config/config.h @@ -103,6 +103,14 @@ TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_tim TotpConfigFileUpdateResult totp_config_file_update_notification_method(NotificationMethod new_notification_method); +/** + * @brief Updates automation method in an application config file + * @param new_automation_method new automation method to be set + * @return Config file update result + */ +TotpConfigFileUpdateResult + totp_config_file_update_automation_method(AutomationMethod new_automation_method); + /** * @brief Updates application user settings * @param plugin_state application state diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index 487fb159e1..526179f412 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -13,6 +13,7 @@ #define TOTP_CONFIG_KEY_BASE_IV "BaseIV" #define TOTP_CONFIG_KEY_PINSET "PinIsSet" #define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod" +#define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod" #define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1" #define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256" diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 966c9fb346..74ec52f2c0 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -8,6 +8,7 @@ #include #include #include +#include "features_config.h" #include "services/config/config.h" #include "types/plugin_state.h" #include "types/token_info.h" @@ -108,6 +109,14 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { return false; } +#ifdef TOTP_BADBT_TYPE_ENABLED + if(plugin_state->automation_method & AutomationMethodBadBt) { + plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init(); + } else { + plugin_state->bt_type_code_worker_context = NULL; + } +#endif + return true; } @@ -130,6 +139,13 @@ static void totp_plugin_state_free(PluginState* plugin_state) { free(plugin_state->crypto_verify_data); } +#ifdef TOTP_BADBT_TYPE_ENABLED + if(plugin_state->bt_type_code_worker_context != NULL) { + totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); + plugin_state->bt_type_code_worker_context = NULL; + } +#endif + furi_mutex_free(plugin_state->mutex); free(plugin_state); } diff --git a/applications/external/totp/types/automation_method.h b/applications/external/totp/types/automation_method.h new file mode 100644 index 0000000000..b51e59e03c --- /dev/null +++ b/applications/external/totp/types/automation_method.h @@ -0,0 +1,13 @@ +#pragma once + +#include "../features_config.h" + +typedef uint8_t AutomationMethod; + +enum AutomationMethods { + AutomationMethodNone = 0b00, + AutomationMethodBadUsb = 0b01, +#ifdef TOTP_BADBT_TYPE_ENABLED + AutomationMethodBadBt = 0b10, +#endif +}; diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index dee5003052..59a218ce3b 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -3,9 +3,14 @@ #include #include #include +#include "../features_config.h" #include "../lib/list/list.h" #include "../ui/totp_scenes_enum.h" #include "notification_method.h" +#include "automation_method.h" +#ifdef TOTP_BADBT_TYPE_ENABLED +#include "../workers/bt_type_code/bt_type_code.h" +#endif #define TOTP_IV_SIZE 16 @@ -92,4 +97,16 @@ typedef struct { * @brief Main rendering loop mutex */ FuriMutex* mutex; + + /** + * @brief Automation method + */ + AutomationMethod automation_method; + +#ifdef TOTP_BADBT_TYPE_ENABLED + /** + * @brief Bad-Bluetooth worker context + */ + TotpBtTypeCodeWorkerContext* bt_type_code_worker_context; +#endif } PluginState; diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index fcc9f37d8e..c77e88ab4b 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -37,7 +37,9 @@ void totp_scene_director_activate_scene( } void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state) { - switch(plugin_state->current_scene) { + Scene current_scene = plugin_state->current_scene; + plugin_state->current_scene = TotpSceneNone; + switch(current_scene) { case TotpSceneGenerateToken: totp_scene_generate_token_deactivate(plugin_state); break; diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index b8936f3955..1671542b87 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -10,16 +10,35 @@ #include "../../../services/convert/convert.h" #include "../../../lib/roll_value/roll_value.h" #include "../../../types/nullable.h" +#include "../../../features_config.h" +#ifdef TOTP_BADBT_TYPE_ENABLED +#include "../../../workers/bt_type_code/bt_type_code.h" +#endif char* YES_NO_LIST[] = {"NO", "YES"}; +char* ON_OFF_LIST[] = {"OFF", "ON"}; -typedef enum { HoursInput, MinutesInput, Sound, Vibro, ConfirmButton } Control; +typedef enum { + HoursInput, + MinutesInput, + Sound, + Vibro, + BadUsb, +#ifdef TOTP_BADBT_TYPE_ENABLED + BadBt, +#endif + ConfirmButton +} Control; typedef struct { int8_t tz_offset_hours; uint8_t tz_offset_minutes; bool notification_sound; bool notification_vibro; + bool badusb_enabled; +#ifdef TOTP_BADBT_TYPE_ENABLED + bool badbt_enabled; +#endif uint8_t y_offset; TotpNullable_uint16_t current_token_index; Control selected_control; @@ -47,6 +66,10 @@ void totp_scene_app_settings_activate( scene_state->tz_offset_minutes = 60.0f * off_dec; scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound; scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro; + scene_state->badusb_enabled = plugin_state->automation_method & AutomationMethodBadUsb; +#ifdef TOTP_BADBT_TYPE_ENABLED + scene_state->badbt_enabled = plugin_state->automation_method & AutomationMethodBadBt; +#endif } static void two_digit_to_str(int8_t num, char* str) { @@ -73,7 +96,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu char tmp_str[4]; two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); - canvas_draw_str_aligned(canvas, 0, 16 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); + canvas_draw_str_aligned(canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); ui_control_select_render( canvas, 36, @@ -84,7 +107,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); canvas_draw_str_aligned( - canvas, 0, 34 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); + canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); ui_control_select_render( canvas, 36, @@ -104,7 +127,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications"); canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 0, 80 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); + canvas_draw_str_aligned(canvas, 0, 81 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); ui_control_select_render( canvas, 36, @@ -113,7 +136,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu YES_NO_LIST[scene_state->notification_sound], scene_state->selected_control == Sound); - canvas_draw_str_aligned(canvas, 0, 98 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); + canvas_draw_str_aligned(canvas, 0, 99 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); ui_control_select_render( canvas, 36, @@ -122,10 +145,43 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu YES_NO_LIST[scene_state->notification_vibro], scene_state->selected_control == Vibro); + canvas_draw_icon( + canvas, SCREEN_WIDTH_CENTER - 5, 123 - scene_state->y_offset, &I_totp_arrow_bottom_10x5); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Automation"); + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned( + canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "BadUSB:"); + ui_control_select_render( + canvas, + 36, + 138 - scene_state->y_offset, + SCREEN_WIDTH - 36, + ON_OFF_LIST[scene_state->badusb_enabled], + scene_state->selected_control == BadUsb); + +#ifdef TOTP_BADBT_TYPE_ENABLED + canvas_draw_str_aligned(canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "BadBT:"); + ui_control_select_render( + canvas, + 36, + 156 - scene_state->y_offset, + SCREEN_WIDTH - 36, + ON_OFF_LIST[scene_state->badbt_enabled], + scene_state->selected_control == BadBt); +#endif + ui_control_button_render( canvas, SCREEN_WIDTH_CENTER - 24, - 115 - scene_state->y_offset, +#ifdef TOTP_BADBT_TYPE_ENABLED + 178 - scene_state->y_offset, +#else + 165 - scene_state->y_offset, +#endif 48, 13, "Confirm", @@ -152,7 +208,9 @@ bool totp_scene_app_settings_handle_event( HoursInput, ConfirmButton, RollOverflowBehaviorStop); - if(scene_state->selected_control > MinutesInput) { + if(scene_state->selected_control > Vibro) { + scene_state->y_offset = 128; + } else if(scene_state->selected_control > MinutesInput) { scene_state->y_offset = 64; } else { scene_state->y_offset = 0; @@ -161,7 +219,9 @@ bool totp_scene_app_settings_handle_event( case InputKeyDown: totp_roll_value_uint8_t( &scene_state->selected_control, 1, HoursInput, ConfirmButton, RollOverflowBehaviorStop); - if(scene_state->selected_control > MinutesInput) { + if(scene_state->selected_control > Vibro) { + scene_state->y_offset = 128; + } else if(scene_state->selected_control > MinutesInput) { scene_state->y_offset = 64; } else { scene_state->y_offset = 0; @@ -178,7 +238,14 @@ bool totp_scene_app_settings_handle_event( scene_state->notification_sound = !scene_state->notification_sound; } else if(scene_state->selected_control == Vibro) { scene_state->notification_vibro = !scene_state->notification_vibro; + } else if(scene_state->selected_control == BadUsb) { + scene_state->badusb_enabled = !scene_state->badusb_enabled; } +#ifdef TOTP_BADBT_TYPE_ENABLED + else if(scene_state->selected_control == BadBt) { + scene_state->badbt_enabled = !scene_state->badbt_enabled; + } +#endif break; case InputKeyLeft: if(scene_state->selected_control == HoursInput) { @@ -191,7 +258,14 @@ bool totp_scene_app_settings_handle_event( scene_state->notification_sound = !scene_state->notification_sound; } else if(scene_state->selected_control == Vibro) { scene_state->notification_vibro = !scene_state->notification_vibro; + } else if(scene_state->selected_control == BadUsb) { + scene_state->badusb_enabled = !scene_state->badusb_enabled; + } +#ifdef TOTP_BADBT_TYPE_ENABLED + else if(scene_state->selected_control == BadBt) { + scene_state->badbt_enabled = !scene_state->badbt_enabled; } +#endif break; case InputKeyOk: if(scene_state->selected_control == ConfirmButton) { @@ -204,12 +278,26 @@ bool totp_scene_app_settings_handle_event( (scene_state->notification_vibro ? NotificationMethodVibro : NotificationMethodNone); + plugin_state->automation_method = + scene_state->badusb_enabled ? AutomationMethodBadUsb : AutomationMethodNone; +#ifdef TOTP_BADBT_TYPE_ENABLED + plugin_state->automation_method |= scene_state->badbt_enabled ? AutomationMethodBadBt : + AutomationMethodNone; +#endif + if(totp_config_file_update_user_settings(plugin_state) != TotpConfigFileUpdateSuccess) { totp_dialogs_config_updating_error(plugin_state); return false; } +#ifdef TOTP_BADBT_TYPE_ENABLED + if(!scene_state->badbt_enabled && plugin_state->bt_type_code_worker_context != NULL) { + totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); + plugin_state->bt_type_code_worker_context = NULL; + } +#endif + if(!scene_state->current_token_index.is_null) { TokenMenuSceneContext generate_scene_context = { .current_token_index = scene_state->current_token_index.value}; diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index a882ae78ca..9e8b21d09a 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -14,7 +14,11 @@ #include "../../../lib/roll_value/roll_value.h" #include "../../scene_director.h" #include "../token_menu/totp_scene_token_menu.h" -#include "../../../workers/type_code/type_code.h" +#include "../../../features_config.h" +#include "../../../workers/usb_type_code/usb_type_code.h" +#ifdef TOTP_BADBT_TYPE_ENABLED +#include "../../../workers/bt_type_code/bt_type_code.h" +#endif static const uint8_t PROGRESS_BAR_MARGIN = 3; static const uint8_t PROGRESS_BAR_HEIGHT = 4; @@ -25,9 +29,10 @@ typedef struct { bool need_token_update; TokenInfo* current_token; uint32_t last_token_gen_time; - TotpTypeCodeWorkerContext* type_code_worker_context; + TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context; NotificationMessage const** notification_sequence_new_token; NotificationMessage const** notification_sequence_badusb; + FuriMutex* last_code_update_sync; } SceneState; static const NotificationSequence* @@ -72,7 +77,7 @@ static const NotificationSequence* } static const NotificationSequence* - get_notification_sequence_badusb(const PluginState* plugin_state, SceneState* scene_state) { + get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) { if(scene_state->notification_sequence_badusb == NULL) { uint8_t i = 0; uint8_t length = 3; @@ -201,9 +206,28 @@ void totp_scene_generate_token_activate( plugin_state->current_scene_state = scene_state; FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset); update_totp_params(plugin_state); - scene_state->type_code_worker_context = totp_type_code_worker_start(); - scene_state->type_code_worker_context->string = &scene_state->last_code[0]; - scene_state->type_code_worker_context->string_length = TOTP_TOKEN_DIGITS_MAX_COUNT + 1; + + scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal); + if(plugin_state->automation_method & AutomationMethodBadUsb) { + scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start( + &scene_state->last_code[0], + TOTP_TOKEN_DIGITS_MAX_COUNT + 1, + scene_state->last_code_update_sync); + } + +#ifdef TOTP_BADBT_TYPE_ENABLED + + if(plugin_state->automation_method & AutomationMethodBadBt) { + if(plugin_state->bt_type_code_worker_context == NULL) { + plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init(); + } + totp_bt_type_code_worker_start( + plugin_state->bt_type_code_worker_context, + &scene_state->last_code[0], + TOTP_TOKEN_DIGITS_MAX_COUNT + 1, + scene_state->last_code_update_sync); + } +#endif } void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) { @@ -242,8 +266,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ const TokenInfo* tokenInfo = scene_state->current_token; if(tokenInfo->token != NULL && tokenInfo->token_length > 0) { - furi_mutex_acquire( - scene_state->type_code_worker_context->string_sync, FuriWaitForever); + furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever); size_t key_length; uint8_t* key = totp_crypto_decrypt( tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length); @@ -262,12 +285,11 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ memset_s(key, key_length, 0, key_length); free(key); } else { - furi_mutex_acquire( - scene_state->type_code_worker_context->string_sync, FuriWaitForever); + furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever); int_token_to_str(0, scene_state->last_code, tokenInfo->digits); } - furi_mutex_release(scene_state->type_code_worker_context->string_sync); + furi_mutex_release(scene_state->last_code_update_sync); if(is_new_token_time) { notification_message( @@ -327,6 +349,15 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ canvas_draw_icon( canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); } + +#if defined(TOTP_BADBT_TYPE_ENABLED) && defined(TOTP_BADBT_TYPE_ICON_ENABLED) + if(plugin_state->automation_method & AutomationMethodBadBt && + plugin_state->bt_type_code_worker_context != NULL && + plugin_state->bt_type_code_worker_context->is_advertising) { + canvas_draw_icon( + canvas, SCREEN_WIDTH_CENTER - 5, SCREEN_HEIGHT_CENTER + 13, &I_hid_ble_10x7); + } +#endif } bool totp_scene_generate_token_handle_event( @@ -341,14 +372,27 @@ bool totp_scene_generate_token_handle_event( } SceneState* scene_state; - if(event->input.type == InputTypeLong && event->input.key == InputKeyDown) { - scene_state = (SceneState*)plugin_state->current_scene_state; - totp_type_code_worker_notify( - scene_state->type_code_worker_context, TotpTypeCodeWorkerEventType); - notification_message( - plugin_state->notification_app, - get_notification_sequence_badusb(plugin_state, scene_state)); - return true; + if(event->input.type == InputTypeLong) { + if(event->input.key == InputKeyDown && plugin_state->automation_method & AutomationMethodBadUsb) { + scene_state = (SceneState*)plugin_state->current_scene_state; + totp_usb_type_code_worker_notify( + scene_state->usb_type_code_worker_context, TotpUsbTypeCodeWorkerEventType); + notification_message( + plugin_state->notification_app, + get_notification_sequence_automation(plugin_state, scene_state)); + return true; + } +#ifdef TOTP_BADBT_TYPE_ENABLED + else if(event->input.key == InputKeyUp && plugin_state->automation_method & AutomationMethodBadBt) { + scene_state = (SceneState*)plugin_state->current_scene_state; + totp_bt_type_code_worker_notify( + plugin_state->bt_type_code_worker_context, TotpBtTypeCodeWorkerEventType); + notification_message( + plugin_state->notification_app, + get_notification_sequence_automation(plugin_state, scene_state)); + return true; + } +#endif } if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) { @@ -400,7 +444,14 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { if(plugin_state->current_scene_state == NULL) return; SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - totp_type_code_worker_stop(scene_state->type_code_worker_context); + if(plugin_state->automation_method & AutomationMethodBadUsb) { + totp_usb_type_code_worker_stop(scene_state->usb_type_code_worker_context); + } +#ifdef TOTP_BADBT_TYPE_ENABLED + if(plugin_state->automation_method & AutomationMethodBadBt) { + totp_bt_type_code_worker_stop(plugin_state->bt_type_code_worker_context); + } +#endif if(scene_state->notification_sequence_new_token != NULL) { free(scene_state->notification_sequence_new_token); @@ -410,6 +461,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { free(scene_state->notification_sequence_badusb); } + furi_mutex_free(scene_state->last_code_update_sync); + free(scene_state); plugin_state->current_scene_state = NULL; } diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c new file mode 100644 index 0000000000..8b9cf6548b --- /dev/null +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -0,0 +1,141 @@ +#include "bt_type_code.h" +#include +#include +#include "../../types/common.h" +#include "../../services/convert/convert.h" +#include "../constants.h" + +#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys") + +static inline bool totp_type_code_worker_stop_requested() { + return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop; +} + +static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) { + uint8_t i = 0; + do { + furi_delay_ms(500); + i++; + } while(!furi_hal_bt_is_active() && i < 100 && !totp_type_code_worker_stop_requested()); + + if(furi_hal_bt_is_active() && furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) { + furi_delay_ms(500); + i = 0; + while(i < context->string_length && context->string[i] != 0) { + uint8_t digit = CONVERT_CHAR_TO_DIGIT(context->string[i]); + if(digit > 9) break; + uint8_t hid_kb_key = hid_number_keys[digit]; + furi_hal_bt_hid_kb_press(hid_kb_key); + furi_delay_ms(30); + furi_hal_bt_hid_kb_release(hid_kb_key); + i++; + } + + furi_mutex_release(context->string_sync); + } +} + +static int32_t totp_type_code_worker_callback(void* context) { + furi_assert(context); + FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(context_mutex == NULL) { + return 251; + } + + TotpBtTypeCodeWorkerContext* bt_context = context; + + furi_hal_bt_start_advertising(); + bt_context->is_advertising = true; + + while(true) { + uint32_t flags = furi_thread_flags_wait( + TotpBtTypeCodeWorkerEventStop | TotpBtTypeCodeWorkerEventType, + FuriFlagWaitAny, + FuriWaitForever); + furi_check((flags & FuriFlagError) == 0); //-V562 + if(flags & TotpBtTypeCodeWorkerEventStop) break; + + if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) { + if(flags & TotpBtTypeCodeWorkerEventType) { + totp_type_code_worker_type_code(bt_context); + } + + furi_mutex_release(context_mutex); + } + } + + furi_hal_bt_stop_advertising(); + + bt_context->is_advertising = false; + + furi_mutex_free(context_mutex); + + return 0; +} + +void totp_bt_type_code_worker_start( + TotpBtTypeCodeWorkerContext* context, + char* code_buf, + uint8_t code_buf_length, + FuriMutex* code_buf_update_sync) { + furi_assert(context != NULL); + context->string = code_buf; + context->string_length = code_buf_length; + context->string_sync = code_buf_update_sync; + context->thread = furi_thread_alloc(); + furi_thread_set_name(context->thread, "TOTPBtHidWorker"); + furi_thread_set_stack_size(context->thread, 1024); + furi_thread_set_context(context->thread, context); + furi_thread_set_callback(context->thread, totp_type_code_worker_callback); + furi_thread_start(context->thread); +} + +void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context) { + furi_assert(context != NULL); + furi_thread_flags_set(furi_thread_get_id(context->thread), TotpBtTypeCodeWorkerEventStop); + furi_thread_join(context->thread); + furi_thread_free(context->thread); + context->thread = NULL; +} + +void totp_bt_type_code_worker_notify( + TotpBtTypeCodeWorkerContext* context, + TotpBtTypeCodeWorkerEvent event) { + furi_assert(context != NULL); + furi_thread_flags_set(furi_thread_get_id(context->thread), event); +} + +TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() { + TotpBtTypeCodeWorkerContext* context = malloc(sizeof(TotpBtTypeCodeWorkerContext)); + furi_check(context != NULL); + + context->bt = furi_record_open(RECORD_BT); + context->is_advertising = false; + bt_disconnect(context->bt); + furi_delay_ms(200); + bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH); + if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) { + FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to keyboard HID profile"); + } + + return context; +} + +void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) { + furi_assert(context != NULL); + + if(context->thread != NULL) { + totp_bt_type_code_worker_stop(context); + } + + bt_disconnect(context->bt); + bt_keys_storage_set_default_path(context->bt); + + if(!bt_set_profile(context->bt, BtProfileSerial)) { + FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to Serial profile"); + } + furi_record_close(RECORD_BT); + context->bt = NULL; + + free(context); +} \ No newline at end of file diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h new file mode 100644 index 0000000000..475b66db40 --- /dev/null +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +typedef uint8_t TotpBtTypeCodeWorkerEvent; + +typedef struct { + char* string; + uint8_t string_length; + FuriThread* thread; + FuriMutex* string_sync; + Bt* bt; + bool is_advertising; +} TotpBtTypeCodeWorkerContext; + +enum TotpBtTypeCodeWorkerEvents { + TotpBtTypeCodeWorkerEventReserved = (1 << 0), + TotpBtTypeCodeWorkerEventStop = (1 << 1), + TotpBtTypeCodeWorkerEventType = (1 << 2) +}; + +TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(); +void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context); +void totp_bt_type_code_worker_start( + TotpBtTypeCodeWorkerContext* context, + char* code_buf, + uint8_t code_buf_length, + FuriMutex* code_buf_update_sync); +void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context); +void totp_bt_type_code_worker_notify( + TotpBtTypeCodeWorkerContext* context, + TotpBtTypeCodeWorkerEvent event); \ No newline at end of file diff --git a/applications/external/totp/workers/constants.c b/applications/external/totp/workers/constants.c new file mode 100644 index 0000000000..f3c1035781 --- /dev/null +++ b/applications/external/totp/workers/constants.c @@ -0,0 +1,14 @@ +#include "constants.h" +#include + +const uint8_t hid_number_keys[10] = { + HID_KEYBOARD_0, + HID_KEYBOARD_1, + HID_KEYBOARD_2, + HID_KEYBOARD_3, + HID_KEYBOARD_4, + HID_KEYBOARD_5, + HID_KEYBOARD_6, + HID_KEYBOARD_7, + HID_KEYBOARD_8, + HID_KEYBOARD_9}; \ No newline at end of file diff --git a/applications/external/totp/workers/constants.h b/applications/external/totp/workers/constants.h new file mode 100644 index 0000000000..c314b6c166 --- /dev/null +++ b/applications/external/totp/workers/constants.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const uint8_t hid_number_keys[10]; \ No newline at end of file diff --git a/applications/external/totp/workers/type_code/type_code.h b/applications/external/totp/workers/type_code/type_code.h deleted file mode 100644 index 27f2e02d4c..0000000000 --- a/applications/external/totp/workers/type_code/type_code.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef uint8_t TotpTypeCodeWorkerEvent; - -typedef struct { - char* string; - uint8_t string_length; - FuriThread* thread; - FuriMutex* string_sync; - FuriHalUsbInterface* usb_mode_prev; -} TotpTypeCodeWorkerContext; - -enum TotpTypeCodeWorkerEvents { - TotpTypeCodeWorkerEventReserved = (1 << 0), - TotpTypeCodeWorkerEventStop = (1 << 1), - TotpTypeCodeWorkerEventType = (1 << 2) -}; - -TotpTypeCodeWorkerContext* totp_type_code_worker_start(); -void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context); -void totp_type_code_worker_notify( - TotpTypeCodeWorkerContext* context, - TotpTypeCodeWorkerEvent event); \ No newline at end of file diff --git a/applications/external/totp/workers/type_code/type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c similarity index 68% rename from applications/external/totp/workers/type_code/type_code.c rename to applications/external/totp/workers/usb_type_code/usb_type_code.c index f2b4c9b9e6..3132e2317e 100644 --- a/applications/external/totp/workers/type_code/type_code.c +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c @@ -1,19 +1,8 @@ -#include "type_code.h" +#include "usb_type_code.h" #include "../../services/convert/convert.h" +#include "../constants.h" -static const uint8_t hid_number_keys[10] = { - HID_KEYBOARD_0, - HID_KEYBOARD_1, - HID_KEYBOARD_2, - HID_KEYBOARD_3, - HID_KEYBOARD_4, - HID_KEYBOARD_5, - HID_KEYBOARD_6, - HID_KEYBOARD_7, - HID_KEYBOARD_8, - HID_KEYBOARD_9}; - -static void totp_type_code_worker_restore_usb_mode(TotpTypeCodeWorkerContext* context) { +static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { if(context->usb_mode_prev != NULL) { furi_hal_usb_set_config(context->usb_mode_prev, NULL); context->usb_mode_prev = NULL; @@ -21,10 +10,10 @@ static void totp_type_code_worker_restore_usb_mode(TotpTypeCodeWorkerContext* co } static inline bool totp_type_code_worker_stop_requested() { - return furi_thread_flags_get() & TotpTypeCodeWorkerEventStop; + return furi_thread_flags_get() & TotpUsbTypeCodeWorkerEventStop; } -static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context) { +static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* context) { context->usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_unlock(); furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); @@ -57,6 +46,7 @@ static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context) } static int32_t totp_type_code_worker_callback(void* context) { + furi_assert(context); FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); if(context_mutex == NULL) { return 251; @@ -64,14 +54,14 @@ static int32_t totp_type_code_worker_callback(void* context) { while(true) { uint32_t flags = furi_thread_flags_wait( - TotpTypeCodeWorkerEventStop | TotpTypeCodeWorkerEventType, + TotpUsbTypeCodeWorkerEventStop | TotpUsbTypeCodeWorkerEventType, FuriFlagWaitAny, FuriWaitForever); furi_check((flags & FuriFlagError) == 0); //-V562 - if(flags & TotpTypeCodeWorkerEventStop) break; + if(flags & TotpUsbTypeCodeWorkerEventStop) break; if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) { - if(flags & TotpTypeCodeWorkerEventType) { + if(flags & TotpUsbTypeCodeWorkerEventType) { totp_type_code_worker_type_code(context); } @@ -84,13 +74,18 @@ static int32_t totp_type_code_worker_callback(void* context) { return 0; } -TotpTypeCodeWorkerContext* totp_type_code_worker_start() { - TotpTypeCodeWorkerContext* context = malloc(sizeof(TotpTypeCodeWorkerContext)); +TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( + char* code_buf, + uint8_t code_buf_length, + FuriMutex* code_buf_update_sync) { + TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext)); furi_check(context != NULL); - context->string_sync = furi_mutex_alloc(FuriMutexTypeNormal); + context->string = code_buf; + context->string_length = code_buf_length; + context->string_sync = code_buf_update_sync; context->thread = furi_thread_alloc(); context->usb_mode_prev = NULL; - furi_thread_set_name(context->thread, "TOTPHidWorker"); + furi_thread_set_name(context->thread, "TOTPUsbHidWorker"); furi_thread_set_stack_size(context->thread, 1024); furi_thread_set_context(context->thread, context); furi_thread_set_callback(context->thread, totp_type_code_worker_callback); @@ -98,19 +93,18 @@ TotpTypeCodeWorkerContext* totp_type_code_worker_start() { return context; } -void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context) { +void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) { furi_assert(context != NULL); - furi_thread_flags_set(furi_thread_get_id(context->thread), TotpTypeCodeWorkerEventStop); + furi_thread_flags_set(furi_thread_get_id(context->thread), TotpUsbTypeCodeWorkerEventStop); furi_thread_join(context->thread); furi_thread_free(context->thread); - furi_mutex_free(context->string_sync); totp_type_code_worker_restore_usb_mode(context); free(context); } -void totp_type_code_worker_notify( - TotpTypeCodeWorkerContext* context, - TotpTypeCodeWorkerEvent event) { +void totp_usb_type_code_worker_notify( + TotpUsbTypeCodeWorkerContext* context, + TotpUsbTypeCodeWorkerEvent event) { furi_assert(context != NULL); furi_thread_flags_set(furi_thread_get_id(context->thread), event); } \ No newline at end of file diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.h b/applications/external/totp/workers/usb_type_code/usb_type_code.h new file mode 100644 index 0000000000..94fddcc596 --- /dev/null +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +typedef uint8_t TotpUsbTypeCodeWorkerEvent; + +typedef struct { + char* string; + uint8_t string_length; + FuriThread* thread; + FuriMutex* string_sync; + FuriHalUsbInterface* usb_mode_prev; +} TotpUsbTypeCodeWorkerContext; + +enum TotpUsbTypeCodeWorkerEvents { + TotpUsbTypeCodeWorkerEventReserved = (1 << 0), + TotpUsbTypeCodeWorkerEventStop = (1 << 1), + TotpUsbTypeCodeWorkerEventType = (1 << 2) +}; + +TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( + char* code_buf, + uint8_t code_buf_length, + FuriMutex* code_buf_update_sync); +void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context); +void totp_usb_type_code_worker_notify( + TotpUsbTypeCodeWorkerContext* context, + TotpUsbTypeCodeWorkerEvent event); \ No newline at end of file diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index f5c7af0248..d85eb784b6 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -63,7 +63,7 @@ static bool fap_loader_item_callback( return fap_loader_load_name_and_icon(path, fap_loader->storage, icon_ptr, item_name); } -static bool fap_loader_run_selected_app(FapLoader* loader) { +static bool fap_loader_run_selected_app(FapLoader* loader, bool ignore_mismatch) { furi_assert(loader); FuriString* error_message; @@ -72,6 +72,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { bool file_selected = false; bool show_error = true; + bool retry = false; do { file_selected = true; loader->app = flipper_application_alloc(loader->storage, firmware_api_interface); @@ -82,14 +83,29 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { FlipperApplicationPreloadStatus preload_res = flipper_application_preload(loader->app, furi_string_get_cstr(loader->fap_path)); if(preload_res != FlipperApplicationPreloadStatusSuccess) { - const char* err_msg = flipper_application_preload_status_to_string(preload_res); - furi_string_printf(error_message, "Preload failed: %s", err_msg); - FURI_LOG_E( - TAG, - "FAP Loader failed to preload %s: %s", - furi_string_get_cstr(loader->fap_path), - err_msg); - break; + if(preload_res == FlipperApplicationPreloadStatusApiMismatch) { + if(!ignore_mismatch) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "API Mismatch", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, "Cancel", NULL, "Continue"); + dialog_message_set_text(message, "This app might not\nwork correctly\nContinue anyways?", 64, 32, AlignCenter, AlignCenter); + if(dialog_message_show(loader->dialogs, message) == DialogMessageButtonRight) { + retry = true; + } + dialog_message_free(message); + show_error = false; + break; + } + } else { + const char* err_msg = flipper_application_preload_status_to_string(preload_res); + furi_string_printf(error_message, "Preload failed: %s", err_msg); + FURI_LOG_E( + TAG, + "FAP Loader failed to preload %s: %s", + furi_string_get_cstr(loader->fap_path), + err_msg); + break; + } } FURI_LOG_I(TAG, "FAP Loader is mapping"); @@ -155,7 +171,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { flipper_application_free(loader->app); } - return file_selected; + return retry; } static bool fap_loader_select_app(FapLoader* loader) { @@ -203,12 +219,16 @@ int32_t fap_loader_app(void* p) { if(p) { loader = fap_loader_alloc((const char*)p); view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - fap_loader_run_selected_app(loader); + if(fap_loader_run_selected_app(loader, false)) { + fap_loader_run_selected_app(loader, true); + } } else { loader = fap_loader_alloc(EXT_PATH("apps")); while(fap_loader_select_app(loader)) { view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - fap_loader_run_selected_app(loader); + if(fap_loader_run_selected_app(loader, false)) { + fap_loader_run_selected_app(loader, true); + } }; } diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 69f946a9a5..f94b97659b 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -19,6 +19,7 @@ typedef enum { SubmenuIndexSommer_FM_868, SubmenuIndexDTMNeo433, SubmenuIndexGibidi433, + SubmenuIndexGSN, SubmenuIndexNiceFlo12bit, SubmenuIndexNiceFlo24bit, SubmenuIndexNiceFlorS_433_92, diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 50af126693..ac177c0d03 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -152,6 +152,12 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexGibidi433, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "KL: GSN 433MHz", + SubmenuIndexGSN, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "KL: Elmes (PL) 433MHz", @@ -521,6 +527,31 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; + case SubmenuIndexGSN: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x0FFFFFFF, + 0x2, + 0x0003, + "GSN", + subghz->txrx->preset); + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "GSN"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; case SubmenuIndexIronLogic: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); diff --git a/lib/toolbox/hmac_sha256.c b/applications/main/u2f/hmac_sha256.c similarity index 100% rename from lib/toolbox/hmac_sha256.c rename to applications/main/u2f/hmac_sha256.c diff --git a/lib/toolbox/hmac_sha256.h b/applications/main/u2f/hmac_sha256.h similarity index 100% rename from lib/toolbox/hmac_sha256.h rename to applications/main/u2f/hmac_sha256.h diff --git a/applications/main/u2f/u2f.c b/applications/main/u2f/u2f.c index 767733ce65..0ed5ebb296 100644 --- a/applications/main/u2f/u2f.c +++ b/applications/main/u2f/u2f.c @@ -7,7 +7,7 @@ #include // for lfs_tobe32 #include "toolbox/sha256.h" -#include "toolbox/hmac_sha256.h" +#include "hmac_sha256.h" #include "micro-ecc/uECC.h" #define TAG "U2F" diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index bb14d28a35..2f3ca18f4e 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -18,6 +18,7 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = { Canvas* canvas_init() { Canvas* canvas = malloc(sizeof(Canvas)); + canvas->compress_icon = compress_icon_alloc(); // Setup u8g2 u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); @@ -36,6 +37,7 @@ Canvas* canvas_init() { void canvas_free(Canvas* canvas) { furi_assert(canvas); + compress_icon_free(canvas->compress_icon); free(canvas); } @@ -221,7 +223,7 @@ void canvas_draw_bitmap( x += canvas->offset_x; y += canvas->offset_y; uint8_t* bitmap_data = NULL; - furi_hal_compress_icon_decode(compressed_bitmap_data, &bitmap_data); + compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data); u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data); } @@ -236,7 +238,8 @@ void canvas_draw_icon_animation( x += canvas->offset_x; y += canvas->offset_y; uint8_t* icon_data = NULL; - furi_hal_compress_icon_decode(icon_animation_get_data(icon_animation), &icon_data); + compress_icon_decode( + canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data); u8g2_DrawXBM( &canvas->fb, x, @@ -253,7 +256,7 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) { x += canvas->offset_x; y += canvas->offset_y; uint8_t* icon_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(icon), &icon_data); + compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data); } @@ -387,7 +390,7 @@ void canvas_draw_icon_bitmap( x += canvas->offset_x; y += canvas->offset_y; uint8_t* icon_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(icon), &icon_data); + compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); u8g2_DrawXBM(&canvas->fb, x, y, w, h, icon_data); } diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 745599c956..aadb5200f6 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -7,6 +7,7 @@ #include "canvas.h" #include +#include #ifdef __cplusplus extern "C" { @@ -21,6 +22,7 @@ struct Canvas { uint8_t offset_y; uint8_t width; uint8_t height; + CompressIcon* compress_icon; }; /** Allocate memory and initialize canvas diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 24b48a837d..392011620a 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -250,6 +250,7 @@ static void gui_redraw(Gui* gui) { p->callback( canvas_get_buffer(gui->canvas), canvas_get_buffer_size(gui->canvas), + canvas_get_orientation(gui->canvas), p->context); } } while(false); diff --git a/applications/services/gui/gui.h b/applications/services/gui/gui.h index d7d73f27b6..1b5987edac 100644 --- a/applications/services/gui/gui.h +++ b/applications/services/gui/gui.h @@ -27,7 +27,11 @@ typedef enum { } GuiLayer; /** Gui Canvas Commit Callback */ -typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context); +typedef void (*GuiCanvasCommitCallback)( + uint8_t* data, + size_t size, + CanvasOrientation orientation, + void* context); #define RECORD_GUI "gui" diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index c2af425e93..0c70702cf4 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -33,8 +33,18 @@ typedef struct { uint32_t input_counter; } RpcGuiSystem; -static void - rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) { +static const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = { + [CanvasOrientationHorizontal] = PB_Gui_ScreenOrientation_HORIZONTAL, + [CanvasOrientationHorizontalFlip] = PB_Gui_ScreenOrientation_HORIZONTAL_FLIP, + [CanvasOrientationVertical] = PB_Gui_ScreenOrientation_VERTICAL, + [CanvasOrientationVerticalFlip] = PB_Gui_ScreenOrientation_VERTICAL_FLIP, +}; + +static void rpc_system_gui_screen_stream_frame_callback( + uint8_t* data, + size_t size, + CanvasOrientation orientation, + void* context) { furi_assert(data); furi_assert(context); @@ -44,6 +54,8 @@ static void furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size); memcpy(buffer, data, size); + rpc_gui->transmit_frame->content.gui_screen_frame.orientation = + rpc_system_gui_screen_orientation_map[orientation]; furi_thread_flags_set(furi_thread_get_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagTransmit); } @@ -53,12 +65,22 @@ static int32_t rpc_system_gui_screen_stream_frame_transmit_thread(void* context) RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context; + uint32_t transmit_time = 0; while(true) { uint32_t flags = furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever); + if(flags & RpcGuiWorkerFlagTransmit) { + transmit_time = furi_get_tick(); rpc_send(rpc_gui->session, rpc_gui->transmit_frame); + transmit_time = furi_get_tick() - transmit_time; + + // Guaranteed bandwidth reserve + uint32_t extra_delay = transmit_time / 20; + if(extra_delay > 500) extra_delay = 500; + if(extra_delay) furi_delay_tick(extra_delay); } + if(flags & RpcGuiWorkerFlagExit) { break; } diff --git a/assets/protobuf b/assets/protobuf index 6460660237..1f6b4a08c5 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 6460660237005d02d5c223835659b40e373bade9 +Subproject commit 1f6b4a08c5d05c2b17926a3ba79f60109638932f diff --git a/documentation/AppsOnSDCard.md b/documentation/AppsOnSDCard.md index 7543057069..212df5b1bf 100644 --- a/documentation/AppsOnSDCard.md +++ b/documentation/AppsOnSDCard.md @@ -13,7 +13,7 @@ FAPs are created and developed the same way as internal applications that are pa To build your application as a FAP, create a folder with your app's source code in `applications_user`, then write its code the way you'd do when creating a regular built-in application. Then configure its `application.fam` manifest, and set its _apptype_ to FlipperAppType.EXTERNAL. See [Application Manifests](./AppManifests.md#application-definition) for more details. - To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest. -- To build your app and upload it over USB to run on Flipper, use `./fbt launch_app APPSRC=applications/path/to/app`. This command is configured in the default [VS Code profile](../.vscode/ReadMe.md) as a "Launch App on Flipper" build action (Ctrl+Shift+B menu). +- To build your app and upload it over USB to run on Flipper, use `./fbt launch_app APPSRC=applications_user/path/to/app`. This command is configured in the default [VS Code profile](../.vscode/ReadMe.md) as a "Launch App on Flipper" build action (Ctrl+Shift+B menu). - To build all FAPs, run `./fbt faps` or `./fbt fap_dist`. ## FAP assets diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index d01cd62500..41cf0d6bc0 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -91,7 +91,8 @@ Your frequencies will be added after default ones ``` 310000000, 315000000, - 433420000, + 318000000, + 418000000, 433920000, 868350000, ``` diff --git a/documentation/UnitTests.md b/documentation/UnitTests.md index d38d4c4b19..4717daa8ca 100644 --- a/documentation/UnitTests.md +++ b/documentation/UnitTests.md @@ -17,11 +17,11 @@ To run the unit tests, follow these steps: 1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests`. 2. Flash the firmware using your preferred method. -3. Copy the [assets/unit_tests](assets/unit_tests) folder to the root of your Flipper Zero's SD card. +3. Copy the [assets/unit_tests](/assets/unit_tests) folder to the root of your Flipper Zero's SD card. 4. Launch the CLI session and run the `unit_tests` command. **NOTE:** To run a particular test (and skip all others), specify its name as the command argument. -See [test_index.c](applications/debug/unit_tests/test_index.c) for the complete list of test names. +See [test_index.c](/applications/debug/unit_tests/test_index.c) for the complete list of test names. ## Adding unit tests @@ -29,7 +29,7 @@ See [test_index.c](applications/debug/unit_tests/test_index.c) for the complete #### Entry point -The common entry point for all tests is the [unit_tests](applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](applications/debug/unit_tests/test_index.c) source file. +The common entry point for all tests is the [unit_tests](/applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](/applications/debug/unit_tests/test_index.c) source file. #### Test assets @@ -42,10 +42,10 @@ Some unit tests require external data in order to function. These files (commonl Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol. To add unit tests for your protocol, follow these steps: -1. Create a file named `test_.irtest` in the [assets](assets/unit_tests/infrared) directory. +1. Create a file named `test_.irtest` in the [assets](/assets/unit_tests/infrared) directory. 2. Fill it with the test data (more on it below). -3. Add the test code to [infrared_test.c](applications/debug/unit_tests/infrared/infrared_test.c). -4. Update the [assets](assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass. +3. Add the test code to [infrared_test.c](/applications/debug/unit_tests/infrared/infrared_test.c). +4. Update the [assets](/assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass. ##### Test data format diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 7fa269c961..40e23a747f 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,18.2,, +Version,+,20.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -57,7 +57,6 @@ Header,+,firmware/targets/furi_hal_include/furi_hal.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_serial.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_compress.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,, @@ -153,14 +152,12 @@ Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, -Header,+,lib/one_wire/one_wire_host_timing.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, -Header,+,lib/toolbox/hmac_sha256.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, @@ -169,6 +166,7 @@ Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, Header,+,lib/toolbox/random_name.h,, Header,+,lib/toolbox/saved_struct.h,, +Header,+,lib/toolbox/sha256.h,, Header,+,lib/toolbox/stream/buffered_file_stream.h,, Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, @@ -877,12 +875,12 @@ Function,-,furi_hal_clock_resume_tick,void, Function,-,furi_hal_clock_suspend_tick,void, Function,-,furi_hal_clock_switch_to_hsi,void, Function,-,furi_hal_clock_switch_to_pll,void, -Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t -Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,furi_hal_compress_free,void,FuriHalCompress* -Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**" -Function,-,furi_hal_compress_icon_init,void, +Function,-,compress_alloc,Compress*,uint16_t +Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,compress_free,void,Compress* +Function,-,compress_icon_decode,void,"const uint8_t*, uint8_t**" +Function,-,compress_icon_init,void, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, Function,+,furi_hal_console_init,void, @@ -1316,9 +1314,6 @@ Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" Function,+,hal_sd_detect,_Bool, Function,+,hal_sd_detect_init,void, Function,+,hal_sd_detect_set_low,void, -Function,+,hmac_sha256_finish,void,"const hmac_sha256_context*, const uint8_t*, uint8_t*" -Function,+,hmac_sha256_init,void,"hmac_sha256_context*, const uint8_t*" -Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, unsigned" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* Function,+,icon_animation_get_height,uint8_t,const IconAnimation* @@ -1484,8 +1479,8 @@ Function,+,onewire_host_read_bit,_Bool,OneWireHost* Function,+,onewire_host_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t" Function,+,onewire_host_reset,_Bool,OneWireHost* Function,+,onewire_host_reset_search,void,OneWireHost* -Function,+,onewire_host_search,uint8_t,"OneWireHost*, uint8_t*, OneWireHostSearchMode" -Function,+,onewire_host_skip,void,OneWireHost* +Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode" +Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool" Function,+,onewire_host_start,void,OneWireHost* Function,+,onewire_host_stop,void,OneWireHost* Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t" @@ -1499,6 +1494,7 @@ Function,+,onewire_slave_receive_bit,_Bool,OneWireSlave* Function,+,onewire_slave_send,_Bool,"OneWireSlave*, const uint8_t*, size_t" Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool" Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*" +Function,+,onewire_slave_set_overdrive,void,"OneWireSlave*, _Bool" Function,+,onewire_slave_set_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*" Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*" Function,+,onewire_slave_start,void,OneWireSlave* diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index 2c255fa0de..4064dd6472 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -1,5 +1,6 @@ #include #include +#include #include @@ -7,29 +8,20 @@ void furi_hal_init_early() { furi_hal_cortex_init_early(); - furi_hal_clock_init_early(); - furi_hal_resources_init_early(); - furi_hal_os_init(); - furi_hal_spi_config_init_early(); - furi_hal_i2c_init_early(); furi_hal_light_init(); - furi_hal_rtc_init_early(); } void furi_hal_deinit_early() { furi_hal_rtc_deinit_early(); - furi_hal_i2c_deinit_early(); furi_hal_spi_config_deinit_early(); - furi_hal_resources_deinit_early(); - furi_hal_clock_deinit_early(); } @@ -38,40 +30,24 @@ void furi_hal_init() { furi_hal_clock_init(); furi_hal_console_init(); furi_hal_rtc_init(); - furi_hal_interrupt_init(); - furi_hal_flash_init(); - furi_hal_resources_init(); - FURI_LOG_I(TAG, "GPIO OK"); - furi_hal_version_init(); - furi_hal_spi_config_init(); furi_hal_spi_dma_init(); - furi_hal_speaker_init(); - FURI_LOG_I(TAG, "Speaker OK"); - furi_hal_crypto_init(); - - // USB -#ifndef FURI_RAM_EXEC - furi_hal_usb_init(); - FURI_LOG_I(TAG, "USB OK"); -#endif - furi_hal_i2c_init(); - - // High Level furi_hal_power_init(); furi_hal_light_init(); + furi_hal_bt_init(); + furi_hal_memory_init(); + #ifndef FURI_RAM_EXEC + furi_hal_usb_init(); furi_hal_vibro_init(); #endif - furi_hal_bt_init(); - furi_hal_compress_icon_init(); } void furi_hal_switch(void* address) { diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 41cc80bfba..abb258cb11 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -4,6 +4,8 @@ #include #include +#define TAG "FuriHalResources" + const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; const GpioPin ibutton_gpio = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; @@ -198,6 +200,8 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); + + FURI_LOG_I(TAG, "Init OK"); } int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 69d0b371d5..a1830aba8e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,18.2,, +Version,+,20.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -63,7 +63,6 @@ Header,+,firmware/targets/furi_hal_include/furi_hal.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_serial.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_compress.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,, @@ -175,7 +174,6 @@ Header,+,lib/mlib/m-variant.h,, Header,+,lib/nfc/nfc_device.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, -Header,+,lib/one_wire/one_wire_host_timing.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, Header,+,lib/subghz/blocks/const.h,, @@ -196,7 +194,6 @@ Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, -Header,+,lib/toolbox/hmac_sha256.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, @@ -205,6 +202,7 @@ Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, Header,+,lib/toolbox/random_name.h,, Header,+,lib/toolbox/saved_struct.h,, +Header,+,lib/toolbox/sha256.h,, Header,+,lib/toolbox/stream/buffered_file_stream.h,, Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, @@ -708,6 +706,13 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,-,compress_alloc,Compress*,uint16_t +Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,compress_free,void,Compress* +Function,-,compress_icon_alloc,CompressIcon*, +Function,-,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,-,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" @@ -1078,12 +1083,6 @@ Function,-,furi_hal_clock_resume_tick,void, Function,-,furi_hal_clock_suspend_tick,void, Function,-,furi_hal_clock_switch_to_hsi,void, Function,-,furi_hal_clock_switch_to_pll,void, -Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t -Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,furi_hal_compress_free,void,FuriHalCompress* -Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**" -Function,-,furi_hal_compress_icon_init,void, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, Function,+,furi_hal_console_init,void, @@ -1638,9 +1637,6 @@ Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" Function,+,hal_sd_detect,_Bool, Function,+,hal_sd_detect_init,void, Function,+,hal_sd_detect_set_low,void, -Function,+,hmac_sha256_finish,void,"const hmac_sha256_context*, const uint8_t*, uint8_t*" -Function,+,hmac_sha256_init,void,"hmac_sha256_context*, const uint8_t*" -Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, unsigned" Function,-,hypot,double,"double, double" Function,-,hypotf,float,"float, float" Function,-,hypotl,long double,"long double, long double" @@ -2109,8 +2105,8 @@ Function,+,onewire_host_read_bit,_Bool,OneWireHost* Function,+,onewire_host_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t" Function,+,onewire_host_reset,_Bool,OneWireHost* Function,+,onewire_host_reset_search,void,OneWireHost* -Function,+,onewire_host_search,uint8_t,"OneWireHost*, uint8_t*, OneWireHostSearchMode" -Function,+,onewire_host_skip,void,OneWireHost* +Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode" +Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool" Function,+,onewire_host_start,void,OneWireHost* Function,+,onewire_host_stop,void,OneWireHost* Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t" @@ -2124,6 +2120,7 @@ Function,+,onewire_slave_receive_bit,_Bool,OneWireSlave* Function,+,onewire_slave_send,_Bool,"OneWireSlave*, const uint8_t*, size_t" Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool" Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*" +Function,+,onewire_slave_set_overdrive,void,"OneWireSlave*, _Bool" Function,+,onewire_slave_set_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*" Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*" Function,+,onewire_slave_start,void,OneWireSlave* diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 3fc19c8192..c9b4c80887 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -8,29 +8,20 @@ void furi_hal_init_early() { furi_hal_cortex_init_early(); - furi_hal_clock_init_early(); - furi_hal_resources_init_early(); - furi_hal_os_init(); - furi_hal_spi_config_init_early(); - furi_hal_i2c_init_early(); furi_hal_light_init(); - furi_hal_rtc_init_early(); } void furi_hal_deinit_early() { furi_hal_rtc_deinit_early(); - furi_hal_i2c_deinit_early(); furi_hal_spi_config_deinit_early(); - furi_hal_resources_deinit_early(); - furi_hal_clock_deinit_early(); } @@ -39,40 +30,23 @@ void furi_hal_init() { furi_hal_clock_init(); furi_hal_console_init(); furi_hal_rtc_init(); - furi_hal_interrupt_init(); - furi_hal_flash_init(); - furi_hal_resources_init(); - FURI_LOG_I(TAG, "GPIO OK"); - furi_hal_version_init(); - furi_hal_spi_config_init(); furi_hal_spi_dma_init(); - furi_hal_ibutton_init(); - FURI_LOG_I(TAG, "iButton OK"); furi_hal_speaker_init(); - FURI_LOG_I(TAG, "Speaker OK"); - furi_hal_crypto_init(); - furi_hal_i2c_init(); - - // High Level furi_hal_power_init(); furi_hal_light_init(); - furi_hal_bt_init(); furi_hal_memory_init(); - furi_hal_compress_icon_init(); #ifndef FURI_RAM_EXEC - // USB furi_hal_usb_init(); - FURI_LOG_I(TAG, "USB OK"); furi_hal_vibro_init(); furi_hal_subghz_init(); furi_hal_nfc_init(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c index c05cd69a82..f19fd0a0ef 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c @@ -7,6 +7,7 @@ #include +#define TAG "FuriHalIbutton" #define FURI_HAL_IBUTTON_TIMER TIM1 #define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16 @@ -33,6 +34,8 @@ static void furi_hal_ibutton_emulate_isr() { void furi_hal_ibutton_init() { furi_hal_ibutton = malloc(sizeof(FuriHalIbutton)); furi_hal_ibutton->state = FuriHalIbuttonStateIdle; + + FURI_LOG_I(TAG, "Init OK"); } void furi_hal_ibutton_emulate_start( diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 4e63263cfc..5cf44a6a8a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -4,6 +4,8 @@ #include #include +#define TAG "FuriHalResources" + const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; @@ -195,6 +197,8 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); + + FURI_LOG_I(TAG, "Init OK"); } int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.c b/firmware/targets/f7/furi_hal/furi_hal_uart.c index 54232e67fa..71b5c7ba04 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_uart.c +++ b/firmware/targets/f7/furi_hal/furi_hal_uart.c @@ -44,7 +44,8 @@ static void furi_hal_usart_init(uint32_t baud) { while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) ; - LL_USART_EnableIT_RXNE_RXFNE(USART1); + LL_USART_DisableIT_ERROR(USART1); + NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); } @@ -79,8 +80,8 @@ static void furi_hal_lpuart_init(uint32_t baud) { ; furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); + LL_LPUART_DisableIT_ERROR(LPUART1); - LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); } @@ -190,19 +191,25 @@ void furi_hal_uart_set_irq_cb( void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), void* ctx) { if(cb == NULL) { - if(ch == FuriHalUartIdUSART1) + if(ch == FuriHalUartIdUSART1) { NVIC_DisableIRQ(USART1_IRQn); - else if(ch == FuriHalUartIdLPUART1) + LL_USART_DisableIT_RXNE_RXFNE(USART1); + } else if(ch == FuriHalUartIdLPUART1) { NVIC_DisableIRQ(LPUART1_IRQn); + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + } irq_cb[ch] = cb; irq_ctx[ch] = ctx; } else { irq_ctx[ch] = ctx; irq_cb[ch] = cb; - if(ch == FuriHalUartIdUSART1) + if(ch == FuriHalUartIdUSART1) { NVIC_EnableIRQ(USART1_IRQn); - else if(ch == FuriHalUartIdLPUART1) + LL_USART_EnableIT_RXNE_RXFNE(USART1); + } else if(ch == FuriHalUartIdLPUART1) { NVIC_EnableIRQ(LPUART1_IRQn); + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + } } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index 5cb7fd2983..d27613410b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -8,7 +8,6 @@ #include "usb_hid.h" #define HID_EP_IN 0x81 -#define HID_EP_OUT 0x01 #define HID_EP_SZ 0x10 #define HID_INTERVAL 2 @@ -16,17 +15,15 @@ #define HID_VID_DEFAULT 0x046D #define HID_PID_DEFAULT 0xC529 -struct HidIadDescriptor { - struct usb_iad_descriptor hid_iad; +struct HidIntfDescriptor { struct usb_interface_descriptor hid; struct usb_hid_descriptor hid_desc; struct usb_endpoint_descriptor hid_ep_in; - struct usb_endpoint_descriptor hid_ep_out; }; struct HidConfigDescriptor { struct usb_config_descriptor config; - struct HidIadDescriptor iad_0; + struct HidIntfDescriptor intf_0; } __attribute__((packed)); enum HidReportId { @@ -35,78 +32,98 @@ enum HidReportId { ReportIdConsumer = 3, }; -/* HID report: keyboard+mouse */ +/* HID report descriptor: keyboard + mouse + consumer control */ static const uint8_t hid_report_desc[] = { + // clang-format off HID_USAGE_PAGE(HID_PAGE_DESKTOP), HID_USAGE(HID_DESKTOP_KEYBOARD), HID_COLLECTION(HID_APPLICATION_COLLECTION), - HID_REPORT_ID(ReportIdKeyboard), - HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), - HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL), - HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI), - HID_LOGICAL_MINIMUM(0), - HID_LOGICAL_MAXIMUM(1), - HID_REPORT_SIZE(1), - HID_REPORT_COUNT(8), - HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - HID_REPORT_COUNT(1), - HID_REPORT_SIZE(8), - HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - HID_USAGE_PAGE(HID_PAGE_LED), - HID_REPORT_COUNT(8), - HID_REPORT_SIZE(1), - HID_USAGE_MINIMUM(1), - HID_USAGE_MAXIMUM(8), - HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - HID_REPORT_COUNT(HID_KB_MAX_KEYS), - HID_REPORT_SIZE(8), - HID_LOGICAL_MINIMUM(0), - HID_LOGICAL_MAXIMUM(101), - HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), - HID_USAGE_MINIMUM(0), - HID_USAGE_MAXIMUM(101), - HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_REPORT_ID(ReportIdKeyboard), + // Keyboard report + HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), + HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL), + HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(1), + HID_REPORT_SIZE(1), + HID_REPORT_COUNT(8), + // Input - Modifier keys byte + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_REPORT_COUNT(1), + HID_REPORT_SIZE(8), + // Input - Reserved byte + HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_USAGE_PAGE(HID_PAGE_LED), + HID_REPORT_COUNT(8), + HID_REPORT_SIZE(1), + HID_USAGE_MINIMUM(1), + HID_USAGE_MAXIMUM(8), + // Output - LEDs + HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_REPORT_COUNT(HID_KB_MAX_KEYS), + HID_REPORT_SIZE(8), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(101), + HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), + HID_USAGE_MINIMUM(0), + HID_USAGE_MAXIMUM(101), + // Input - Key codes + HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), HID_END_COLLECTION, + HID_USAGE_PAGE(HID_PAGE_DESKTOP), HID_USAGE(HID_DESKTOP_MOUSE), HID_COLLECTION(HID_APPLICATION_COLLECTION), - HID_USAGE(HID_DESKTOP_POINTER), - HID_COLLECTION(HID_PHYSICAL_COLLECTION), - HID_REPORT_ID(ReportIdMouse), - HID_USAGE_PAGE(HID_PAGE_BUTTON), - HID_USAGE_MINIMUM(1), - HID_USAGE_MAXIMUM(3), - HID_LOGICAL_MINIMUM(0), - HID_LOGICAL_MAXIMUM(1), - HID_REPORT_COUNT(3), - HID_REPORT_SIZE(1), - HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - HID_REPORT_SIZE(1), - HID_REPORT_COUNT(5), - HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - HID_USAGE_PAGE(HID_PAGE_DESKTOP), - HID_USAGE(HID_DESKTOP_X), - HID_USAGE(HID_DESKTOP_Y), - HID_USAGE(HID_DESKTOP_WHEEL), - HID_LOGICAL_MINIMUM(-127), - HID_LOGICAL_MAXIMUM(127), - HID_REPORT_SIZE(8), - HID_REPORT_COUNT(3), - HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), - HID_END_COLLECTION, + HID_USAGE(HID_DESKTOP_POINTER), + HID_COLLECTION(HID_PHYSICAL_COLLECTION), + HID_REPORT_ID(ReportIdMouse), + // Mouse report + HID_USAGE_PAGE(HID_PAGE_BUTTON), + HID_USAGE_MINIMUM(1), + HID_USAGE_MAXIMUM(3), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(1), + HID_REPORT_COUNT(3), + HID_REPORT_SIZE(1), + // Input - Mouse keys + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_REPORT_SIZE(1), + HID_REPORT_COUNT(5), + // Input - Mouse keys padding + HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_USAGE_PAGE(HID_PAGE_DESKTOP), + HID_USAGE(HID_DESKTOP_X), + HID_USAGE(HID_DESKTOP_Y), + HID_USAGE(HID_DESKTOP_WHEEL), + HID_LOGICAL_MINIMUM(-127), + HID_LOGICAL_MAXIMUM(127), + HID_REPORT_SIZE(8), + HID_REPORT_COUNT(3), + // Input - Mouse movement data (x, y, scroll) + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), + HID_END_COLLECTION, HID_END_COLLECTION, + HID_USAGE_PAGE(HID_PAGE_CONSUMER), HID_USAGE(HID_CONSUMER_CONTROL), HID_COLLECTION(HID_APPLICATION_COLLECTION), - HID_REPORT_ID(ReportIdConsumer), - HID_LOGICAL_MINIMUM(0), - HID_RI_LOGICAL_MAXIMUM(16, 0x3FF), - HID_USAGE_MINIMUM(0), - HID_RI_USAGE_MAXIMUM(16, 0x3FF), - HID_REPORT_COUNT(HID_CONSUMER_MAX_KEYS), - HID_REPORT_SIZE(16), - HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_REPORT_ID(ReportIdConsumer), + // Consumer report + HID_LOGICAL_MINIMUM(0), + HID_RI_LOGICAL_MAXIMUM(16, 0x3FF), + HID_USAGE_MINIMUM(0), + HID_RI_USAGE_MAXIMUM(16, 0x3FF), + HID_REPORT_COUNT(HID_CONSUMER_MAX_KEYS), + HID_REPORT_SIZE(16), + // Input - Consumer control keys + HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), HID_END_COLLECTION, + // clang-format on }; /* Device descriptor */ @@ -114,9 +131,9 @@ static struct usb_device_descriptor hid_device_desc = { .bLength = sizeof(struct usb_device_descriptor), .bDescriptorType = USB_DTYPE_DEVICE, .bcdUSB = VERSION_BCD(2, 0, 0), - .bDeviceClass = USB_CLASS_IAD, - .bDeviceSubClass = USB_SUBCLASS_IAD, - .bDeviceProtocol = USB_PROTO_IAD, + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = USB_SUBCLASS_NONE, + .bDeviceProtocol = USB_PROTO_NONE, .bMaxPacketSize0 = USB_EP0_SIZE, .idVendor = HID_VID_DEFAULT, .idProduct = HID_PID_DEFAULT, @@ -140,29 +157,18 @@ static const struct HidConfigDescriptor hid_cfg_desc = { .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, .bMaxPower = USB_CFG_POWER_MA(100), }, - .iad_0 = + .intf_0 = { - .hid_iad = - { - .bLength = sizeof(struct usb_iad_descriptor), - .bDescriptorType = USB_DTYPE_INTERFASEASSOC, - .bFirstInterface = 0, - .bInterfaceCount = 1, - .bFunctionClass = USB_CLASS_PER_INTERFACE, - .bFunctionSubClass = USB_SUBCLASS_NONE, - .bFunctionProtocol = USB_PROTO_NONE, - .iFunction = NO_DESCRIPTOR, - }, .hid = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DTYPE_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, - .bNumEndpoints = 2, + .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT, - .bInterfaceProtocol = USB_HID_PROTO_NONBOOT, + .bInterfaceSubClass = USB_HID_SUBCLASS_BOOT, + .bInterfaceProtocol = USB_HID_PROTO_KEYBOARD, .iInterface = NO_DESCRIPTOR, }, .hid_desc = @@ -184,15 +190,6 @@ static const struct HidConfigDescriptor hid_cfg_desc = { .wMaxPacketSize = HID_EP_SZ, .bInterval = HID_INTERVAL, }, - .hid_ep_out = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_OUT, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = HID_EP_SZ, - .bInterval = HID_INTERVAL, - }, }, }; @@ -206,9 +203,11 @@ struct HidReportMouse { struct HidReportKB { uint8_t report_id; - uint8_t mods; - uint8_t reserved; - uint8_t btn[HID_KB_MAX_KEYS]; + struct { + uint8_t mods; + uint8_t reserved; + uint8_t btn[HID_KB_MAX_KEYS]; + } boot; } __attribute__((packed)); struct HidReportConsumer { @@ -256,6 +255,7 @@ static bool hid_connected = false; static HidStateCallback callback; static void* cb_ctx; static uint8_t led_state; +static bool boot_protocol = false; bool furi_hal_hid_is_connected() { return hid_connected; @@ -280,31 +280,31 @@ void furi_hal_hid_set_state_callback(HidStateCallback cb, void* ctx) { bool furi_hal_hid_kb_press(uint16_t button) { for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { - if(hid_report.keyboard.btn[key_nb] == 0) { - hid_report.keyboard.btn[key_nb] = button & 0xFF; + if(hid_report.keyboard.boot.btn[key_nb] == 0) { + hid_report.keyboard.boot.btn[key_nb] = button & 0xFF; break; } } - hid_report.keyboard.mods |= (button >> 8); + hid_report.keyboard.boot.mods |= (button >> 8); return hid_send_report(ReportIdKeyboard); } bool furi_hal_hid_kb_release(uint16_t button) { for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { - if(hid_report.keyboard.btn[key_nb] == (button & 0xFF)) { - hid_report.keyboard.btn[key_nb] = 0; + if(hid_report.keyboard.boot.btn[key_nb] == (button & 0xFF)) { + hid_report.keyboard.boot.btn[key_nb] = 0; break; } } - hid_report.keyboard.mods &= ~(button >> 8); + hid_report.keyboard.boot.mods &= ~(button >> 8); return hid_send_report(ReportIdKeyboard); } bool furi_hal_hid_kb_release_all() { for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { - hid_report.keyboard.btn[key_nb] = 0; + hid_report.keyboard.boot.btn[key_nb] = 0; } - hid_report.keyboard.mods = 0; + hid_report.keyboard.boot.mods = 0; return hid_send_report(ReportIdKeyboard); } @@ -434,27 +434,35 @@ static void hid_on_suspend(usbd_device* dev) { static bool hid_send_report(uint8_t report_id) { if((hid_semaphore == NULL) || (hid_connected == false)) return false; + if((boot_protocol == true) && (report_id != ReportIdKeyboard)) return false; furi_check(furi_semaphore_acquire(hid_semaphore, FuriWaitForever) == FuriStatusOk); - if(hid_connected == true) { + if(hid_connected == false) { + return false; + } + if(boot_protocol == true) { + usbd_ep_write( + usb_dev, HID_EP_IN, &hid_report.keyboard.boot, sizeof(hid_report.keyboard.boot)); + } else { if(report_id == ReportIdKeyboard) usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.keyboard, sizeof(hid_report.keyboard)); else if(report_id == ReportIdMouse) usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.mouse, sizeof(hid_report.mouse)); else if(report_id == ReportIdConsumer) usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.consumer, sizeof(hid_report.consumer)); - return true; } - return false; + return true; } static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(dev); if(event == usbd_evt_eptx) { furi_semaphore_release(hid_semaphore); + } else if(boot_protocol == true) { + usbd_ep_read(usb_dev, ep, &led_state, sizeof(led_state)); } else { struct HidReportLED leds; - usbd_ep_read(usb_dev, ep, &leds, 2); + usbd_ep_read(usb_dev, ep, &leds, sizeof(leds)); led_state = leds.led_state; } } @@ -464,18 +472,15 @@ static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { switch(cfg) { case 0: /* deconfiguring device */ - usbd_ep_deconfig(dev, HID_EP_OUT); usbd_ep_deconfig(dev, HID_EP_IN); - usbd_reg_endpoint(dev, HID_EP_OUT, 0); usbd_reg_endpoint(dev, HID_EP_IN, 0); return usbd_ack; case 1: /* configuring device */ usbd_ep_config(dev, HID_EP_IN, USB_EPTYPE_INTERRUPT, HID_EP_SZ); - usbd_ep_config(dev, HID_EP_OUT, USB_EPTYPE_INTERRUPT, HID_EP_SZ); usbd_reg_endpoint(dev, HID_EP_IN, hid_txrx_ep_callback); - usbd_reg_endpoint(dev, HID_EP_OUT, hid_txrx_ep_callback); usbd_ep_write(dev, HID_EP_IN, 0, 0); + boot_protocol = false; /* BIOS will SET_PROTOCOL if it wants this */ return usbd_ack; default: return usbd_fail; @@ -493,8 +498,21 @@ static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal case USB_HID_SETIDLE: return usbd_ack; case USB_HID_GETREPORT: - dev->status.data_ptr = &hid_report; - dev->status.data_count = sizeof(hid_report); + if(boot_protocol == true) { + dev->status.data_ptr = &hid_report.keyboard.boot; + dev->status.data_count = sizeof(hid_report.keyboard.boot); + } else { + dev->status.data_ptr = &hid_report; + dev->status.data_count = sizeof(hid_report); + } + return usbd_ack; + case USB_HID_SETPROTOCOL: + if(req->wValue == 0) + boot_protocol = true; + else if(req->wValue == 1) + boot_protocol = false; + else + return usbd_fail; return usbd_ack; default: return usbd_fail; @@ -505,10 +523,11 @@ static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) { switch(req->wValue >> 8) { case USB_DTYPE_HID: - dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.iad_0.hid_desc); - dev->status.data_count = sizeof(hid_cfg_desc.iad_0.hid_desc); + dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.intf_0.hid_desc); + dev->status.data_count = sizeof(hid_cfg_desc.intf_0.hid_desc); return usbd_ack; case USB_DTYPE_HID_REPORT: + boot_protocol = false; /* BIOS does not read this */ dev->status.data_ptr = (uint8_t*)hid_report_desc; dev->status.data_count = sizeof(hid_report_desc); return usbd_ack; diff --git a/firmware/targets/f7/src/dfu.c b/firmware/targets/f7/src/dfu.c index f32ac2ac48..b060bc8d26 100644 --- a/firmware/targets/f7/src/dfu.c +++ b/firmware/targets/f7/src/dfu.c @@ -4,10 +4,11 @@ #include #include #include +#include void flipper_boot_dfu_show_splash() { // Initialize - furi_hal_compress_icon_init(); + CompressIcon* compress_icon = compress_icon_alloc(); u8g2_t* fb = malloc(sizeof(u8g2_t)); memset(fb, 0, sizeof(u8g2_t)); @@ -15,13 +16,15 @@ void flipper_boot_dfu_show_splash() { u8g2_InitDisplay(fb); u8g2_SetDrawColor(fb, 0x01); uint8_t* splash_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(&I_DFU_128x50), &splash_data); + compress_icon_decode(compress_icon, icon_get_data(&I_DFU_128x50), &splash_data); u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data); u8g2_SetFont(fb, u8g2_font_helvB08_tr); u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode"); u8g2_DrawStr(fb, 2, 21, "DFU Started"); u8g2_SetPowerSave(fb, 0); u8g2_SendBuffer(fb); + + compress_icon_free(compress_icon); } void flipper_boot_dfu_exec() { diff --git a/firmware/targets/f7/src/recovery.c b/firmware/targets/f7/src/recovery.c index db538b0d51..d037e8118f 100644 --- a/firmware/targets/f7/src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -4,6 +4,7 @@ #include #include #include +#include #define COUNTER_VALUE (136U) @@ -27,9 +28,9 @@ void flipper_boot_recovery_exec() { u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); u8g2_InitDisplay(fb); - furi_hal_compress_icon_init(); + CompressIcon* compress_icon = compress_icon_alloc(); uint8_t* splash_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(&I_Erase_pin_128x64), &splash_data); + compress_icon_decode(compress_icon, icon_get_data(&I_Erase_pin_128x64), &splash_data); u8g2_ClearBuffer(fb); u8g2_SetDrawColor(fb, 0x01); @@ -38,6 +39,7 @@ void flipper_boot_recovery_exec() { u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data); u8g2_SendBuffer(fb); u8g2_SetPowerSave(fb, 0); + compress_icon_free(compress_icon); size_t counter = COUNTER_VALUE; while(counter) { diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index ad4340dd40..2eb4688d42 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -33,7 +33,6 @@ struct STOP_EXTERNING_ME {}; #include #include #include -#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_compress.h b/firmware/targets/furi_hal_include/furi_hal_compress.h deleted file mode 100644 index f80aee5162..0000000000 --- a/firmware/targets/furi_hal_include/furi_hal_compress.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @file furi_hal_compress.h - * LZSS based compression HAL API - */ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Defines encoder and decoder window size */ -#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG (8) - -/** Defines encoder and decoder lookahead buffer size */ -#define FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4) - -/** FuriHalCompress control structure */ -typedef struct FuriHalCompress FuriHalCompress; - -/** Initialize icon decoder - */ -void furi_hal_compress_icon_init(); - -/** Icon decoder - * - * @param icon_data pointer to icon data - * @param decoded_buff pointer to decoded buffer - */ -void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff); - -/** Allocate encoder and decoder - * - * @param compress_buff_size size of decoder and encoder buffer to allocate - * - * @return FuriHalCompress instance - */ -FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size); - -/** Free encoder and decoder - * - * @param compress FuriHalCompress instance - */ -void furi_hal_compress_free(FuriHalCompress* compress); - -/** Encode data - * - * @param compress FuriHalCompress instance - * @param data_in pointer to input data - * @param data_in_size size of input data - * @param data_out maximum size of output data - * @param data_res_size pointer to result output data size - * - * @return true on success - */ -bool furi_hal_compress_encode( - FuriHalCompress* compress, - uint8_t* data_in, - size_t data_in_size, - uint8_t* data_out, - size_t data_out_size, - size_t* data_res_size); - -/** Decode data - * - * @param compress FuriHalCompress instance - * @param data_in pointer to input data - * @param data_in_size size of input data - * @param data_out maximum size of output data - * @param data_res_size pointer to result output data size - * - * @return true on success - */ -bool furi_hal_compress_decode( - FuriHalCompress* compress, - uint8_t* data_in, - size_t data_in_size, - uint8_t* data_out, - size_t data_out_size, - size_t* data_res_size); - -#ifdef __cplusplus -} -#endif diff --git a/lib/err.h b/lib/err.h new file mode 100644 index 0000000000..a0e93874e6 --- /dev/null +++ b/lib/err.h @@ -0,0 +1,4 @@ +#pragma once +#include + +#define err(...) FURI_LOG_E("Heatshrink", "Error: %d-%s", __VA_ARGS__) \ No newline at end of file diff --git a/lib/heatshrink b/lib/heatshrink new file mode 160000 index 0000000000..7398ccc916 --- /dev/null +++ b/lib/heatshrink @@ -0,0 +1 @@ +Subproject commit 7398ccc91652a33483245200cfa1a83b073bc206 diff --git a/lib/heatshrink/heatshrink_common.h b/lib/heatshrink/heatshrink_common.h deleted file mode 100644 index 243f447029..0000000000 --- a/lib/heatshrink/heatshrink_common.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HEATSHRINK_H -#define HEATSHRINK_H - -#define HEATSHRINK_AUTHOR "Scott Vokes " -#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" - -/* Version 0.4.1 */ -#define HEATSHRINK_VERSION_MAJOR 0 -#define HEATSHRINK_VERSION_MINOR 4 -#define HEATSHRINK_VERSION_PATCH 1 - -#define HEATSHRINK_MIN_WINDOW_BITS 4 -#define HEATSHRINK_MAX_WINDOW_BITS 15 - -#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3 - -#define HEATSHRINK_LITERAL_MARKER 0x01 -#define HEATSHRINK_BACKREF_MARKER 0x00 - -#endif diff --git a/lib/heatshrink/heatshrink_config.h b/lib/heatshrink/heatshrink_config.h deleted file mode 100644 index 7f2373c0d4..0000000000 --- a/lib/heatshrink/heatshrink_config.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -#include - -/* Should functionality assuming dynamic allocation be used? */ -#ifndef HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DYNAMIC_ALLOC 1 -#endif - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Optional replacement of malloc/free */ - #define HEATSHRINK_MALLOC(SZ) malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) free(P) -#else - /* Required parameters for static configuration */ - #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 1024 - #define HEATSHRINK_STATIC_WINDOW_BITS 8 - #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 -#endif - -/* Turn on logging for debugging. */ -#define HEATSHRINK_DEBUGGING_LOGS 0 - -/* Use indexing for faster compression. (This requires additional space.) */ -#define HEATSHRINK_USE_INDEX 1 - -#endif diff --git a/lib/heatshrink/heatshrink_decoder.c b/lib/heatshrink/heatshrink_decoder.c deleted file mode 100644 index 2878283675..0000000000 --- a/lib/heatshrink/heatshrink_decoder.c +++ /dev/null @@ -1,364 +0,0 @@ -#include -#include -#include "heatshrink_decoder.h" - -/* States for the polling state machine. */ -typedef enum { - HSDS_TAG_BIT, /* tag bit */ - HSDS_YIELD_LITERAL, /* ready to yield literal byte */ - HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ - HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ - HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ - HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ - HSDS_YIELD_BACKREF, /* ready to yield back-reference */ -} HSD_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "tag_bit", - "yield_literal", - "backref_index_msb", - "backref_index_lsb", - "backref_count_msb", - "backref_count_lsb", - "yield_backref", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define NO_BITS ((uint16_t)-1) - -/* Forward references. */ -static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count); -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_decoder *heatshrink_decoder_alloc(uint8_t* buffer, - uint16_t input_buffer_size, - uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (input_buffer_size == 0) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - size_t sz = sizeof(heatshrink_decoder); - heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); - if (hsd == NULL) { return NULL; } - hsd->input_buffer_size = input_buffer_size; - hsd->window_sz2 = window_sz2; - hsd->lookahead_sz2 = lookahead_sz2; - hsd->buffers = buffer; - heatshrink_decoder_reset(hsd); - LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", - sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); - return hsd; -} - -void heatshrink_decoder_free(heatshrink_decoder *hsd) { - size_t sz = sizeof(heatshrink_decoder); - HEATSHRINK_FREE(hsd, sz); - (void)sz; /* may not be used by free */ -} -#endif - -void heatshrink_decoder_reset(heatshrink_decoder *hsd) { - hsd->state = HSDS_TAG_BIT; - hsd->input_size = 0; - hsd->input_index = 0; - hsd->bit_index = 0x00; - hsd->current_byte = 0x00; - hsd->output_count = 0; - hsd->output_index = 0; - hsd->head_index = 0; -} - -/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSDR_SINK_ERROR_NULL; - } - - size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; - if (rem == 0) { - *input_size = 0; - return HSDR_SINK_FULL; - } - - size = rem < size ? rem : size; - LOG("-- sinking %zd bytes\n", size); - /* copy into input buffer (at head of buffers) */ - memcpy(&hsd->buffers[hsd->input_size], in_buf, size); - hsd->input_size += size; - *input_size = size; - return HSDR_SINK_OK; -} - - -/***************** - * Decompression * - *****************/ - -#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) -#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) - -// States -static HSD_state st_tag_bit(heatshrink_decoder *hsd); -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi); -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi); - -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSDR_POLL_ERROR_NULL; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- poll, state is %d (%s), input_size %d\n", - hsd->state, state_names[hsd->state], hsd->input_size); - uint8_t in_state = hsd->state; - switch (in_state) { - case HSDS_TAG_BIT: - hsd->state = st_tag_bit(hsd); - break; - case HSDS_YIELD_LITERAL: - hsd->state = st_yield_literal(hsd, &oi); - break; - case HSDS_BACKREF_INDEX_MSB: - hsd->state = st_backref_index_msb(hsd); - break; - case HSDS_BACKREF_INDEX_LSB: - hsd->state = st_backref_index_lsb(hsd); - break; - case HSDS_BACKREF_COUNT_MSB: - hsd->state = st_backref_count_msb(hsd); - break; - case HSDS_BACKREF_COUNT_LSB: - hsd->state = st_backref_count_lsb(hsd); - break; - case HSDS_YIELD_BACKREF: - hsd->state = st_yield_backref(hsd, &oi); - break; - default: - return HSDR_POLL_ERROR_UNKNOWN; - } - - /* If the current state cannot advance, check if input or output - * buffer are exhausted. */ - if (hsd->state == in_state) { - if (*output_size == out_buf_size) { return HSDR_POLL_MORE; } - return HSDR_POLL_EMPTY; - } - } -} - -static HSD_state st_tag_bit(heatshrink_decoder *hsd) { - uint32_t bits = get_bits(hsd, 1); // get tag bit - if (bits == NO_BITS) { - return HSDS_TAG_BIT; - } else if (bits) { - return HSDS_YIELD_LITERAL; - } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { - return HSDS_BACKREF_INDEX_MSB; - } else { - hsd->output_index = 0; - return HSDS_BACKREF_INDEX_LSB; - } -} - -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi) { - /* Emit a repeated section from the window buffer, and add it (again) - * to the window buffer. (Note that the repetition can include - * itself.)*/ - if (*oi->output_size < oi->buf_size) { - uint16_t byte = get_bits(hsd, 8); - if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */ - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint8_t c = byte & 0xFF; - LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); - buf[hsd->head_index++ & mask] = c; - push_byte(hsd, oi, c); - return HSDS_TAG_BIT; - } else { - return HSDS_YIELD_LITERAL; - } -} - -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - ASSERT(bit_ct > 8); - uint16_t bits = get_bits(hsd, bit_ct - 8); - LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; } - hsd->output_index = bits << 8; - return HSDS_BACKREF_INDEX_LSB; -} - -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); - LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; } - hsd->output_index |= bits; - hsd->output_index++; - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - hsd->output_count = 0; - return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - ASSERT(br_bit_ct > 8); - uint16_t bits = get_bits(hsd, br_bit_ct - 8); - LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; } - hsd->output_count = bits << 8; - return HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); - LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; } - hsd->output_count |= bits; - hsd->output_count++; - return HSDS_YIELD_BACKREF; -} - -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi) { - size_t count = oi->buf_size - *oi->output_size; - if (count > 0) { - size_t i = 0; - if (hsd->output_count < count) count = hsd->output_count; - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint16_t neg_offset = hsd->output_index; - LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); - ASSERT(neg_offset <= mask + 1); - ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd))); - - for (i=0; ihead_index - neg_offset) & mask]; - push_byte(hsd, oi, c); - buf[hsd->head_index & mask] = c; - hsd->head_index++; - LOG(" -- ++ 0x%02x\n", c); - } - hsd->output_count -= count; - if (hsd->output_count == 0) { return HSDS_TAG_BIT; } - } - return HSDS_YIELD_BACKREF; -} - -/* Get the next COUNT bits from the input buffer, saving incremental progress. - * Returns NO_BITS on end of input, or if more than 15 bits are requested. */ -static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) { - uint16_t accumulator = 0; - int i = 0; - if (count > 15) { return NO_BITS; } - LOG("-- popping %u bit(s)\n", count); - - /* If we aren't able to get COUNT bits, suspend immediately, because we - * don't track how many bits of COUNT we've accumulated before suspend. */ - if (hsd->input_size == 0) { - if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; } - } - - for (i = 0; i < count; i++) { - if (hsd->bit_index == 0x00) { - if (hsd->input_size == 0) { - LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", - accumulator, accumulator); - return NO_BITS; - } - hsd->current_byte = hsd->buffers[hsd->input_index++]; - LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); - if (hsd->input_index == hsd->input_size) { - hsd->input_index = 0; /* input is exhausted */ - hsd->input_size = 0; - } - hsd->bit_index = 0x80; - } - accumulator <<= 1; - if (hsd->current_byte & hsd->bit_index) { - accumulator |= 0x01; - if (0) { - LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", - accumulator, hsd->bit_index); - } - } else { - if (0) { - LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", - accumulator, hsd->bit_index); - } - } - hsd->bit_index >>= 1; - } - - if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); } - return accumulator; -} - -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { - if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } - switch (hsd->state) { - case HSDS_TAG_BIT: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If we want to finish with no input, but are in these states, it's - * because the 0-bit padding to the last byte looks like a backref - * marker bit followed by all 0s for index and count bits. */ - case HSDS_BACKREF_INDEX_LSB: - case HSDS_BACKREF_INDEX_MSB: - case HSDS_BACKREF_COUNT_LSB: - case HSDS_BACKREF_COUNT_MSB: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If the output stream is padded with 0xFFs (possibly due to being in - * flash memory), also explicitly check the input size rather than - * uselessly returning MORE but yielding 0 bytes when polling. */ - case HSDS_YIELD_LITERAL: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - default: - return HSDR_FINISH_MORE; - } -} - -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) { - LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); - oi->buf[(*oi->output_size)++] = byte; - (void)hsd; -} diff --git a/lib/heatshrink/heatshrink_decoder.h b/lib/heatshrink/heatshrink_decoder.h deleted file mode 100644 index 687b0806b4..0000000000 --- a/lib/heatshrink/heatshrink_decoder.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef HEATSHRINK_DECODER_H -#define HEATSHRINK_DECODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSDR_SINK_OK, /* data sunk, ready to poll */ - HSDR_SINK_FULL, /* out of space in internal buffer */ - HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ -} HSD_sink_res; - -typedef enum { - HSDR_POLL_EMPTY, /* input exhausted */ - HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ - HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ - HSDR_POLL_ERROR_UNKNOWN=-2, -} HSD_poll_res; - -typedef enum { - HSDR_FINISH_DONE, /* output is done */ - HSDR_FINISH_MORE, /* more output remains */ - HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ -} HSD_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ - ((BUF)->input_buffer_size) -#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ - ((BUF)->window_sz2) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - ((BUF)->lookahead_sz2) -#else -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ - HEATSHRINK_STATIC_INPUT_BUFFER_SIZE -#define HEATSHRINK_DECODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t input_index; /* offset to next unprocessed input byte */ - uint16_t output_count; /* how many bytes to output */ - uint16_t output_index; /* index for bytes to output */ - uint16_t head_index; /* head of window buffer */ - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of input */ - uint8_t bit_index; /* current bit index */ - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Fields that are only used if dynamically allocated. */ - uint8_t window_sz2; /* window buffer bits */ - uint8_t lookahead_sz2; /* lookahead bits */ - uint16_t input_buffer_size; /* input buffer size */ - - /* Input buffer, then expansion window buffer */ - uint8_t* buffers; -#else - /* Input buffer, then expansion window buffer */ - uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) - + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; -#endif -} heatshrink_decoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, - * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead - * size of 2^lookahead_sz2. (The window buffer and lookahead sizes - * must match the settings used when the data was compressed.) - * Returns NULL on error. */ -heatshrink_decoder *heatshrink_decoder_alloc(uint8_t* buffer, uint16_t input_buffer_size, - uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); - -/* Free a decoder. */ -void heatshrink_decoder_free(heatshrink_decoder *hsd); -#endif - -/* Reset a decoder. */ -void heatshrink_decoder_reset(heatshrink_decoder *hsd); - -/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to - * indicate how many bytes were actually sunk (in case a buffer was filled). */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the dencoder that the input stream is finished. - * If the return value is HSDR_FINISH_MORE, there is still more output, so - * call heatshrink_decoder_poll and repeat. */ -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); - -#endif diff --git a/lib/heatshrink/heatshrink_encoder.c b/lib/heatshrink/heatshrink_encoder.c deleted file mode 100644 index 98f27dff87..0000000000 --- a/lib/heatshrink/heatshrink_encoder.c +++ /dev/null @@ -1,602 +0,0 @@ -#include -#include -#include -#include "heatshrink_encoder.h" - -typedef enum { - HSES_NOT_FULL, /* input buffer not full enough */ - HSES_FILLED, /* buffer is full */ - HSES_SEARCH, /* searching for patterns */ - HSES_YIELD_TAG_BIT, /* yield tag bit */ - HSES_YIELD_LITERAL, /* emit literal byte */ - HSES_YIELD_BR_INDEX, /* yielding backref index */ - HSES_YIELD_BR_LENGTH, /* yielding backref length */ - HSES_SAVE_BACKLOG, /* copying buffer to backlog */ - HSES_FLUSH_BITS, /* flush bit buffer */ - HSES_DONE, /* done */ -} HSE_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "not_full", - "filled", - "search", - "yield_tag_bit", - "yield_literal", - "yield_br_index", - "yield_br_length", - "save_backlog", - "flush_bits", - "done", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -// Encoder flags -enum { - FLAG_IS_FINISHING = 0x01, -}; - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define MATCH_NOT_FOUND ((uint16_t)-1) - -static uint16_t get_input_offset(heatshrink_encoder *hse); -static uint16_t get_input_buffer_size(heatshrink_encoder *hse); -static uint16_t get_lookahead_size(heatshrink_encoder *hse); -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); -static int can_take_byte(output_info *oi); -static int is_finishing(heatshrink_encoder *hse); -static void save_backlog(heatshrink_encoder *hse); - -/* Push COUNT (max 8) bits to the output buffer, which has room. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi); -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - - /* Note: 2 * the window size is used because the buffer needs to fit - * (1 << window_sz2) bytes for the current input, and an additional - * (1 << window_sz2) bytes for the previous buffer of input, which - * will be scanned for useful backreferences. */ - size_t buf_sz = (2 << window_sz2); - - heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse)); - if (hse == NULL) { return NULL; } - hse->window_sz2 = window_sz2; - hse->lookahead_sz2 = lookahead_sz2; - hse->buffer = buffer; - heatshrink_encoder_reset(hse); - -#if HEATSHRINK_USE_INDEX - size_t index_sz = buf_sz*sizeof(uint16_t); - hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); - if (hse->search_index == NULL) { - HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); - return NULL; - } - hse->search_index->size = index_sz; -#endif - - LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", - buf_sz, get_input_buffer_size(hse)); - return hse; -} - -void heatshrink_encoder_free(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; - HEATSHRINK_FREE(hse->search_index, index_sz); - (void)index_sz; -#endif - HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder)); -} -#endif - -void heatshrink_encoder_reset(heatshrink_encoder *hse) { - hse->input_size = 0; - hse->state = HSES_NOT_FULL; - hse->match_scan_index = 0; - hse->flags = 0; - hse->bit_index = 0x80; - hse->current_byte = 0x00; - hse->match_length = 0; - - hse->outgoing_bits = 0x0000; - hse->outgoing_bits_count = 0; - - #ifdef LOOP_DETECT - hse->loop_detect = (uint32_t)-1; - #endif -} - -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSER_SINK_ERROR_NULL; - } - - /* Sinking more content after saying the content is done, tsk tsk */ - if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } - - /* Sinking more content before processing is done */ - if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } - - uint16_t write_offset = get_input_offset(hse) + hse->input_size; - uint16_t ibs = get_input_buffer_size(hse); - uint16_t rem = ibs - hse->input_size; - uint16_t cp_sz = rem < size ? rem : size; - - memcpy(&hse->buffer[write_offset], in_buf, cp_sz); - *input_size = cp_sz; - hse->input_size += cp_sz; - - LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", - cp_sz, size, write_offset, hse->input_size); - if (cp_sz == rem) { - LOG("-- internal buffer is now full\n"); - hse->state = HSES_FILLED; - } - - return HSER_SINK_OK; -} - - -/*************** - * Compression * - ***************/ - -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length); -static void do_indexing(heatshrink_encoder *hse); - -static HSE_state st_step_search(heatshrink_encoder *hse); -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_save_backlog(heatshrink_encoder *hse); -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi); - -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSER_POLL_ERROR_NULL; - } - if (out_buf_size == 0) { - LOG("-- MISUSE: output buffer size is 0\n"); - return HSER_POLL_ERROR_MISUSE; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- polling, state %u (%s), flags 0x%02x\n", - hse->state, state_names[hse->state], hse->flags); - - uint8_t in_state = hse->state; - switch (in_state) { - case HSES_NOT_FULL: - return HSER_POLL_EMPTY; - case HSES_FILLED: - do_indexing(hse); - hse->state = HSES_SEARCH; - break; - case HSES_SEARCH: - hse->state = st_step_search(hse); - break; - case HSES_YIELD_TAG_BIT: - hse->state = st_yield_tag_bit(hse, &oi); - break; - case HSES_YIELD_LITERAL: - hse->state = st_yield_literal(hse, &oi); - break; - case HSES_YIELD_BR_INDEX: - hse->state = st_yield_br_index(hse, &oi); - break; - case HSES_YIELD_BR_LENGTH: - hse->state = st_yield_br_length(hse, &oi); - break; - case HSES_SAVE_BACKLOG: - hse->state = st_save_backlog(hse); - break; - case HSES_FLUSH_BITS: - hse->state = st_flush_bit_buffer(hse, &oi); - /* fall through */ - case HSES_DONE: - return HSER_POLL_EMPTY; - default: - LOG("-- bad state %s\n", state_names[hse->state]); - return HSER_POLL_ERROR_MISUSE; - } - - if (hse->state == in_state) { - /* Check if output buffer is exhausted. */ - if (*output_size == out_buf_size) return HSER_POLL_MORE; - } - } -} - -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { - if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } - LOG("-- setting is_finishing flag\n"); - hse->flags |= FLAG_IS_FINISHING; - if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } - return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; -} - -static HSE_state st_step_search(heatshrink_encoder *hse) { - uint16_t window_length = get_input_buffer_size(hse); - uint16_t lookahead_sz = get_lookahead_size(hse); - uint16_t msi = hse->match_scan_index; - LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", - msi, hse->input_size + msi, 2*window_length, hse->input_size); - - bool fin = is_finishing(hse); - if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) { - /* Current search buffer is exhausted, copy it into the - * backlog and await more input. */ - LOG("-- end of search @ %d\n", msi); - return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG; - } - - uint16_t input_offset = get_input_offset(hse); - uint16_t end = input_offset + msi; - uint16_t start = end - window_length; - - uint16_t max_possible = lookahead_sz; - if (hse->input_size - msi < lookahead_sz) { - max_possible = hse->input_size - msi; - } - - uint16_t match_length = 0; - uint16_t match_pos = find_longest_match(hse, - start, end, max_possible, &match_length); - - if (match_pos == MATCH_NOT_FOUND) { - LOG("ss Match not found\n"); - hse->match_scan_index++; - hse->match_length = 0; - return HSES_YIELD_TAG_BIT; - } else { - LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); - hse->match_pos = match_pos; - hse->match_length = match_length; - ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/); - - return HSES_YIELD_TAG_BIT; - } -} - -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - if (hse->match_length == 0) { - add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); - return HSES_YIELD_LITERAL; - } else { - add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); - hse->outgoing_bits = hse->match_pos - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); - return HSES_YIELD_BR_INDEX; - } - } else { - return HSES_YIELD_TAG_BIT; /* output is full, continue */ - } -} - -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - push_literal_byte(hse, oi); - return HSES_SEARCH; - } else { - return HSES_YIELD_LITERAL; - } -} - -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref index %u\n", hse->match_pos); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_INDEX; /* continue */ - } else { - hse->outgoing_bits = hse->match_length - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); - return HSES_YIELD_BR_LENGTH; /* done */ - } - } else { - return HSES_YIELD_BR_INDEX; /* continue */ - } -} - -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref length %u\n", hse->match_length); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_LENGTH; - } else { - hse->match_scan_index += hse->match_length; - hse->match_length = 0; - return HSES_SEARCH; - } - } else { - return HSES_YIELD_BR_LENGTH; - } -} - -static HSE_state st_save_backlog(heatshrink_encoder *hse) { - LOG("-- saving backlog\n"); - save_backlog(hse); - return HSES_NOT_FULL; -} - -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi) { - if (hse->bit_index == 0x80) { - LOG("-- done!\n"); - return HSES_DONE; - } else if (can_take_byte(oi)) { - LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); - oi->buf[(*oi->output_size)++] = hse->current_byte; - LOG("-- done!\n"); - return HSES_DONE; - } else { - return HSES_FLUSH_BITS; - } -} - -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { - LOG("-- adding tag bit: %d\n", tag); - push_bits(hse, 1, tag, oi); -} - -static uint16_t get_input_offset(heatshrink_encoder *hse) { - return get_input_buffer_size(hse); -} - -static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - (void)hse; -} - -static uint16_t get_lookahead_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - (void)hse; -} - -static void do_indexing(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - /* Build an index array I that contains flattened linked lists - * for the previous instances of every byte in the buffer. - * - * For example, if buf[200] == 'x', then index[200] will either - * be an offset i such that buf[i] == 'x', or a negative offset - * to indicate end-of-list. This significantly speeds up matching, - * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. - * - * Future optimization options: - * 1. Since any negative value represents end-of-list, the other - * 15 bits could be used to improve the index dynamically. - * - * 2. Likewise, the last lookahead_sz bytes of the index will - * not be usable, so temporary data could be stored there to - * dynamically improve the index. - * */ - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t last[256]; - memset(last, 0xFF, sizeof(last)); - - uint8_t * const data = hse->buffer; - int16_t * const index = hsi->index; - - const uint16_t input_offset = get_input_offset(hse); - const uint16_t end = input_offset + hse->input_size; - - for (uint16_t i=0; iflags & FLAG_IS_FINISHING; -} - -static int can_take_byte(output_info *oi) { - return *oi->output_size < oi->buf_size; -} - -/* Return the longest match for the bytes at buf[end:end+maxlen] between - * buf[start] and buf[end-1]. If no match is found, return -1. */ -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length) { - LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", - end, end + maxlen, start, end + maxlen - 1, maxlen); - uint8_t *buf = hse->buffer; - - uint16_t match_maxlen = 0; - uint16_t match_index = MATCH_NOT_FOUND; - - uint16_t len = 0; - uint8_t * const needlepoint = &buf[end]; -#if HEATSHRINK_USE_INDEX - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t pos = hsi->index[end]; - - while (pos - (int16_t)start >= 0) { - uint8_t * const pospoint = &buf[pos]; - len = 0; - - /* Only check matches that will potentially beat the current maxlen. - * This is redundant with the index if match_maxlen is 0, but the - * added branch overhead to check if it == 0 seems to be worse. */ - if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { - pos = hsi->index[pos]; - continue; - } - - for (len = 1; len < maxlen; len++) { - if (pospoint[len] != needlepoint[len]) break; - } - - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* won't find better */ - } - pos = hsi->index[pos]; - } -#else - for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { - uint8_t * const pospoint = &buf[pos]; - if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) - && (*pospoint == *needlepoint)) { - for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", - pos + len, pospoint[len], needlepoint[len], start); - } - if (pospoint[len] != needlepoint[len]) { break; } - } - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* don't keep searching */ - } - } - } -#endif - - const size_t break_even_point = - (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + - HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - - /* Instead of comparing break_even_point against 8*match_maxlen, - * compare match_maxlen against break_even_point/8 to avoid - * overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and - * 3, respectively, break_even_point/8 will always be at least 1. */ - if (match_maxlen > (break_even_point / 8)) { - LOG("-- best match: %u bytes at -%u\n", - match_maxlen, end - match_index); - *match_length = match_maxlen; - return end - match_index; - } - LOG("-- none found\n"); - return MATCH_NOT_FOUND; -} - -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { - uint8_t count = 0; - uint8_t bits = 0; - if (hse->outgoing_bits_count > 8) { - count = 8; - bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); - } else { - count = hse->outgoing_bits_count; - bits = hse->outgoing_bits; - } - - if (count > 0) { - LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); - push_bits(hse, count, bits, oi); - hse->outgoing_bits_count -= count; - } - return count; -} - -/* Push COUNT (max 8) bits to the output buffer, which has room. - * Bytes are set from the lowest bits, up. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi) { - ASSERT(count <= 8); - LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); - - /* If adding a whole byte and at the start of a new output byte, - * just push it through whole and skip the bit IO loop. */ - if (count == 8 && hse->bit_index == 0x80) { - oi->buf[(*oi->output_size)++] = bits; - } else { - for (int i=count - 1; i>=0; i--) { - bool bit = bits & (1 << i); - if (bit) { hse->current_byte |= hse->bit_index; } - if (0) { - LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", - bit ? 1 : 0, hse->bit_index, hse->current_byte); - } - hse->bit_index >>= 1; - if (hse->bit_index == 0x00) { - hse->bit_index = 0x80; - LOG(" > pushing byte 0x%02x\n", hse->current_byte); - oi->buf[(*oi->output_size)++] = hse->current_byte; - hse->current_byte = 0x00; - } - } - } -} - -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { - uint16_t processed_offset = hse->match_scan_index - 1; - uint16_t input_offset = get_input_offset(hse) + processed_offset; - uint8_t c = hse->buffer[input_offset]; - LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", - c, isprint(c) ? c : '.', input_offset); - push_bits(hse, 8, c, oi); -} - -static void save_backlog(heatshrink_encoder *hse) { - size_t input_buf_sz = get_input_buffer_size(hse); - - uint16_t msi = hse->match_scan_index; - - /* Copy processed data to beginning of buffer, so it can be - * used for future matches. Don't bother checking whether the - * input is less than the maximum size, because if it isn't, - * we're done anyway. */ - uint16_t rem = input_buf_sz - msi; // unprocessed bytes - uint16_t shift_sz = input_buf_sz + rem; - - memmove(&hse->buffer[0], - &hse->buffer[input_buf_sz - rem], - shift_sz); - - hse->match_scan_index = 0; - hse->input_size -= input_buf_sz - rem; -} diff --git a/lib/heatshrink/heatshrink_encoder.h b/lib/heatshrink/heatshrink_encoder.h deleted file mode 100644 index e2ccb44c7c..0000000000 --- a/lib/heatshrink/heatshrink_encoder.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef HEATSHRINK_ENCODER_H -#define HEATSHRINK_ENCODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSER_SINK_OK, /* data sunk into input buffer */ - HSER_SINK_ERROR_NULL=-1, /* NULL argument */ - HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ -} HSE_sink_res; - -typedef enum { - HSER_POLL_EMPTY, /* input exhausted */ - HSER_POLL_MORE, /* poll again for more output */ - HSER_POLL_ERROR_NULL=-1, /* NULL argument */ - HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ -} HSE_poll_res; - -typedef enum { - HSER_FINISH_DONE, /* encoding is complete */ - HSER_FINISH_MORE, /* more output remaining; use poll */ - HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ -} HSE_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ - ((HSE)->window_sz2) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ - ((HSE)->lookahead_sz2) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - ((HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[]; -}; -#else -#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - (&(HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; -}; -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t match_scan_index; - uint16_t match_length; - uint16_t match_pos; - uint16_t outgoing_bits; /* enqueued outgoing bits */ - uint8_t outgoing_bits_count; - uint8_t flags; - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of output */ - uint8_t bit_index; /* current bit index */ -#if HEATSHRINK_DYNAMIC_ALLOC - uint8_t window_sz2; /* 2^n size of window */ - uint8_t lookahead_sz2; /* 2^n size of lookahead */ -#if HEATSHRINK_USE_INDEX - struct hs_index *search_index; -#endif - /* input buffer and / sliding window for expansion */ - uint8_t* buffer; -#else - #if HEATSHRINK_USE_INDEX - struct hs_index search_index; - #endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; -#endif -} heatshrink_encoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a new encoder struct and its buffers. - * Returns NULL on error. */ -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2, - uint8_t lookahead_sz2); - -/* Free an encoder. */ -void heatshrink_encoder_free(heatshrink_encoder *hse); -#endif - -/* Reset an encoder. */ -void heatshrink_encoder_reset(heatshrink_encoder *hse); - -/* Sink up to SIZE bytes from IN_BUF into the encoder. - * INPUT_SIZE is set to the number of bytes actually sunk (in case a - * buffer was filled.). */ -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the encoder that the input stream is finished. - * If the return value is HSER_FINISH_MORE, there is still more output, so - * call heatshrink_encoder_poll and repeat. */ -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); - -#endif diff --git a/lib/ibutton/protocols/dallas/dallas_common.h b/lib/ibutton/protocols/dallas/dallas_common.h index 7991a1f8ba..6f5ff7cc01 100644 --- a/lib/ibutton/protocols/dallas/dallas_common.h +++ b/lib/ibutton/protocols/dallas/dallas_common.h @@ -1,10 +1,10 @@ #pragma once -#include - #include #include +#include + #define DALLAS_COMMON_MANUFACTURER_NAME "Dallas" #define DALLAS_COMMON_CMD_READ_ROM 0x33U diff --git a/lib/ibutton/protocols/dallas/protocol_dallas_base.h b/lib/ibutton/protocols/dallas/protocol_dallas_base.h index b4edb2b20d..55e1099360 100644 --- a/lib/ibutton/protocols/dallas/protocol_dallas_base.h +++ b/lib/ibutton/protocols/dallas/protocol_dallas_base.h @@ -2,11 +2,11 @@ #include "../protocol_common_i.h" -#include - #include #include +#include + typedef bool (*iButtonProtocolDallasReadWriteFunc)(OneWireHost*, iButtonProtocolData*); typedef void (*iButtonProtocolDallasEmulateFunc)(OneWireSlave*, iButtonProtocolData*); typedef bool (*iButtonProtocolDallasSaveFunc)(FlipperFormat*, const iButtonProtocolData*); diff --git a/lib/ibutton/protocols/dallas/protocol_ds1420.c b/lib/ibutton/protocols/dallas/protocol_ds1420.c index ebfb700ce4..85e0145f47 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds1420.c +++ b/lib/ibutton/protocols/dallas/protocol_ds1420.c @@ -67,6 +67,14 @@ bool dallas_ds1420_write_blank(OneWireHost* host, iButtonProtocolData* protocol_ tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)); } +static bool dallas_ds1420_reset_callback(bool is_short, void* context) { + DS1420ProtocolData* data = context; + if(!is_short) { + onewire_slave_set_overdrive(data->state.bus, is_short); + } + return !is_short; +} + static bool dallas_ds1420_command_callback(uint8_t command, void* context) { furi_assert(context); DS1420ProtocolData* data = context; @@ -92,7 +100,7 @@ void dallas_ds1420_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data DS1420ProtocolData* data = protocol_data; data->state.bus = bus; - onewire_slave_set_reset_callback(bus, NULL, NULL); + onewire_slave_set_reset_callback(bus, dallas_ds1420_reset_callback, protocol_data); onewire_slave_set_command_callback(bus, dallas_ds1420_command_callback, protocol_data); } diff --git a/lib/ibutton/protocols/dallas/protocol_ds1971.c b/lib/ibutton/protocols/dallas/protocol_ds1971.c index eb5b330b79..a806acb22e 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds1971.c +++ b/lib/ibutton/protocols/dallas/protocol_ds1971.c @@ -53,7 +53,7 @@ const iButtonProtocolDallasBase ibutton_protocol_ds1971 = { .name = DS1971_FAMILY_NAME, .read = dallas_ds1971_read, - .write_blank = NULL, /* No data to write a blank */ + .write_blank = NULL, // TODO: Implement writing to blank .write_copy = dallas_ds1971_write_copy, .emulate = dallas_ds1971_emulate, .save = dallas_ds1971_save, @@ -76,7 +76,7 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d DS1971ProtocolData* data = protocol_data; onewire_host_reset(host); - onewire_host_skip(host); + onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); // Starting writing from address 0x0000 onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH); onewire_host_write(host, 0x00); @@ -87,7 +87,7 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d bool pad_valid = false; if(onewire_host_reset(host)) { pad_valid = true; - onewire_host_skip(host); + onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH); onewire_host_write(host, 0x00); @@ -103,7 +103,7 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d // Copy scratchpad to memory and confirm if(pad_valid) { if(onewire_host_reset(host)) { - onewire_host_skip(host); + onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH); onewire_host_write(host, DS1971_CMD_FINALIZATION); @@ -114,10 +114,16 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d return pad_valid; } -static void dallas_ds1971_reset_callback(void* context) { +static bool dallas_ds1971_reset_callback(bool is_short, void* context) { furi_assert(context); DS1971ProtocolData* data = context; - data->state.command_state = DallasCommonCommandStateIdle; + + if(!is_short) { + data->state.command_state = DallasCommonCommandStateIdle; + onewire_slave_set_overdrive(data->state.bus, is_short); + } + + return !is_short; } static bool dallas_ds1971_command_callback(uint8_t command, void* context) { diff --git a/lib/ibutton/protocols/dallas/protocol_ds1990.c b/lib/ibutton/protocols/dallas/protocol_ds1990.c index 0d9c937ee4..86d39f1bd8 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds1990.c +++ b/lib/ibutton/protocols/dallas/protocol_ds1990.c @@ -67,6 +67,14 @@ bool dallas_ds1990_write_blank(OneWireHost* host, iButtonProtocolData* protocol_ tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)); } +static bool dallas_ds1990_reset_callback(bool is_short, void* context) { + DS1990ProtocolData* data = context; + if(!is_short) { + onewire_slave_set_overdrive(data->state.bus, is_short); + } + return !is_short; +} + static bool dallas_ds1990_command_callback(uint8_t command, void* context) { furi_assert(context); DS1990ProtocolData* data = context; @@ -92,7 +100,7 @@ void dallas_ds1990_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data DS1990ProtocolData* data = protocol_data; data->state.bus = bus; - onewire_slave_set_reset_callback(bus, NULL, NULL); + onewire_slave_set_reset_callback(bus, dallas_ds1990_reset_callback, protocol_data); onewire_slave_set_command_callback(bus, dallas_ds1990_command_callback, protocol_data); } diff --git a/lib/ibutton/protocols/dallas/protocol_ds1992.c b/lib/ibutton/protocols/dallas/protocol_ds1992.c index 17d631259e..0b4d4b34f2 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds1992.c +++ b/lib/ibutton/protocols/dallas/protocol_ds1992.c @@ -87,10 +87,16 @@ bool dallas_ds1992_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d DS1992_SRAM_DATA_SIZE); } -static void dallas_ds1992_reset_callback(void* context) { +static bool dallas_ds1992_reset_callback(bool is_short, void* context) { furi_assert(context); DS1992ProtocolData* data = context; - data->state.command_state = DallasCommonCommandStateIdle; + + if(!is_short) { + data->state.command_state = DallasCommonCommandStateIdle; + onewire_slave_set_overdrive(data->state.bus, is_short); + } + + return !is_short; } static bool dallas_ds1992_command_callback(uint8_t command, void* context) { diff --git a/lib/ibutton/protocols/dallas/protocol_ds1996.c b/lib/ibutton/protocols/dallas/protocol_ds1996.c index 74a5792c66..5358b63e26 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds1996.c +++ b/lib/ibutton/protocols/dallas/protocol_ds1996.c @@ -63,24 +63,54 @@ const iButtonProtocolDallasBase ibutton_protocol_ds1996 = { bool dallas_ds1996_read(OneWireHost* host, iButtonProtocolData* protocol_data) { DS1996ProtocolData* data = protocol_data; - return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) && - dallas_common_read_mem(host, 0, data->sram_data, DS1996_SRAM_DATA_SIZE); + bool success = false; + + do { + if(!onewire_host_reset(host)) break; + if(!dallas_common_read_rom(host, &data->rom_data)) break; + if(!onewire_host_reset(host)) break; + + onewire_host_write(host, DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM); + onewire_host_set_overdrive(host, true); + + if(!dallas_common_read_mem(host, 0, data->sram_data, DS1996_SRAM_DATA_SIZE)) break; + success = true; + } while(false); + + onewire_host_set_overdrive(host, false); + return success; } bool dallas_ds1996_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) { DS1996ProtocolData* data = protocol_data; - return dallas_common_write_mem( - host, - DS1996_COPY_SCRATCH_TIMEOUT_US, - DS1996_SRAM_PAGE_SIZE, - data->sram_data, - DS1996_SRAM_DATA_SIZE); + bool success = false; + + do { + if(!onewire_host_reset(host)) break; + + onewire_host_write(host, DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM); + onewire_host_set_overdrive(host, true); + + if(!dallas_common_write_mem( + host, + DS1996_COPY_SCRATCH_TIMEOUT_US, + DS1996_SRAM_PAGE_SIZE, + data->sram_data, + DS1996_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + onewire_host_set_overdrive(host, false); + return success; } -static void dallas_ds1996_reset_callback(void* context) { +static bool dallas_ds1996_reset_callback(bool is_short, void* context) { furi_assert(context); DS1996ProtocolData* data = context; data->state.command_state = DallasCommonCommandStateIdle; + onewire_slave_set_overdrive(data->state.bus, is_short); + return true; } static bool dallas_ds1996_command_callback(uint8_t command, void* context) { @@ -96,8 +126,7 @@ static bool dallas_ds1996_command_callback(uint8_t command, void* context) { } else if(data->state.command_state == DallasCommonCommandStateRomCmd) { data->state.command_state = DallasCommonCommandStateMemCmd; - dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE); - return false; + return dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE); } else { return false; @@ -120,8 +149,17 @@ static bool dallas_ds1996_command_callback(uint8_t command, void* context) { } case DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + onewire_slave_set_overdrive(bus, true); + return true; + } else { + return false; + } + + case DALLAS_COMMON_CMD_MATCH_ROM: case DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM: - /* TODO: Overdrive mode support */ + /* TODO: Match ROM command support */ default: return false; } diff --git a/lib/ibutton/protocols/dallas/protocol_ds_generic.c b/lib/ibutton/protocols/dallas/protocol_ds_generic.c index 50fd045112..af355f4612 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds_generic.c +++ b/lib/ibutton/protocols/dallas/protocol_ds_generic.c @@ -61,6 +61,14 @@ bool ds_generic_write_blank(OneWireHost* host, iButtonProtocolData* protocol_dat return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)); } +static bool ds_generic_reset_callback(bool is_short, void* context) { + DallasGenericProtocolData* data = context; + if(!is_short) { + onewire_slave_set_overdrive(data->state.bus, is_short); + } + return !is_short; +} + static bool ds_generic_command_callback(uint8_t command, void* context) { furi_assert(context); DallasGenericProtocolData* data = context; @@ -85,7 +93,7 @@ void ds_generic_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { DallasGenericProtocolData* data = protocol_data; data->state.bus = bus; - onewire_slave_set_reset_callback(bus, NULL, NULL); + onewire_slave_set_reset_callback(bus, ds_generic_reset_callback, NULL); onewire_slave_set_command_callback(bus, ds_generic_command_callback, protocol_data); } diff --git a/lib/misc.scons b/lib/misc.scons index 49b6b61d97..b479851b12 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -36,7 +36,6 @@ for lib in libs_recurse: sources += libenv.GlobRecursive("*.c*", lib) libs_plain = [ - "heatshrink", "nanopb", ] @@ -47,6 +46,12 @@ for lib in libs_plain: source=True, ) +sources += Glob( + "heatshrink/heatshrink_*.c*", + exclude=GLOB_FILE_EXCLUSION, + source=True, +) + lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) Return("lib") diff --git a/lib/one_wire/SConscript b/lib/one_wire/SConscript index 8d73c9dbf0..2dde9153df 100644 --- a/lib/one_wire/SConscript +++ b/lib/one_wire/SConscript @@ -8,7 +8,6 @@ env.Append( "#/lib/one_wire", ], SDK_HEADERS=[ - File("one_wire_host_timing.h"), File("one_wire_host.h"), File("one_wire_slave.h"), File("maxim_crc.h"), diff --git a/lib/one_wire/one_wire_host.c b/lib/one_wire/one_wire_host.c index 0a4a79f5cc..678812105c 100644 --- a/lib/one_wire/one_wire_host.c +++ b/lib/one_wire/one_wire_host.c @@ -1,10 +1,54 @@ #include +/** + * Timings based on Application Note 126: + * https://www.analog.com/media/en/technical-documentation/tech-articles/1wire-communication-through-software--maxim-integrated.pdf + */ + #include "one_wire_host.h" -#include "one_wire_host_timing.h" + +typedef struct { + uint16_t a; + uint16_t b; + uint16_t c; + uint16_t d; + uint16_t e; + uint16_t f; + uint16_t g; + uint16_t h; + uint16_t i; + uint16_t j; +} OneWireHostTimings; + +static const OneWireHostTimings onewire_host_timings_normal = { + .a = 9, + .b = 64, + .c = 64, + .d = 14, + .e = 9, + .f = 55, + .g = 0, + .h = 480, + .i = 70, + .j = 410, +}; + +static const OneWireHostTimings onewire_host_timings_overdrive = { + .a = 1, + .b = 8, + .c = 8, + .d = 3, + .e = 1, + .f = 7, + .g = 3, + .h = 70, + .i = 9, + .j = 40, +}; struct OneWireHost { const GpioPin* gpio_pin; + const OneWireHostTimings* timings; unsigned char saved_rom[8]; /** < global search state */ uint8_t last_discrepancy; uint8_t last_family_discrepancy; @@ -15,6 +59,7 @@ OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin) { OneWireHost* host = malloc(sizeof(OneWireHost)); host->gpio_pin = gpio_pin; onewire_host_reset_search(host); + onewire_host_set_overdrive(host, false); return host; } @@ -27,6 +72,8 @@ bool onewire_host_reset(OneWireHost* host) { uint8_t r; uint8_t retries = 125; + const OneWireHostTimings* timings = host->timings; + // wait until the gpio is high furi_hal_gpio_write(host->gpio_pin, true); do { @@ -35,19 +82,19 @@ bool onewire_host_reset(OneWireHost* host) { } while(!furi_hal_gpio_read(host->gpio_pin)); // pre delay - furi_delay_us(OWH_RESET_DELAY_PRE); + furi_delay_us(timings->g); // drive low furi_hal_gpio_write(host->gpio_pin, false); - furi_delay_us(OWH_RESET_DRIVE); + furi_delay_us(timings->h); // release furi_hal_gpio_write(host->gpio_pin, true); - furi_delay_us(OWH_RESET_RELEASE); + furi_delay_us(timings->i); // read and post delay r = !furi_hal_gpio_read(host->gpio_pin); - furi_delay_us(OWH_RESET_DELAY_POST); + furi_delay_us(timings->j); return r; } @@ -55,17 +102,19 @@ bool onewire_host_reset(OneWireHost* host) { bool onewire_host_read_bit(OneWireHost* host) { bool result; + const OneWireHostTimings* timings = host->timings; + // drive low furi_hal_gpio_write(host->gpio_pin, false); - furi_delay_us(OWH_READ_DRIVE); + furi_delay_us(timings->a); // release furi_hal_gpio_write(host->gpio_pin, true); - furi_delay_us(OWH_READ_RELEASE); + furi_delay_us(timings->e); // read and post delay result = furi_hal_gpio_read(host->gpio_pin); - furi_delay_us(OWH_READ_DELAY_POST); + furi_delay_us(timings->f); return result; } @@ -89,22 +138,24 @@ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) } void onewire_host_write_bit(OneWireHost* host, bool value) { + const OneWireHostTimings* timings = host->timings; + if(value) { // drive low furi_hal_gpio_write(host->gpio_pin, false); - furi_delay_us(OWH_WRITE_1_DRIVE); + furi_delay_us(timings->a); // release furi_hal_gpio_write(host->gpio_pin, true); - furi_delay_us(OWH_WRITE_1_RELEASE); + furi_delay_us(timings->b); } else { // drive low furi_hal_gpio_write(host->gpio_pin, false); - furi_delay_us(OWH_WRITE_0_DRIVE); + furi_delay_us(timings->c); // release furi_hal_gpio_write(host->gpio_pin, true); - furi_delay_us(OWH_WRITE_0_RELEASE); + furi_delay_us(timings->d); } } @@ -122,10 +173,6 @@ void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t } } -void onewire_host_skip(OneWireHost* host) { - onewire_host_write(host, 0xCC); -} - void onewire_host_start(OneWireHost* host) { furi_hal_gpio_write(host->gpio_pin, true); furi_hal_gpio_init(host->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); @@ -154,7 +201,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code) { host->last_device_flag = false; } -uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) { +bool onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) { uint8_t id_bit_number; uint8_t last_zero, rom_byte_number, search_result; uint8_t id_bit, cmp_id_bit; @@ -268,3 +315,7 @@ uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSea return search_result; } + +void onewire_host_set_overdrive(OneWireHost* host, bool set) { + host->timings = set ? &onewire_host_timings_overdrive : &onewire_host_timings_normal; +} diff --git a/lib/one_wire/one_wire_host.h b/lib/one_wire/one_wire_host.h index dc469904dd..9f9bd4ffd7 100644 --- a/lib/one_wire/one_wire_host.h +++ b/lib/one_wire/one_wire_host.h @@ -15,114 +15,115 @@ extern "C" { typedef enum { OneWireHostSearchModeConditional = 0, /**< Search for alarmed device */ - OneWireHostSearchModeNormal = 1, /**< Search all devices */ + OneWireHostSearchModeNormal = 1, /**< Search for all devices */ } OneWireHostSearchMode; typedef struct OneWireHost OneWireHost; /** - * Allocate onewire host bus - * @param pin - * @return OneWireHost* + * Allocate OneWireHost instance + * @param [in] gpio_pin connection pin + * @return pointer to OneWireHost instance */ OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin); /** - * Deallocate onewire host bus - * @param host + * Destroy OneWireHost instance, free resources + * @param [in] host pointer to OneWireHost instance */ void onewire_host_free(OneWireHost* host); /** - * Reset bus - * @param host - * @return bool + * Reset the 1-Wire bus + * @param [in] host pointer to OneWireHost instance + * @return true if presence was detected, false otherwise */ bool onewire_host_reset(OneWireHost* host); /** * Read one bit - * @param host - * @return bool + * @param [in] host pointer to OneWireHost instance + * @return received bit value */ bool onewire_host_read_bit(OneWireHost* host); /** * Read one byte - * @param host - * @return uint8_t + * @param [in] host pointer to OneWireHost instance + * @return received byte value */ uint8_t onewire_host_read(OneWireHost* host); /** - * Read many bytes - * @param host - * @param buffer - * @param count + * Read one or more bytes + * @param [in] host pointer to OneWireHost instance + * @param [out] buffer received data buffer + * @param [in] count number of bytes to read */ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count); /** * Write one bit - * @param host - * @param value + * @param [in] host pointer to OneWireHost instance + * @param value bit value to write */ void onewire_host_write_bit(OneWireHost* host, bool value); /** * Write one byte - * @param host - * @param value + * @param [in] host pointer to OneWireHost instance + * @param value byte value to write */ void onewire_host_write(OneWireHost* host, uint8_t value); /** - * Write many bytes - * @param host - * @param buffer - * @param count + * Write one or more bytes + * @param [in] host pointer to OneWireHost instance + * @param [in] buffer pointer to the data to write + * @param [in] count size of the data to write */ void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count); -/** - * Skip ROM command - * @param host - */ -void onewire_host_skip(OneWireHost* host); - /** * Start working with the bus - * @param host + * @param [in] host pointer to OneWireHost instance */ void onewire_host_start(OneWireHost* host); /** * Stop working with the bus - * @param host + * @param [in] host pointer to OneWireHost instance */ void onewire_host_stop(OneWireHost* host); /** - * - * @param host + * Reset previous search results + * @param [in] host pointer to OneWireHost instance */ void onewire_host_reset_search(OneWireHost* host); /** - * - * @param host - * @param family_code + * Set the family code to search for + * @param [in] host pointer to OneWireHost instance + * @param [in] family_code device family code */ void onewire_host_target_search(OneWireHost* host, uint8_t family_code); /** - * - * @param host - * @param newAddr - * @param mode - * @return uint8_t + * Search for devices on the 1-Wire bus + * @param [in] host pointer to OneWireHost instance + * @param [out] new_addr pointer to the buffer to contain the unique ROM of the found device + * @param [in] mode search mode + * @return true on success, false otherwise + */ +bool onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode); + +/** + * Enable overdrive mode + * @param [in] host pointer to OneWireHost instance + * @param [in] set true to turn overdrive on, false to turn it off */ -uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode); +void onewire_host_set_overdrive(OneWireHost* host, bool set); #ifdef __cplusplus } diff --git a/lib/one_wire/one_wire_host_timing.h b/lib/one_wire/one_wire_host_timing.h deleted file mode 100644 index f95dd3561e..0000000000 --- a/lib/one_wire/one_wire_host_timing.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file one_wire_host_timing.h - * - * 1-Wire library, timing list - */ - -#pragma once - -#define OWH_TIMING_A 9 -#define OWH_TIMING_B 64 -#define OWH_TIMING_C 64 -#define OWH_TIMING_D 14 -#define OWH_TIMING_E 9 -#define OWH_TIMING_F 55 -#define OWH_TIMING_G 0 -#define OWH_TIMING_H 480 -#define OWH_TIMING_I 70 -#define OWH_TIMING_J 410 - -#define OWH_WRITE_1_DRIVE OWH_TIMING_A -#define OWH_WRITE_1_RELEASE OWH_TIMING_B -#define OWH_WRITE_0_DRIVE OWH_TIMING_C -#define OWH_WRITE_0_RELEASE OWH_TIMING_D -#define OWH_READ_DRIVE 3 -#define OWH_READ_RELEASE OWH_TIMING_E -#define OWH_READ_DELAY_POST OWH_TIMING_F -#define OWH_RESET_DELAY_PRE OWH_TIMING_G -#define OWH_RESET_DRIVE OWH_TIMING_H -#define OWH_RESET_RELEASE OWH_TIMING_I -#define OWH_RESET_DELAY_POST OWH_TIMING_J diff --git a/lib/one_wire/one_wire_slave.c b/lib/one_wire/one_wire_slave.c index d1676cf3b8..733b36e30e 100644 --- a/lib/one_wire/one_wire_slave.c +++ b/lib/one_wire/one_wire_slave.c @@ -3,20 +3,7 @@ #include #include -#define ONEWIRE_TRSTL_MIN 270 /* Minimum Reset Low time */ -#define ONEWIRE_TRSTL_MAX 1200 /* Maximum Reset Low time */ - -#define ONEWIRE_TPDH_TYP 20 /* Typical Presence Detect High time */ -#define ONEWIRE_TPDL_MIN 100 /* Minimum Presence Detect Low time */ -#define ONEWIRE_TPDL_MAX 480 /* Maximum Presence Detect Low time */ - -#define ONEWIRE_TSLOT_MIN 60 /* Minimum Read/Write Slot time */ -#define ONEWIRE_TSLOT_MAX 135 /* Maximum Read/Write Slot time */ - -#define ONEWIRE_TW1L_MAX 20 /* Maximum Master Write 1 time */ -#define ONEWIRE_TRL_TMSR_MAX 30 /* Maximum Master Read Low + Read Sample time */ - -#define ONEWIRE_TH_TIMEOUT 15000 /* Maximum time before general timeout */ +#define TH_TIMEOUT_MAX 15000 /* Maximum time before general timeout */ typedef enum { OneWireSlaveErrorNone = 0, @@ -26,10 +13,29 @@ typedef enum { OneWireSlaveErrorTimeout, } OneWireSlaveError; +typedef struct { + uint16_t trstl_min; /* Minimum Reset Low time */ + uint16_t trstl_max; /* Maximum Reset Low time */ + + uint16_t tpdh_typ; /* Typical Presence Detect High time */ + uint16_t tpdl_min; /* Minimum Presence Detect Low time */ + uint16_t tpdl_max; /* Maximum Presence Detect Low time */ + + uint16_t tslot_min; /* Minimum Read/Write Slot time */ + uint16_t tslot_max; /* Maximum Read/Write Slot time */ + + uint16_t tw1l_max; /* Maximum Master Write 1 time */ + uint16_t trl_tmsr_max; /* Maximum Master Read Low + Read Sample time */ +} OneWireSlaveTimings; + struct OneWireSlave { const GpioPin* gpio_pin; + const OneWireSlaveTimings* timings; OneWireSlaveError error; + bool is_first_reset; + bool is_short_reset; + OneWireSlaveResetCallback reset_callback; OneWireSlaveCommandCallback command_callback; OneWireSlaveResultCallback result_callback; @@ -39,42 +45,72 @@ struct OneWireSlave { void* command_callback_context; }; +static const OneWireSlaveTimings onewire_slave_timings_normal = { + .trstl_min = 270, + .trstl_max = 1200, + + .tpdh_typ = 20, + .tpdl_min = 100, + .tpdl_max = 480, + + .tslot_min = 60, + .tslot_max = 135, + + .tw1l_max = 20, + .trl_tmsr_max = 30, +}; + +static const OneWireSlaveTimings onewire_slave_timings_overdrive = { + .trstl_min = 48, + .trstl_max = 80, + + .tpdh_typ = 0, + .tpdl_min = 8, + .tpdl_max = 24, + + .tslot_min = 6, + .tslot_max = 16, + + .tw1l_max = 2, + .trl_tmsr_max = 3, +}; + /*********************** PRIVATE ***********************/ -static uint32_t - onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { - uint32_t start = DWT->CYCCNT; - uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond(); - uint32_t time_captured; +static bool + onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time_us, const bool pin_value) { + const uint32_t time_start = DWT->CYCCNT; + const uint32_t time_ticks = time_us * furi_hal_cortex_instructions_per_microsecond(); + + uint32_t time_elapsed; do { //-V1044 - time_captured = DWT->CYCCNT; + time_elapsed = DWT->CYCCNT - time_start; if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) { - uint32_t remaining_time = time_ticks - (time_captured - start); - remaining_time /= furi_hal_cortex_instructions_per_microsecond(); - return remaining_time; + return time_ticks >= time_elapsed; } - } while((time_captured - start) < time_ticks); + } while(time_elapsed < time_ticks); - return 0; + return false; } -static bool onewire_slave_show_presence(OneWireSlave* bus) { +static inline bool onewire_slave_show_presence(OneWireSlave* bus) { + const OneWireSlaveTimings* timings = bus->timings; // wait until the bus is high (might return immediately) - onewire_slave_wait_while_gpio_is(bus, ONEWIRE_TRSTL_MAX, false); + onewire_slave_wait_while_gpio_is(bus, timings->trstl_max, false); // wait while master delay presence check - furi_delay_us(ONEWIRE_TPDH_TYP); + furi_delay_us(timings->tpdh_typ); // show presence furi_hal_gpio_write(bus->gpio_pin, false); - furi_delay_us(ONEWIRE_TPDL_MIN); + furi_delay_us(timings->tpdl_min); furi_hal_gpio_write(bus->gpio_pin, true); // somebody also can show presence - const uint32_t wait_low_time = ONEWIRE_TPDL_MAX - ONEWIRE_TPDL_MIN; + const uint32_t wait_low_time = timings->tpdl_max - timings->tpdl_min; // so we will wait - if(onewire_slave_wait_while_gpio_is(bus, wait_low_time, false) == 0) { + if(!onewire_slave_wait_while_gpio_is(bus, wait_low_time, false)) { bus->error = OneWireSlaveErrorPresenceConflict; return false; } @@ -85,27 +121,36 @@ static bool onewire_slave_show_presence(OneWireSlave* bus) { static inline bool onewire_slave_receive_and_process_command(OneWireSlave* bus) { /* Reset condition detected, send a presence pulse and reset protocol state */ if(bus->error == OneWireSlaveErrorResetInProgress) { - if(onewire_slave_show_presence(bus)) { - bus->error = OneWireSlaveErrorNone; + if(!bus->is_first_reset) { + /* Guess the reset type */ + bus->is_short_reset = onewire_slave_wait_while_gpio_is( + bus, + onewire_slave_timings_overdrive.trstl_max - + onewire_slave_timings_overdrive.tslot_max, + false); + } else { + bus->is_first_reset = false; + } - if(bus->reset_callback != NULL) { - bus->reset_callback(bus->reset_callback_context); - } + furi_assert(bus->reset_callback); - return true; + if(bus->reset_callback(bus->is_short_reset, bus->reset_callback_context)) { + if(onewire_slave_show_presence(bus)) { + bus->error = OneWireSlaveErrorNone; + return true; + } } } else if(bus->error == OneWireSlaveErrorNone) { uint8_t command; - if(!onewire_slave_receive(bus, &command, 1)) { - /* Upon failure, request an additional iteration to - choose the appropriate action by checking bus->error */ - return true; - } else if(bus->command_callback) { - return bus->command_callback(command, bus->command_callback_context); - } else { - bus->error = OneWireSlaveErrorInvalidCommand; + if(onewire_slave_receive(bus, &command, sizeof(command))) { + furi_assert(bus->command_callback); + if(bus->command_callback(command, bus->command_callback_context)) { + return true; + } } + + return (bus->error == OneWireSlaveErrorResetInProgress); } return false; @@ -115,9 +160,6 @@ static inline bool onewire_slave_bus_start(OneWireSlave* bus) { FURI_CRITICAL_ENTER(); furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - /* Start in Reset state in order to send a presence pulse immediately */ - bus->error = OneWireSlaveErrorResetInProgress; - while(onewire_slave_receive_and_process_command(bus)) ; @@ -139,7 +181,15 @@ static void onewire_slave_exti_callback(void* context) { const uint32_t pulse_length = (DWT->CYCCNT - pulse_start) / furi_hal_cortex_instructions_per_microsecond(); - if((pulse_length >= ONEWIRE_TRSTL_MIN) && pulse_length <= (ONEWIRE_TRSTL_MAX)) { + if((pulse_length >= onewire_slave_timings_overdrive.trstl_min) && + (pulse_length <= onewire_slave_timings_normal.trstl_max)) { + /* Start in reset state in order to send a presence pulse immediately */ + bus->error = OneWireSlaveErrorResetInProgress; + /* Determine reset type (chooses speed mode if supported by the emulated device) */ + bus->is_short_reset = pulse_length <= onewire_slave_timings_overdrive.trstl_max; + /* Initial reset allows going directly into overdrive mode */ + bus->is_first_reset = true; + const bool result = onewire_slave_bus_start(bus); if(result && bus->result_callback != NULL) { @@ -158,6 +208,7 @@ OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) { OneWireSlave* bus = malloc(sizeof(OneWireSlave)); bus->gpio_pin = gpio_pin; + bus->timings = &onewire_slave_timings_normal; bus->error = OneWireSlaveErrorNone; return bus; @@ -205,52 +256,45 @@ void onewire_slave_set_result_callback( } bool onewire_slave_receive_bit(OneWireSlave* bus) { + const OneWireSlaveTimings* timings = bus->timings; // wait while bus is low - uint32_t time = ONEWIRE_TSLOT_MAX; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - if(time == 0) { + if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) { bus->error = OneWireSlaveErrorResetInProgress; return false; } // wait while bus is high - time = ONEWIRE_TH_TIMEOUT; - time = onewire_slave_wait_while_gpio_is(bus, time, true); - if(time == 0) { + if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) { bus->error = OneWireSlaveErrorTimeout; return false; } // wait a time of zero - time = ONEWIRE_TW1L_MAX; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - - return (time > 0); + return onewire_slave_wait_while_gpio_is(bus, timings->tw1l_max, false); } bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { + const OneWireSlaveTimings* timings = bus->timings; // wait while bus is low - uint32_t time = ONEWIRE_TSLOT_MAX; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - if(time == 0) { + if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) { bus->error = OneWireSlaveErrorResetInProgress; return false; } // wait while bus is high - time = ONEWIRE_TH_TIMEOUT; - time = onewire_slave_wait_while_gpio_is(bus, time, true); - if(time == 0) { + if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) { bus->error = OneWireSlaveErrorTimeout; return false; } // choose write time + uint32_t time; + if(!value) { furi_hal_gpio_write(bus->gpio_pin, false); - time = ONEWIRE_TRL_TMSR_MAX; + time = timings->trl_tmsr_max; } else { - time = ONEWIRE_TSLOT_MIN; + time = timings->tslot_min; } // hold line for ZERO or ONE time @@ -301,3 +345,13 @@ bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size) { } return true; } + +void onewire_slave_set_overdrive(OneWireSlave* bus, bool set) { + const OneWireSlaveTimings* new_timings = set ? &onewire_slave_timings_overdrive : + &onewire_slave_timings_normal; + if(bus->timings != new_timings) { + /* Prevent erroneous reset by waiting for the previous time slot to finish */ + onewire_slave_wait_while_gpio_is(bus, bus->timings->tslot_max, false); + bus->timings = new_timings; + } +} diff --git a/lib/one_wire/one_wire_slave.h b/lib/one_wire/one_wire_slave.h index 914cd9335e..21114b912c 100644 --- a/lib/one_wire/one_wire_slave.h +++ b/lib/one_wire/one_wire_slave.h @@ -18,68 +18,85 @@ extern "C" { typedef struct OneWireDevice OneWireDevice; typedef struct OneWireSlave OneWireSlave; -typedef void (*OneWireSlaveResetCallback)(void* context); -typedef void (*OneWireSlaveResultCallback)(void* context); +typedef bool (*OneWireSlaveResetCallback)(bool is_short, void* context); typedef bool (*OneWireSlaveCommandCallback)(uint8_t command, void* context); +typedef void (*OneWireSlaveResultCallback)(void* context); /** - * Allocate onewire slave - * @param gpio_pin - * @return OneWireSlave* + * Allocate OneWireSlave instance + * @param [in] gpio_pin connection pin + * @return pointer to OneWireSlave instance */ OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin); /** - * Free onewire slave - * @param bus + * Destroy OneWireSlave instance, free resources + * @param [in] bus pointer to OneWireSlave instance */ void onewire_slave_free(OneWireSlave* bus); /** * Start working with the bus - * @param bus + * @param [in] bus pointer to OneWireSlave instance */ void onewire_slave_start(OneWireSlave* bus); /** * Stop working with the bus - * @param bus + * @param [in] bus pointer to OneWireSlave instance */ void onewire_slave_stop(OneWireSlave* bus); /** - * TODO: description comment + * Receive one bit + * @param [in] bus pointer to OneWireSlave instance + * @return received bit value */ bool onewire_slave_receive_bit(OneWireSlave* bus); /** - * TODO: description comment + * Send one bit + * @param [in] bus pointer to OneWireSlave instance + * @param [in] value bit value to send + * @return true on success, false on failure */ bool onewire_slave_send_bit(OneWireSlave* bus, bool value); /** - * Send data - * @param bus - * @param data - * @param data_size - * @return bool + * Send one or more bytes of data + * @param [in] bus pointer to OneWireSlave instance + * @param [in] data pointer to the data to send + * @param [in] data_size size of the data to send + * @return true on success, false on failure */ bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size); /** - * Receive data - * @param bus - * @param data - * @param data_size - * @return bool + * Receive one or more bytes of data + * @param [in] bus pointer to OneWireSlave instance + * @param [out] data pointer to the receive buffer + * @param [in] data_size number of bytes to receive + * @return true on success, false on failure */ bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size); /** - * Set a callback to be called on each reset - * @param bus - * @param callback - * @param context + * Enable overdrive mode + * @param [in] bus pointer to OneWireSlave instance + * @param [in] set true to turn overdrive on, false to turn it off + */ +void onewire_slave_set_overdrive(OneWireSlave* bus, bool set); + +/** + * Set a callback function to be called on each reset. + * The return value of the callback determines whether the emulated device + * supports the short reset (passed as the is_short parameter). + * In most applications, it should also call onewire_slave_set_overdrive() + * to set the appropriate speed mode. + * + * @param [in] bus pointer to OneWireSlave instance + * @param [in] callback pointer to a callback function + * @param [in] context additional parameter to be passed to the callback */ void onewire_slave_set_reset_callback( OneWireSlave* bus, @@ -87,10 +104,13 @@ void onewire_slave_set_reset_callback( void* context); /** - * Set a callback to be called on each command - * @param bus - * @param callback - * @param context + * Set a callback function to be called on each command. + * The return value of the callback determines whether further operation + * is possible. As a rule of thumb, return true unless a critical error happened. + * + * @param [in] bus pointer to OneWireSlave instance + * @param [in] callback pointer to a callback function + * @param [in] context additional parameter to be passed to the callback */ void onewire_slave_set_command_callback( OneWireSlave* bus, @@ -99,9 +119,9 @@ void onewire_slave_set_command_callback( /** * Set a callback to report emulation success - * @param bus - * @param result_cb - * @param context + * @param [in] bus pointer to OneWireSlave instance + * @param [in] result_cb pointer to a callback function + * @param [in] context additional parameter to be passed to the callback */ void onewire_slave_set_result_callback( OneWireSlave* bus, diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 144c161203..b72cf99eb5 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -79,7 +79,8 @@ static const uint32_t subghz_frequency_list[] = { static const uint32_t subghz_hopper_frequency_list[] = { 310000000, 315000000, - 433420000, + 318000000, + 418000000, 433920000, 868350000, 0, diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index c01e32a6b3..caff7bd0a2 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -13,7 +13,7 @@ env.Append( File("manchester_encoder.h"), File("path.h"), File("random_name.h"), - File("hmac_sha256.h"), + File("sha256.h"), File("crc32_calc.h"), File("dir_walk.h"), File("md5.h"), diff --git a/firmware/targets/f7/furi_hal/furi_hal_compress.c b/lib/toolbox/compress.c similarity index 67% rename from firmware/targets/f7/furi_hal/furi_hal_compress.c rename to lib/toolbox/compress.c index 7e31dbbf7f..0d5e1c654d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_compress.c +++ b/lib/toolbox/compress.c @@ -1,115 +1,112 @@ -#include +#include "compress.h" #include #include #include -#define TAG "FuriHalCompress" +/** Defines encoder and decoder window size */ +#define COMPRESS_EXP_BUFF_SIZE_LOG (8u) -#define FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE (2 * 512) -#define FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE (1024) +/** Defines encoder and decoder lookahead buffer size */ +#define COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4u) -#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE (1 << FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG) +/** Buffer sizes for input and output data */ +#define COMPRESS_ICON_ENCODED_BUFF_SIZE (1024u) +#define COMPRESS_ICON_DECODED_BUFF_SIZE (1024u) typedef struct { uint8_t is_compressed; uint8_t reserved; uint16_t compressed_buff_size; -} FuriHalCompressHeader; +} CompressHeader; -typedef struct { - heatshrink_decoder* decoder; - uint8_t - compress_buff[FURI_HAL_COMPRESS_EXP_BUFF_SIZE + FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE]; - uint8_t decoded_buff[FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE]; -} FuriHalCompressIcon; +_Static_assert(sizeof(CompressHeader) == 4, "Incorrect CompressHeader size"); -struct FuriHalCompress { - heatshrink_encoder* encoder; +struct CompressIcon { heatshrink_decoder* decoder; - uint8_t* compress_buff; - uint16_t compress_buff_size; + uint8_t decoded_buff[COMPRESS_ICON_DECODED_BUFF_SIZE]; }; -static FuriHalCompressIcon* icon_decoder; +CompressIcon* compress_icon_alloc() { + CompressIcon* instance = malloc(sizeof(CompressIcon)); + instance->decoder = heatshrink_decoder_alloc( + COMPRESS_ICON_ENCODED_BUFF_SIZE, + COMPRESS_EXP_BUFF_SIZE_LOG, + COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); + heatshrink_decoder_reset(instance->decoder); + memset(instance->decoded_buff, 0, sizeof(instance->decoded_buff)); -static void furi_hal_compress_reset(FuriHalCompress* compress) { - furi_assert(compress); - heatshrink_encoder_reset(compress->encoder); - heatshrink_decoder_reset(compress->decoder); - memset(compress->compress_buff, 0, compress->compress_buff_size); + return instance; } -void furi_hal_compress_icon_init() { - icon_decoder = malloc(sizeof(FuriHalCompressIcon)); - icon_decoder->decoder = heatshrink_decoder_alloc( - icon_decoder->compress_buff, - FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); - heatshrink_decoder_reset(icon_decoder->decoder); - memset(icon_decoder->decoded_buff, 0, sizeof(icon_decoder->decoded_buff)); - FURI_LOG_I(TAG, "Init OK"); +void compress_icon_free(CompressIcon* instance) { + furi_assert(instance); + heatshrink_decoder_free(instance->decoder); + free(instance); } -void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff) { +void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff) { + furi_assert(instance); furi_assert(icon_data); furi_assert(decoded_buff); - FuriHalCompressHeader* header = (FuriHalCompressHeader*)icon_data; + CompressHeader* header = (CompressHeader*)icon_data; if(header->is_compressed) { size_t data_processed = 0; heatshrink_decoder_sink( - icon_decoder->decoder, - (uint8_t*)&icon_data[4], + instance->decoder, + (uint8_t*)&icon_data[sizeof(CompressHeader)], header->compressed_buff_size, &data_processed); while(1) { HSD_poll_res res = heatshrink_decoder_poll( - icon_decoder->decoder, - icon_decoder->decoded_buff, - sizeof(icon_decoder->decoded_buff), + instance->decoder, + instance->decoded_buff, + sizeof(instance->decoded_buff), &data_processed); furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE)); if(res != HSDR_POLL_MORE) { break; } } - heatshrink_decoder_reset(icon_decoder->decoder); - memset(icon_decoder->compress_buff, 0, sizeof(icon_decoder->compress_buff)); - *decoded_buff = icon_decoder->decoded_buff; + heatshrink_decoder_reset(instance->decoder); + *decoded_buff = instance->decoded_buff; } else { *decoded_buff = (uint8_t*)&icon_data[1]; } } -FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size) { - FuriHalCompress* compress = malloc(sizeof(FuriHalCompress)); - compress->compress_buff = malloc(compress_buff_size + FURI_HAL_COMPRESS_EXP_BUFF_SIZE); - compress->encoder = heatshrink_encoder_alloc( - compress->compress_buff, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); +struct Compress { + heatshrink_encoder* encoder; + heatshrink_decoder* decoder; +}; + +static void compress_reset(Compress* compress) { + furi_assert(compress); + heatshrink_encoder_reset(compress->encoder); + heatshrink_decoder_reset(compress->decoder); +} + +Compress* compress_alloc(uint16_t compress_buff_size) { + Compress* compress = malloc(sizeof(Compress)); + compress->encoder = + heatshrink_encoder_alloc(COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); compress->decoder = heatshrink_decoder_alloc( - compress->compress_buff, - compress_buff_size, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); + compress_buff_size, COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); return compress; } -void furi_hal_compress_free(FuriHalCompress* compress) { +void compress_free(Compress* compress) { furi_assert(compress); heatshrink_encoder_free(compress->encoder); heatshrink_decoder_free(compress->decoder); - free(compress->compress_buff); free(compress); } -bool furi_hal_compress_encode( - FuriHalCompress* compress, +bool compress_encode( + Compress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, @@ -126,7 +123,7 @@ bool furi_hal_compress_encode( HSE_finish_res finish_res; bool encode_failed = false; size_t sunk = 0; - size_t res_buff_size = sizeof(FuriHalCompressHeader); + size_t res_buff_size = sizeof(CompressHeader); // Sink data to encoding buffer while((sunk < data_in_size) && !encode_failed) { @@ -174,7 +171,7 @@ bool furi_hal_compress_encode( bool result = true; // Write encoded data to output buffer if compression is efficient. Else - write header and original data if(!encode_failed && (res_buff_size < data_in_size + 1)) { - FuriHalCompressHeader header = { + CompressHeader header = { .is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size}; memcpy(data_out, &header, sizeof(header)); *data_res_size = res_buff_size; @@ -186,13 +183,13 @@ bool furi_hal_compress_encode( *data_res_size = 0; result = false; } - furi_hal_compress_reset(compress); + compress_reset(compress); return result; } -bool furi_hal_compress_decode( - FuriHalCompress* compress, +bool compress_decode( + Compress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, @@ -212,11 +209,11 @@ bool furi_hal_compress_decode( size_t res_buff_size = 0; size_t poll_size = 0; - FuriHalCompressHeader* header = (FuriHalCompressHeader*)data_in; + CompressHeader* header = (CompressHeader*)data_in; if(header->is_compressed) { // Sink data to decoding buffer size_t compressed_size = header->compressed_buff_size; - size_t sunk = sizeof(FuriHalCompressHeader); + size_t sunk = sizeof(CompressHeader); while(sunk < compressed_size && !decode_failed) { sink_res = heatshrink_decoder_sink( compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size); @@ -258,7 +255,7 @@ bool furi_hal_compress_decode( } else { result = false; } - furi_hal_compress_reset(compress); + compress_reset(compress); return result; } diff --git a/lib/toolbox/compress.h b/lib/toolbox/compress.h new file mode 100644 index 0000000000..a18551d7f9 --- /dev/null +++ b/lib/toolbox/compress.h @@ -0,0 +1,96 @@ +/** + * @file compress.h + * LZSS based compression HAL API + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Compress Icon control structure */ +typedef struct CompressIcon CompressIcon; + +/** Initialize icon compressor + * + * @return Compress Icon instance + */ +CompressIcon* compress_icon_alloc(); + +/** Free icon compressor + * + * @param instance The Compress Icon instance + */ +void compress_icon_free(CompressIcon* instance); + +/** Decompress icon + * + * @warning decoded_buff pointer set by this function is valid till next + * `compress_icon_decode` or `compress_icon_free` call + * + * @param instance The Compress Icon instance + * @param icon_data pointer to icon data + * @param[in] decoded_buff pointer to decoded buffer pointer + */ +void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff); + +/** Compress control structure */ +typedef struct Compress Compress; + +/** Allocate encoder and decoder + * + * @param compress_buff_size size of decoder and encoder buffer to allocate + * + * @return Compress instance + */ +Compress* compress_alloc(uint16_t compress_buff_size); + +/** Free encoder and decoder + * + * @param compress Compress instance + */ +void compress_free(Compress* compress); + +/** Encode data + * + * @param compress Compress instance + * @param data_in pointer to input data + * @param data_in_size size of input data + * @param data_out maximum size of output data + * @param data_res_size pointer to result output data size + * + * @return true on success + */ +bool compress_encode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size); + +/** Decode data + * + * @param compress Compress instance + * @param data_in pointer to input data + * @param data_in_size size of input data + * @param data_out maximum size of output data + * @param data_res_size pointer to result output data size + * + * @return true on success + */ +bool compress_decode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size); + +#ifdef __cplusplus +} +#endif