1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00

Merge branch 'dev' into release

This commit is contained in:
MX
2023-03-23 00:42:20 +03:00
87 changed files with 1602 additions and 1949 deletions

View File

@@ -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

3
.gitmodules vendored
View File

@@ -34,3 +34,6 @@
[submodule "applications/external/subbrute"]
path = applications/external/subbrute
url = https://github.com/derskythe/flipperzero-subbrute.git
[submodule "lib/heatshrink"]
path = lib/heatshrink
url = https://github.com/flipperdevices/heatshrink.git

View File

@@ -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
* 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/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)
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)

View File

@@ -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)

View File

@@ -19,9 +19,12 @@
#include <one_wire/maxim_crc.h>
#include <one_wire/one_wire_host.h>
#include <furi_hal_power.h>
#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 *****************************/

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -0,0 +1,133 @@
#include "automation.h"
#include <lib/toolbox/args.h>
#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);
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <cli/cli.h>
#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();

View File

@@ -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() {

View File

@@ -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();

View File

@@ -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");
}

View File

@@ -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) {

View File

@@ -0,0 +1,2 @@
#define TOTP_BADBT_TYPE_ENABLED
#define TOTP_BADBT_TYPE_ICON_ENABLED

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

View File

@@ -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);

View File

@@ -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

View File

@@ -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"

View File

@@ -8,6 +8,7 @@
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <dolphin/dolphin.h>
#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);
}

View File

@@ -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
};

View File

@@ -3,9 +3,14 @@
#include <notification/notification.h>
#include <gui/gui.h>
#include <dialogs/dialogs.h>
#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;

View File

@@ -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;

View File

@@ -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};

View File

@@ -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;
}

View File

@@ -0,0 +1,141 @@
#include "bt_type_code.h"
#include <furi_hal_bt_hid.h>
#include <storage/storage.h>
#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);
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <stdlib.h>
#include <furi/furi.h>
#include <furi_hal.h>
#include <bt/bt_service/bt.h>
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);

View File

@@ -0,0 +1,14 @@
#include "constants.h"
#include <furi_hal.h>
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};

View File

@@ -0,0 +1,4 @@
#pragma once
#include <stdint.h>
extern const uint8_t hid_number_keys[10];

View File

@@ -1,27 +0,0 @@
#pragma once
#include <stdlib.h>
#include <furi/furi.h>
#include <furi_hal.h>
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);

View File

@@ -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);
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <stdlib.h>
#include <furi/furi.h>
#include <furi_hal.h>
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);

View File

@@ -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);
}
};
}

View File

@@ -19,6 +19,7 @@ typedef enum {
SubmenuIndexSommer_FM_868,
SubmenuIndexDTMNeo433,
SubmenuIndexGibidi433,
SubmenuIndexGSN,
SubmenuIndexNiceFlo12bit,
SubmenuIndexNiceFlo24bit,
SubmenuIndexNiceFlorS_433_92,

View File

@@ -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);

View File

@@ -7,7 +7,7 @@
#include <littlefs/lfs_util.h> // for lfs_tobe32
#include "toolbox/sha256.h"
#include "toolbox/hmac_sha256.h"
#include "hmac_sha256.h"
#include "micro-ecc/uECC.h"
#define TAG "U2F"

View File

@@ -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);
}

View File

@@ -7,6 +7,7 @@
#include "canvas.h"
#include <u8g2.h>
#include <toolbox/compress.h>
#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

View File

@@ -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);

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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

View File

@@ -91,7 +91,8 @@ Your frequencies will be added after default ones
```
310000000,
315000000,
433420000,
318000000,
418000000,
433920000,
868350000,
```

View File

@@ -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_<your_protocol_name>.irtest` in the [assets](assets/unit_tests/infrared) directory.
1. Create a file named `test_<your_protocol_name>.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

View File

@@ -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*
1 entry status name type params
2 Version + 18.2 20.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
57 Header + firmware/targets/furi_hal_include/furi_hal_bt.h
58 Header + firmware/targets/furi_hal_include/furi_hal_bt_hid.h
59 Header + firmware/targets/furi_hal_include/furi_hal_bt_serial.h
Header + firmware/targets/furi_hal_include/furi_hal_compress.h
60 Header + firmware/targets/furi_hal_include/furi_hal_cortex.h
61 Header + firmware/targets/furi_hal_include/furi_hal_crypto.h
62 Header + firmware/targets/furi_hal_include/furi_hal_debug.h
152 Header + lib/mlib/m-variant.h
153 Header + lib/one_wire/maxim_crc.h
154 Header + lib/one_wire/one_wire_host.h
Header + lib/one_wire/one_wire_host_timing.h
155 Header + lib/one_wire/one_wire_slave.h
156 Header + lib/print/wrappers.h
157 Header + lib/toolbox/args.h
158 Header + lib/toolbox/crc32_calc.h
159 Header + lib/toolbox/dir_walk.h
160 Header + lib/toolbox/float_tools.h
Header + lib/toolbox/hmac_sha256.h
161 Header + lib/toolbox/manchester_decoder.h
162 Header + lib/toolbox/manchester_encoder.h
163 Header + lib/toolbox/md5.h
166 Header + lib/toolbox/protocols/protocol_dict.h
167 Header + lib/toolbox/random_name.h
168 Header + lib/toolbox/saved_struct.h
169 Header + lib/toolbox/sha256.h
170 Header + lib/toolbox/stream/buffered_file_stream.h
171 Header + lib/toolbox/stream/file_stream.h
172 Header + lib/toolbox/stream/stream.h
875 Function - furi_hal_clock_suspend_tick void
876 Function - furi_hal_clock_switch_to_hsi void
877 Function - furi_hal_clock_switch_to_pll void
878 Function - furi_hal_compress_alloc compress_alloc FuriHalCompress* Compress* uint16_t
879 Function - furi_hal_compress_decode compress_decode _Bool FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t* Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*
880 Function - furi_hal_compress_encode compress_encode _Bool FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t* Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*
881 Function - furi_hal_compress_free compress_free void FuriHalCompress* Compress*
882 Function - furi_hal_compress_icon_decode compress_icon_decode void const uint8_t*, uint8_t**
883 Function - furi_hal_compress_icon_init compress_icon_init void
884 Function + furi_hal_console_disable void
885 Function + furi_hal_console_enable void
886 Function + furi_hal_console_init void
1314 Function + hal_sd_detect _Bool
1315 Function + hal_sd_detect_init void
1316 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
1317 Function + icon_animation_alloc IconAnimation* const Icon*
1318 Function + icon_animation_free void IconAnimation*
1319 Function + icon_animation_get_height uint8_t const IconAnimation*
1479 Function + onewire_host_read_bytes void OneWireHost*, uint8_t*, uint16_t
1480 Function + onewire_host_reset _Bool OneWireHost*
1481 Function + onewire_host_reset_search void OneWireHost*
1482 Function + onewire_host_search uint8_t _Bool OneWireHost*, uint8_t*, OneWireHostSearchMode
1483 Function + onewire_host_skip onewire_host_set_overdrive void OneWireHost* OneWireHost*, _Bool
1484 Function + onewire_host_start void OneWireHost*
1485 Function + onewire_host_stop void OneWireHost*
1486 Function + onewire_host_target_search void OneWireHost*, uint8_t
1494 Function + onewire_slave_send _Bool OneWireSlave*, const uint8_t*, size_t
1495 Function + onewire_slave_send_bit _Bool OneWireSlave*, _Bool
1496 Function + onewire_slave_set_command_callback void OneWireSlave*, OneWireSlaveCommandCallback, void*
1497 Function + onewire_slave_set_overdrive void OneWireSlave*, _Bool
1498 Function + onewire_slave_set_reset_callback void OneWireSlave*, OneWireSlaveResetCallback, void*
1499 Function + onewire_slave_set_result_callback void OneWireSlave*, OneWireSlaveResultCallback, void*
1500 Function + onewire_slave_start void OneWireSlave*

View File

@@ -1,5 +1,6 @@
#include <furi_hal.h>
#include <furi_hal_mpu.h>
#include <furi_hal_memory.h>
#include <stm32wbxx_ll_cortex.h>
@@ -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) {

View File

@@ -4,6 +4,8 @@
#include <stm32wbxx_ll_rcc.h>
#include <stm32wbxx_ll_pwr.h>
#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) {

View File

@@ -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*
1 entry status name type params
2 Version + 18.2 20.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
63 Header + firmware/targets/furi_hal_include/furi_hal_bt.h
64 Header + firmware/targets/furi_hal_include/furi_hal_bt_hid.h
65 Header + firmware/targets/furi_hal_include/furi_hal_bt_serial.h
Header + firmware/targets/furi_hal_include/furi_hal_compress.h
66 Header + firmware/targets/furi_hal_include/furi_hal_cortex.h
67 Header + firmware/targets/furi_hal_include/furi_hal_crypto.h
68 Header + firmware/targets/furi_hal_include/furi_hal_debug.h
174 Header + lib/nfc/nfc_device.h
175 Header + lib/one_wire/maxim_crc.h
176 Header + lib/one_wire/one_wire_host.h
Header + lib/one_wire/one_wire_host_timing.h
177 Header + lib/one_wire/one_wire_slave.h
178 Header + lib/print/wrappers.h
179 Header + lib/subghz/blocks/const.h
194 Header + lib/toolbox/crc32_calc.h
195 Header + lib/toolbox/dir_walk.h
196 Header + lib/toolbox/float_tools.h
Header + lib/toolbox/hmac_sha256.h
197 Header + lib/toolbox/manchester_decoder.h
198 Header + lib/toolbox/manchester_encoder.h
199 Header + lib/toolbox/md5.h
202 Header + lib/toolbox/protocols/protocol_dict.h
203 Header + lib/toolbox/random_name.h
204 Header + lib/toolbox/saved_struct.h
205 Header + lib/toolbox/sha256.h
206 Header + lib/toolbox/stream/buffered_file_stream.h
207 Header + lib/toolbox/stream/file_stream.h
208 Header + lib/toolbox/stream/stream.h
706 Function + composite_api_resolver_alloc CompositeApiResolver*
707 Function + composite_api_resolver_free void CompositeApiResolver*
708 Function + composite_api_resolver_get const ElfApiInterface* CompositeApiResolver*
709 Function - compress_alloc Compress* uint16_t
710 Function - compress_decode _Bool Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*
711 Function - compress_encode _Bool Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*
712 Function - compress_free void Compress*
713 Function - compress_icon_alloc CompressIcon*
714 Function - compress_icon_decode void CompressIcon*, const uint8_t*, uint8_t**
715 Function - compress_icon_free void CompressIcon*
716 Function - copysign double double, double
717 Function - copysignf float float, float
718 Function - copysignl long double long double, long double
1083 Function - furi_hal_clock_suspend_tick void
1084 Function - furi_hal_clock_switch_to_hsi void
1085 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
1086 Function + furi_hal_console_disable void
1087 Function + furi_hal_console_enable void
1088 Function + furi_hal_console_init void
1637 Function + hal_sd_detect _Bool
1638 Function + hal_sd_detect_init void
1639 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
1640 Function - hypot double double, double
1641 Function - hypotf float float, float
1642 Function - hypotl long double long double, long double
2105 Function + onewire_host_read_bytes void OneWireHost*, uint8_t*, uint16_t
2106 Function + onewire_host_reset _Bool OneWireHost*
2107 Function + onewire_host_reset_search void OneWireHost*
2108 Function + onewire_host_search uint8_t _Bool OneWireHost*, uint8_t*, OneWireHostSearchMode
2109 Function + onewire_host_skip onewire_host_set_overdrive void OneWireHost* OneWireHost*, _Bool
2110 Function + onewire_host_start void OneWireHost*
2111 Function + onewire_host_stop void OneWireHost*
2112 Function + onewire_host_target_search void OneWireHost*, uint8_t
2120 Function + onewire_slave_send _Bool OneWireSlave*, const uint8_t*, size_t
2121 Function + onewire_slave_send_bit _Bool OneWireSlave*, _Bool
2122 Function + onewire_slave_set_command_callback void OneWireSlave*, OneWireSlaveCommandCallback, void*
2123 Function + onewire_slave_set_overdrive void OneWireSlave*, _Bool
2124 Function + onewire_slave_set_reset_callback void OneWireSlave*, OneWireSlaveResetCallback, void*
2125 Function + onewire_slave_set_result_callback void OneWireSlave*, OneWireSlaveResultCallback, void*
2126 Function + onewire_slave_start void OneWireSlave*

View File

@@ -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();

View File

@@ -7,6 +7,7 @@
#include <furi.h>
#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(

View File

@@ -4,6 +4,8 @@
#include <stm32wbxx_ll_rcc.h>
#include <stm32wbxx_ll_pwr.h>
#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) {

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -4,10 +4,11 @@
#include <alt_boot.h>
#include <u8g2_glue.h>
#include <assets_icons.h>
#include <toolbox/compress.h>
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() {

View File

@@ -4,6 +4,7 @@
#include <alt_boot.h>
#include <u8g2_glue.h>
#include <assets_icons.h>
#include <toolbox/compress.h>
#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) {

View File

@@ -33,7 +33,6 @@ struct STOP_EXTERNING_ME {};
#include <furi_hal_vibro.h>
#include <furi_hal_usb.h>
#include <furi_hal_usb_hid.h>
#include <furi_hal_compress.h>
#include <furi_hal_uart.h>
#include <furi_hal_info.h>
#include <furi_hal_random.h>

View File

@@ -1,87 +0,0 @@
/**
* @file furi_hal_compress.h
* LZSS based compression HAL API
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#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

4
lib/err.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
#include <furi.h>
#define err(...) FURI_LOG_E("Heatshrink", "Error: %d-%s", __VA_ARGS__)

1
lib/heatshrink Submodule

Submodule lib/heatshrink added at 7398ccc916

View File

@@ -1,20 +0,0 @@
#ifndef HEATSHRINK_H
#define HEATSHRINK_H
#define HEATSHRINK_AUTHOR "Scott Vokes <vokes.s@gmail.com>"
#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

View File

@@ -1,28 +0,0 @@
#ifndef HEATSHRINK_CONFIG_H
#define HEATSHRINK_CONFIG_H
#include <furi.h>
/* 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

View File

@@ -1,364 +0,0 @@
#include <stdlib.h>
#include <string.h>
#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 <stdio.h>
#include <ctype.h>
#include <assert.h>
#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; i<count; i++) {
uint8_t c = buf[(hsd->head_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;
}

View File

@@ -1,100 +0,0 @@
#ifndef HEATSHRINK_DECODER_H
#define HEATSHRINK_DECODER_H
#include <stdint.h>
#include <stddef.h>
#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

View File

@@ -1,602 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#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 <stdio.h>
#include <ctype.h>
#include <assert.h>
#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; i<end; i++) {
uint8_t v = data[i];
int16_t lv = last[v];
index[i] = lv;
last[v] = i;
}
#else
(void)hse;
#endif
}
static int is_finishing(heatshrink_encoder *hse) {
return hse->flags & 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<maxlen; len++) {
if (0) {
LOG(" --> 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;
}

View File

@@ -1,109 +0,0 @@
#ifndef HEATSHRINK_ENCODER_H
#define HEATSHRINK_ENCODER_H
#include <stdint.h>
#include <stddef.h>
#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

View File

@@ -1,10 +1,10 @@
#pragma once
#include <flipper_format.h>
#include <one_wire/one_wire_host.h>
#include <one_wire/one_wire_slave.h>
#include <flipper_format/flipper_format.h>
#define DALLAS_COMMON_MANUFACTURER_NAME "Dallas"
#define DALLAS_COMMON_CMD_READ_ROM 0x33U

View File

@@ -2,11 +2,11 @@
#include "../protocol_common_i.h"
#include <flipper_format.h>
#include <one_wire/one_wire_host.h>
#include <one_wire/one_wire_slave.h>
#include <flipper_format/flipper_format.h>
typedef bool (*iButtonProtocolDallasReadWriteFunc)(OneWireHost*, iButtonProtocolData*);
typedef void (*iButtonProtocolDallasEmulateFunc)(OneWireSlave*, iButtonProtocolData*);
typedef bool (*iButtonProtocolDallasSaveFunc)(FlipperFormat*, const iButtonProtocolData*);

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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")

View File

@@ -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"),

View File

@@ -1,10 +1,54 @@
#include <furi.h>
/**
* 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;
}

View File

@@ -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
*/
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);
/**
* Enable overdrive mode
* @param [in] host pointer to OneWireHost instance
* @param [in] set true to turn overdrive on, false to turn it off
*/
void onewire_host_set_overdrive(OneWireHost* host, bool set);
#ifdef __cplusplus
}

View File

@@ -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

View File

@@ -3,20 +3,7 @@
#include <furi.h>
#include <furi_hal.h>
#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);
if(bus->reset_callback(bus->is_short_reset, bus->reset_callback_context)) {
if(onewire_slave_show_presence(bus)) {
bus->error = OneWireSlaveErrorNone;
return true;
}
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;
}
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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"),

View File

@@ -1,115 +1,112 @@
#include <furi_hal_compress.h>
#include "compress.h"
#include <furi.h>
#include <lib/heatshrink/heatshrink_encoder.h>
#include <lib/heatshrink/heatshrink_decoder.h>
#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;
}

96
lib/toolbox/compress.h Normal file
View File

@@ -0,0 +1,96 @@
/**
* @file compress.h
* LZSS based compression HAL API
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#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