mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 20:49:49 +04:00
Merge branch 'dev' into release
This commit is contained in:
16
.drone.yml
16
.drone.yml
@@ -40,9 +40,9 @@ steps:
|
|||||||
image: hfdj/fztools
|
image: hfdj/fztools
|
||||||
pull: never
|
pull: never
|
||||||
commands:
|
commands:
|
||||||
- git clone https://github.com/xMasterX/unleashed-extra-pack.git
|
- git clone https://github.com/xMasterX/all-the-plugins.git
|
||||||
- cp -R unleashed-extra-pack/apps/* assets/resources/apps/
|
- cp -R all-the-plugins/apps/* assets/resources/apps/
|
||||||
- rm -rf unleashed-extra-pack
|
- rm -rf all-the-plugins
|
||||||
- export DIST_SUFFIX=${DRONE_TAG}e
|
- export DIST_SUFFIX=${DRONE_TAG}e
|
||||||
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
|
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
|
||||||
- export FORCE_NO_DIRTY=yes
|
- 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)
|
[-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})
|
[-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:
|
commands:
|
||||||
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
|
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
|
||||||
- chmod +x ./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"
|
- name: "Send extra pack build to telegram"
|
||||||
image: appleboy/drone-telegram
|
image: appleboy/drone-telegram
|
||||||
@@ -281,9 +281,9 @@ steps:
|
|||||||
image: hfdj/fztools
|
image: hfdj/fztools
|
||||||
pull: never
|
pull: never
|
||||||
commands:
|
commands:
|
||||||
- git clone https://github.com/xMasterX/unleashed-extra-pack.git
|
- git clone --branch dev https://github.com/xMasterX/all-the-plugins.git
|
||||||
- cp -R unleashed-extra-pack/apps/* assets/resources/apps/
|
- cp -R all-the-plugins/apps/* assets/resources/apps/
|
||||||
- rm -rf unleashed-extra-pack
|
- rm -rf all-the-plugins
|
||||||
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e
|
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e
|
||||||
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
|
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
|
||||||
- export FORCE_NO_DIRTY=yes
|
- export FORCE_NO_DIRTY=yes
|
||||||
|
|||||||
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -33,4 +33,7 @@
|
|||||||
url = https://github.com/ataradov/free-dap.git
|
url = https://github.com/ataradov/free-dap.git
|
||||||
[submodule "applications/external/subbrute"]
|
[submodule "applications/external/subbrute"]
|
||||||
path = applications/external/subbrute
|
path = applications/external/subbrute
|
||||||
url = https://github.com/derskythe/flipperzero-subbrute.git
|
url = https://github.com/derskythe/flipperzero-subbrute.git
|
||||||
|
[submodule "lib/heatshrink"]
|
||||||
|
path = lib/heatshrink
|
||||||
|
url = https://github.com/flipperdevices/heatshrink.git
|
||||||
|
|||||||
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,16 +1,20 @@
|
|||||||
### New changes
|
### New changes
|
||||||
* Plugins: Fix minesweeper freeze bugs, do some refactoring
|
* If you have copied apps into `apps` folder - remove `apps` folder on your microSD before installing this release to avoid issues!
|
||||||
* Plugins: Update WAV Player, 16 bit support by @LTVA1
|
* Dev Builds: Add extra pack dev branch to avoid "bug" reports with `API mismatch`
|
||||||
* Infrared: Update universal remote assets (by @amec0e | PR #394)
|
* 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)
|
||||||
* 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)
|
* SubGHz: Add manually -> GSN protocol support
|
||||||
* OFW: BadUSB: Script interpreter refactoring
|
* SubGHz: Add 318 and 418 MHz back to hopping list
|
||||||
* OFW: SubGhz: fix Incorrect comparison in subghz_setting_get_hopper_frequency
|
* SubGHz: Fix hopper stuck at 433.42 due to wide range signals -
|
||||||
* OFW: Add one_wire lib to f18, separate ibutton
|
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
|
||||||
* OFW: Improved debugging experience for external apps
|
* Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) -> BadBT Support
|
||||||
* OFW: SD Driver: reinit sd card on error
|
* OFW: Screen streaming improvements
|
||||||
* OFW: OTP programmer: return exit code based on error type
|
* 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)
|
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||||
|
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ Games:
|
|||||||
|
|
||||||
### **Plugins**
|
### **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)
|
## [- TOTP (Authenticator) config description](https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md)
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,12 @@
|
|||||||
#include <one_wire/maxim_crc.h>
|
#include <one_wire/maxim_crc.h>
|
||||||
#include <one_wire/one_wire_host.h>
|
#include <one_wire/one_wire_host.h>
|
||||||
|
|
||||||
|
#include <furi_hal_power.h>
|
||||||
|
|
||||||
#define UPDATE_PERIOD_MS 1000UL
|
#define UPDATE_PERIOD_MS 1000UL
|
||||||
#define TEXT_STORE_SIZE 64U
|
#define TEXT_STORE_SIZE 64U
|
||||||
|
|
||||||
|
#define DS18B20_CMD_SKIP_ROM 0xccU
|
||||||
#define DS18B20_CMD_CONVERT 0x44U
|
#define DS18B20_CMD_CONVERT 0x44U
|
||||||
#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU
|
#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.
|
/* After the reset, a ROM operation must follow.
|
||||||
If there is only one device connected, the "Skip ROM" command is most appropriate
|
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).*/
|
(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.
|
/* After the ROM operation, a device-specific command is issued.
|
||||||
In this case, it's a request to start measuring the temperature. */
|
In this case, it's a request to start measuring the temperature. */
|
||||||
onewire_host_write(onewire, DS18B20_CMD_CONVERT);
|
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.
|
/* After the reset, a ROM operation must follow.
|
||||||
If there is only one device connected, the "Skip ROM" command is most appropriate
|
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).*/
|
(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.
|
/* After the ROM operation, a device-specific command is issued.
|
||||||
This time, it will be the "Read Scratchpad" command which will
|
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 */
|
/* Starts the reader thread and handles the input */
|
||||||
static void example_thermo_run(ExampleThermoContext* context) {
|
static void example_thermo_run(ExampleThermoContext* context) {
|
||||||
|
/* Enable power on external pins */
|
||||||
|
furi_hal_power_enable_otg();
|
||||||
|
|
||||||
/* Configure the hardware in host mode */
|
/* Configure the hardware in host mode */
|
||||||
onewire_host_start(context->onewire);
|
onewire_host_start(context->onewire);
|
||||||
|
|
||||||
@@ -299,6 +305,9 @@ static void example_thermo_run(ExampleThermoContext* context) {
|
|||||||
|
|
||||||
/* Reset the hardware */
|
/* Reset the hardware */
|
||||||
onewire_host_stop(context->onewire);
|
onewire_host_stop(context->onewire);
|
||||||
|
|
||||||
|
/* Disable power on external pins */
|
||||||
|
furi_hal_power_disable_otg();
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************** Initialisation & startup *****************************/
|
/******************** Initialisation & startup *****************************/
|
||||||
|
|||||||
6
applications/external/totp/application.fam
vendored
6
applications/external/totp/application.fam
vendored
@@ -9,10 +9,14 @@ App(
|
|||||||
"dialogs",
|
"dialogs",
|
||||||
"storage",
|
"storage",
|
||||||
"input",
|
"input",
|
||||||
"notification"
|
"notification",
|
||||||
|
"bt"
|
||||||
],
|
],
|
||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
order=20,
|
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_category="Misc",
|
||||||
fap_icon_assets="images",
|
fap_icon_assets="images",
|
||||||
fap_icon="totp_10px.png",
|
fap_icon="totp_10px.png",
|
||||||
|
|||||||
3
applications/external/totp/cli/cli.c
vendored
3
applications/external/totp/cli/cli.c
vendored
@@ -12,6 +12,7 @@
|
|||||||
#include "commands/pin/pin.h"
|
#include "commands/pin/pin.h"
|
||||||
#include "commands/notification/notification.h"
|
#include "commands/notification/notification.h"
|
||||||
#include "commands/reset/reset.h"
|
#include "commands/reset/reset.h"
|
||||||
|
#include "commands/automation/automation.h"
|
||||||
|
|
||||||
static void totp_cli_print_unknown_command(const FuriString* unknown_command) {
|
static void totp_cli_print_unknown_command(const FuriString* unknown_command) {
|
||||||
TOTP_CLI_PRINTF_ERROR(
|
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);
|
totp_cli_command_pin_handle(plugin_state, args, cli);
|
||||||
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_NOTIFICATION) == 0) {
|
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_NOTIFICATION) == 0) {
|
||||||
totp_cli_command_notification_handle(plugin_state, args, cli);
|
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) {
|
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) {
|
||||||
totp_cli_command_reset_handle(cli, cli_context->event_queue);
|
totp_cli_command_reset_handle(cli, cli_context->event_queue);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ void totp_cli_command_add_docopt_usage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void totp_cli_command_add_docopt_arguments() {
|
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() {
|
void totp_cli_command_add_docopt_options() {
|
||||||
|
|||||||
133
applications/external/totp/cli/commands/automation/automation.c
vendored
Normal file
133
applications/external/totp/cli/commands/automation/automation.c
vendored
Normal 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);
|
||||||
|
}
|
||||||
11
applications/external/totp/cli/commands/automation/automation.h
vendored
Normal file
11
applications/external/totp/cli/commands/automation/automation.h
vendored
Normal 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();
|
||||||
@@ -24,7 +24,7 @@ void totp_cli_command_delete_docopt_usage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void totp_cli_command_delete_docopt_arguments() {
|
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() {
|
void totp_cli_command_delete_docopt_options() {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "../pin/pin.h"
|
#include "../pin/pin.h"
|
||||||
#include "../notification/notification.h"
|
#include "../notification/notification.h"
|
||||||
#include "../reset/reset.h"
|
#include "../reset/reset.h"
|
||||||
|
#include "../automation/automation.h"
|
||||||
|
|
||||||
void totp_cli_command_help_docopt_commands() {
|
void totp_cli_command_help_docopt_commands() {
|
||||||
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT
|
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_pin_docopt_usage();
|
||||||
totp_cli_command_notification_docopt_usage();
|
totp_cli_command_notification_docopt_usage();
|
||||||
totp_cli_command_reset_docopt_usage();
|
totp_cli_command_reset_docopt_usage();
|
||||||
|
totp_cli_command_automation_docopt_usage();
|
||||||
cli_nl();
|
cli_nl();
|
||||||
TOTP_CLI_PRINTF("Commands:\r\n");
|
TOTP_CLI_PRINTF("Commands:\r\n");
|
||||||
totp_cli_command_help_docopt_commands();
|
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_pin_docopt_commands();
|
||||||
totp_cli_command_notification_docopt_commands();
|
totp_cli_command_notification_docopt_commands();
|
||||||
totp_cli_command_reset_docopt_commands();
|
totp_cli_command_reset_docopt_commands();
|
||||||
|
totp_cli_command_automation_docopt_commands();
|
||||||
cli_nl();
|
cli_nl();
|
||||||
TOTP_CLI_PRINTF("Arguments:\r\n");
|
TOTP_CLI_PRINTF("Arguments:\r\n");
|
||||||
totp_cli_command_add_docopt_arguments();
|
totp_cli_command_add_docopt_arguments();
|
||||||
totp_cli_command_delete_docopt_arguments();
|
totp_cli_command_delete_docopt_arguments();
|
||||||
totp_cli_command_timezone_docopt_arguments();
|
totp_cli_command_timezone_docopt_arguments();
|
||||||
totp_cli_command_notification_docopt_arguments();
|
totp_cli_command_notification_docopt_arguments();
|
||||||
|
totp_cli_command_automation_docopt_arguments();
|
||||||
cli_nl();
|
cli_nl();
|
||||||
TOTP_CLI_PRINTF("Options:\r\n");
|
TOTP_CLI_PRINTF("Options:\r\n");
|
||||||
totp_cli_command_add_docopt_options();
|
totp_cli_command_add_docopt_options();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "../../../ui/scene_director.h"
|
#include "../../../ui/scene_director.h"
|
||||||
#include "../../cli_helpers.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_NONE "none"
|
||||||
#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "sound"
|
#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "sound"
|
||||||
#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "vibro"
|
#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() {
|
void totp_cli_command_notification_docopt_arguments() {
|
||||||
TOTP_CLI_PRINTF(
|
TOTP_CLI_PRINTF(
|
||||||
" " TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD
|
" " 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_SOUND
|
||||||
", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n");
|
", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ void totp_cli_command_timezone_docopt_usage() {
|
|||||||
|
|
||||||
void totp_cli_command_timezone_docopt_arguments() {
|
void totp_cli_command_timezone_docopt_arguments() {
|
||||||
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE
|
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) {
|
void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
|
||||||
|
|||||||
2
applications/external/totp/features_config.h
vendored
Normal file
2
applications/external/totp/features_config.h
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define TOTP_BADBT_TYPE_ENABLED
|
||||||
|
#define TOTP_BADBT_TYPE_ICON_ENABLED
|
||||||
BIN
applications/external/totp/images/hid_ble_10x7.png
vendored
Normal file
BIN
applications/external/totp/images/hid_ble_10x7.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 158 B |
@@ -4,6 +4,7 @@
|
|||||||
#include "../list/list.h"
|
#include "../list/list.h"
|
||||||
#include "../../types/common.h"
|
#include "../../types/common.h"
|
||||||
#include "../../types/token_info.h"
|
#include "../../types/token_info.h"
|
||||||
|
#include "../../features_config.h"
|
||||||
#include "migrations/config_migration_v1_to_v2.h"
|
#include "migrations/config_migration_v1_to_v2.h"
|
||||||
#include "migrations/config_migration_v2_to_v3.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(
|
flipper_format_write_uint32(
|
||||||
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
|
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();
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
|
||||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||||
@@ -329,6 +338,33 @@ TotpConfigFileUpdateResult
|
|||||||
return update_result;
|
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) {
|
TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) {
|
||||||
Storage* cfg_storage = totp_open_storage();
|
Storage* cfg_storage = totp_open_storage();
|
||||||
FlipperFormat* file;
|
FlipperFormat* file;
|
||||||
@@ -347,6 +383,13 @@ TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginSta
|
|||||||
break;
|
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;
|
update_result = TotpConfigFileUpdateSuccess;
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
@@ -409,6 +452,13 @@ TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const p
|
|||||||
break;
|
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;
|
bool tokens_written = true;
|
||||||
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
|
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
|
||||||
const TokenInfo* token_info = node->data;
|
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;
|
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);
|
} while(false);
|
||||||
|
|
||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
|
|||||||
@@ -103,6 +103,14 @@ TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_tim
|
|||||||
TotpConfigFileUpdateResult
|
TotpConfigFileUpdateResult
|
||||||
totp_config_file_update_notification_method(NotificationMethod new_notification_method);
|
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
|
* @brief Updates application user settings
|
||||||
* @param plugin_state application state
|
* @param plugin_state application state
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#define TOTP_CONFIG_KEY_BASE_IV "BaseIV"
|
#define TOTP_CONFIG_KEY_BASE_IV "BaseIV"
|
||||||
#define TOTP_CONFIG_KEY_PINSET "PinIsSet"
|
#define TOTP_CONFIG_KEY_PINSET "PinIsSet"
|
||||||
#define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod"
|
#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_SHA1_NAME "sha1"
|
||||||
#define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256"
|
#define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256"
|
||||||
|
|||||||
16
applications/external/totp/totp_app.c
vendored
16
applications/external/totp/totp_app.c
vendored
@@ -8,6 +8,7 @@
|
|||||||
#include <notification/notification.h>
|
#include <notification/notification.h>
|
||||||
#include <notification/notification_messages.h>
|
#include <notification/notification_messages.h>
|
||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
|
#include "features_config.h"
|
||||||
#include "services/config/config.h"
|
#include "services/config/config.h"
|
||||||
#include "types/plugin_state.h"
|
#include "types/plugin_state.h"
|
||||||
#include "types/token_info.h"
|
#include "types/token_info.h"
|
||||||
@@ -108,6 +109,14 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +139,13 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
|
|||||||
free(plugin_state->crypto_verify_data);
|
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);
|
furi_mutex_free(plugin_state->mutex);
|
||||||
free(plugin_state);
|
free(plugin_state);
|
||||||
}
|
}
|
||||||
|
|||||||
13
applications/external/totp/types/automation_method.h
vendored
Normal file
13
applications/external/totp/types/automation_method.h
vendored
Normal 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
|
||||||
|
};
|
||||||
17
applications/external/totp/types/plugin_state.h
vendored
17
applications/external/totp/types/plugin_state.h
vendored
@@ -3,9 +3,14 @@
|
|||||||
#include <notification/notification.h>
|
#include <notification/notification.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
|
#include "../features_config.h"
|
||||||
#include "../lib/list/list.h"
|
#include "../lib/list/list.h"
|
||||||
#include "../ui/totp_scenes_enum.h"
|
#include "../ui/totp_scenes_enum.h"
|
||||||
#include "notification_method.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
|
#define TOTP_IV_SIZE 16
|
||||||
|
|
||||||
@@ -92,4 +97,16 @@ typedef struct {
|
|||||||
* @brief Main rendering loop mutex
|
* @brief Main rendering loop mutex
|
||||||
*/
|
*/
|
||||||
FuriMutex* 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;
|
} PluginState;
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ void totp_scene_director_activate_scene(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state) {
|
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:
|
case TotpSceneGenerateToken:
|
||||||
totp_scene_generate_token_deactivate(plugin_state);
|
totp_scene_generate_token_deactivate(plugin_state);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -10,16 +10,35 @@
|
|||||||
#include "../../../services/convert/convert.h"
|
#include "../../../services/convert/convert.h"
|
||||||
#include "../../../lib/roll_value/roll_value.h"
|
#include "../../../lib/roll_value/roll_value.h"
|
||||||
#include "../../../types/nullable.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* 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 {
|
typedef struct {
|
||||||
int8_t tz_offset_hours;
|
int8_t tz_offset_hours;
|
||||||
uint8_t tz_offset_minutes;
|
uint8_t tz_offset_minutes;
|
||||||
bool notification_sound;
|
bool notification_sound;
|
||||||
bool notification_vibro;
|
bool notification_vibro;
|
||||||
|
bool badusb_enabled;
|
||||||
|
#ifdef TOTP_BADBT_TYPE_ENABLED
|
||||||
|
bool badbt_enabled;
|
||||||
|
#endif
|
||||||
uint8_t y_offset;
|
uint8_t y_offset;
|
||||||
TotpNullable_uint16_t current_token_index;
|
TotpNullable_uint16_t current_token_index;
|
||||||
Control selected_control;
|
Control selected_control;
|
||||||
@@ -47,6 +66,10 @@ void totp_scene_app_settings_activate(
|
|||||||
scene_state->tz_offset_minutes = 60.0f * off_dec;
|
scene_state->tz_offset_minutes = 60.0f * off_dec;
|
||||||
scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound;
|
scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound;
|
||||||
scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro;
|
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) {
|
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];
|
char tmp_str[4];
|
||||||
two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]);
|
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(
|
ui_control_select_render(
|
||||||
canvas,
|
canvas,
|
||||||
36,
|
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]);
|
two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]);
|
||||||
canvas_draw_str_aligned(
|
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(
|
ui_control_select_render(
|
||||||
canvas,
|
canvas,
|
||||||
36,
|
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, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications");
|
||||||
canvas_set_font(canvas, FontSecondary);
|
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(
|
ui_control_select_render(
|
||||||
canvas,
|
canvas,
|
||||||
36,
|
36,
|
||||||
@@ -113,7 +136,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
|
|||||||
YES_NO_LIST[scene_state->notification_sound],
|
YES_NO_LIST[scene_state->notification_sound],
|
||||||
scene_state->selected_control == 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(
|
ui_control_select_render(
|
||||||
canvas,
|
canvas,
|
||||||
36,
|
36,
|
||||||
@@ -122,10 +145,43 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
|
|||||||
YES_NO_LIST[scene_state->notification_vibro],
|
YES_NO_LIST[scene_state->notification_vibro],
|
||||||
scene_state->selected_control == 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(
|
ui_control_button_render(
|
||||||
canvas,
|
canvas,
|
||||||
SCREEN_WIDTH_CENTER - 24,
|
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,
|
48,
|
||||||
13,
|
13,
|
||||||
"Confirm",
|
"Confirm",
|
||||||
@@ -152,7 +208,9 @@ bool totp_scene_app_settings_handle_event(
|
|||||||
HoursInput,
|
HoursInput,
|
||||||
ConfirmButton,
|
ConfirmButton,
|
||||||
RollOverflowBehaviorStop);
|
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;
|
scene_state->y_offset = 64;
|
||||||
} else {
|
} else {
|
||||||
scene_state->y_offset = 0;
|
scene_state->y_offset = 0;
|
||||||
@@ -161,7 +219,9 @@ bool totp_scene_app_settings_handle_event(
|
|||||||
case InputKeyDown:
|
case InputKeyDown:
|
||||||
totp_roll_value_uint8_t(
|
totp_roll_value_uint8_t(
|
||||||
&scene_state->selected_control, 1, HoursInput, ConfirmButton, RollOverflowBehaviorStop);
|
&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;
|
scene_state->y_offset = 64;
|
||||||
} else {
|
} else {
|
||||||
scene_state->y_offset = 0;
|
scene_state->y_offset = 0;
|
||||||
@@ -178,7 +238,14 @@ bool totp_scene_app_settings_handle_event(
|
|||||||
scene_state->notification_sound = !scene_state->notification_sound;
|
scene_state->notification_sound = !scene_state->notification_sound;
|
||||||
} else if(scene_state->selected_control == Vibro) {
|
} else if(scene_state->selected_control == Vibro) {
|
||||||
scene_state->notification_vibro = !scene_state->notification_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;
|
break;
|
||||||
case InputKeyLeft:
|
case InputKeyLeft:
|
||||||
if(scene_state->selected_control == HoursInput) {
|
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;
|
scene_state->notification_sound = !scene_state->notification_sound;
|
||||||
} else if(scene_state->selected_control == Vibro) {
|
} else if(scene_state->selected_control == Vibro) {
|
||||||
scene_state->notification_vibro = !scene_state->notification_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;
|
break;
|
||||||
case InputKeyOk:
|
case InputKeyOk:
|
||||||
if(scene_state->selected_control == ConfirmButton) {
|
if(scene_state->selected_control == ConfirmButton) {
|
||||||
@@ -204,12 +278,26 @@ bool totp_scene_app_settings_handle_event(
|
|||||||
(scene_state->notification_vibro ? NotificationMethodVibro :
|
(scene_state->notification_vibro ? NotificationMethodVibro :
|
||||||
NotificationMethodNone);
|
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) !=
|
if(totp_config_file_update_user_settings(plugin_state) !=
|
||||||
TotpConfigFileUpdateSuccess) {
|
TotpConfigFileUpdateSuccess) {
|
||||||
totp_dialogs_config_updating_error(plugin_state);
|
totp_dialogs_config_updating_error(plugin_state);
|
||||||
return false;
|
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) {
|
if(!scene_state->current_token_index.is_null) {
|
||||||
TokenMenuSceneContext generate_scene_context = {
|
TokenMenuSceneContext generate_scene_context = {
|
||||||
.current_token_index = scene_state->current_token_index.value};
|
.current_token_index = scene_state->current_token_index.value};
|
||||||
|
|||||||
@@ -14,7 +14,11 @@
|
|||||||
#include "../../../lib/roll_value/roll_value.h"
|
#include "../../../lib/roll_value/roll_value.h"
|
||||||
#include "../../scene_director.h"
|
#include "../../scene_director.h"
|
||||||
#include "../token_menu/totp_scene_token_menu.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_MARGIN = 3;
|
||||||
static const uint8_t PROGRESS_BAR_HEIGHT = 4;
|
static const uint8_t PROGRESS_BAR_HEIGHT = 4;
|
||||||
@@ -25,9 +29,10 @@ typedef struct {
|
|||||||
bool need_token_update;
|
bool need_token_update;
|
||||||
TokenInfo* current_token;
|
TokenInfo* current_token;
|
||||||
uint32_t last_token_gen_time;
|
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_new_token;
|
||||||
NotificationMessage const** notification_sequence_badusb;
|
NotificationMessage const** notification_sequence_badusb;
|
||||||
|
FuriMutex* last_code_update_sync;
|
||||||
} SceneState;
|
} SceneState;
|
||||||
|
|
||||||
static const NotificationSequence*
|
static const NotificationSequence*
|
||||||
@@ -72,7 +77,7 @@ static const NotificationSequence*
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if(scene_state->notification_sequence_badusb == NULL) {
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
uint8_t length = 3;
|
uint8_t length = 3;
|
||||||
@@ -201,9 +206,28 @@ void totp_scene_generate_token_activate(
|
|||||||
plugin_state->current_scene_state = scene_state;
|
plugin_state->current_scene_state = scene_state;
|
||||||
FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset);
|
FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset);
|
||||||
update_totp_params(plugin_state);
|
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->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
scene_state->type_code_worker_context->string_length = TOTP_TOKEN_DIGITS_MAX_COUNT + 1;
|
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) {
|
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;
|
const TokenInfo* tokenInfo = scene_state->current_token;
|
||||||
|
|
||||||
if(tokenInfo->token != NULL && tokenInfo->token_length > 0) {
|
if(tokenInfo->token != NULL && tokenInfo->token_length > 0) {
|
||||||
furi_mutex_acquire(
|
furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
|
||||||
scene_state->type_code_worker_context->string_sync, FuriWaitForever);
|
|
||||||
size_t key_length;
|
size_t key_length;
|
||||||
uint8_t* key = totp_crypto_decrypt(
|
uint8_t* key = totp_crypto_decrypt(
|
||||||
tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length);
|
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);
|
memset_s(key, key_length, 0, key_length);
|
||||||
free(key);
|
free(key);
|
||||||
} else {
|
} else {
|
||||||
furi_mutex_acquire(
|
furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
|
||||||
scene_state->type_code_worker_context->string_sync, FuriWaitForever);
|
|
||||||
int_token_to_str(0, scene_state->last_code, tokenInfo->digits);
|
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) {
|
if(is_new_token_time) {
|
||||||
notification_message(
|
notification_message(
|
||||||
@@ -327,6 +349,15 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
|
|||||||
canvas_draw_icon(
|
canvas_draw_icon(
|
||||||
canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9);
|
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(
|
bool totp_scene_generate_token_handle_event(
|
||||||
@@ -341,14 +372,27 @@ bool totp_scene_generate_token_handle_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
SceneState* scene_state;
|
SceneState* scene_state;
|
||||||
if(event->input.type == InputTypeLong && event->input.key == InputKeyDown) {
|
if(event->input.type == InputTypeLong) {
|
||||||
scene_state = (SceneState*)plugin_state->current_scene_state;
|
if(event->input.key == InputKeyDown && plugin_state->automation_method & AutomationMethodBadUsb) {
|
||||||
totp_type_code_worker_notify(
|
scene_state = (SceneState*)plugin_state->current_scene_state;
|
||||||
scene_state->type_code_worker_context, TotpTypeCodeWorkerEventType);
|
totp_usb_type_code_worker_notify(
|
||||||
notification_message(
|
scene_state->usb_type_code_worker_context, TotpUsbTypeCodeWorkerEventType);
|
||||||
plugin_state->notification_app,
|
notification_message(
|
||||||
get_notification_sequence_badusb(plugin_state, scene_state));
|
plugin_state->notification_app,
|
||||||
return true;
|
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) {
|
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;
|
if(plugin_state->current_scene_state == NULL) return;
|
||||||
SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
|
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) {
|
if(scene_state->notification_sequence_new_token != NULL) {
|
||||||
free(scene_state->notification_sequence_new_token);
|
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);
|
free(scene_state->notification_sequence_badusb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
furi_mutex_free(scene_state->last_code_update_sync);
|
||||||
|
|
||||||
free(scene_state);
|
free(scene_state);
|
||||||
plugin_state->current_scene_state = NULL;
|
plugin_state->current_scene_state = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
141
applications/external/totp/workers/bt_type_code/bt_type_code.c
vendored
Normal file
141
applications/external/totp/workers/bt_type_code/bt_type_code.c
vendored
Normal 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);
|
||||||
|
}
|
||||||
35
applications/external/totp/workers/bt_type_code/bt_type_code.h
vendored
Normal file
35
applications/external/totp/workers/bt_type_code/bt_type_code.h
vendored
Normal 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);
|
||||||
14
applications/external/totp/workers/constants.c
vendored
Normal file
14
applications/external/totp/workers/constants.c
vendored
Normal 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};
|
||||||
4
applications/external/totp/workers/constants.h
vendored
Normal file
4
applications/external/totp/workers/constants.h
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern const uint8_t hid_number_keys[10];
|
||||||
@@ -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);
|
|
||||||
@@ -1,19 +1,8 @@
|
|||||||
#include "type_code.h"
|
#include "usb_type_code.h"
|
||||||
#include "../../services/convert/convert.h"
|
#include "../../services/convert/convert.h"
|
||||||
|
#include "../constants.h"
|
||||||
|
|
||||||
static const uint8_t hid_number_keys[10] = {
|
static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) {
|
||||||
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) {
|
|
||||||
if(context->usb_mode_prev != NULL) {
|
if(context->usb_mode_prev != NULL) {
|
||||||
furi_hal_usb_set_config(context->usb_mode_prev, NULL);
|
furi_hal_usb_set_config(context->usb_mode_prev, NULL);
|
||||||
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() {
|
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();
|
context->usb_mode_prev = furi_hal_usb_get_config();
|
||||||
furi_hal_usb_unlock();
|
furi_hal_usb_unlock();
|
||||||
furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);
|
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) {
|
static int32_t totp_type_code_worker_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
if(context_mutex == NULL) {
|
if(context_mutex == NULL) {
|
||||||
return 251;
|
return 251;
|
||||||
@@ -64,14 +54,14 @@ static int32_t totp_type_code_worker_callback(void* context) {
|
|||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
uint32_t flags = furi_thread_flags_wait(
|
uint32_t flags = furi_thread_flags_wait(
|
||||||
TotpTypeCodeWorkerEventStop | TotpTypeCodeWorkerEventType,
|
TotpUsbTypeCodeWorkerEventStop | TotpUsbTypeCodeWorkerEventType,
|
||||||
FuriFlagWaitAny,
|
FuriFlagWaitAny,
|
||||||
FuriWaitForever);
|
FuriWaitForever);
|
||||||
furi_check((flags & FuriFlagError) == 0); //-V562
|
furi_check((flags & FuriFlagError) == 0); //-V562
|
||||||
if(flags & TotpTypeCodeWorkerEventStop) break;
|
if(flags & TotpUsbTypeCodeWorkerEventStop) break;
|
||||||
|
|
||||||
if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) {
|
if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) {
|
||||||
if(flags & TotpTypeCodeWorkerEventType) {
|
if(flags & TotpUsbTypeCodeWorkerEventType) {
|
||||||
totp_type_code_worker_type_code(context);
|
totp_type_code_worker_type_code(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,13 +74,18 @@ static int32_t totp_type_code_worker_callback(void* context) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TotpTypeCodeWorkerContext* totp_type_code_worker_start() {
|
TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
|
||||||
TotpTypeCodeWorkerContext* context = malloc(sizeof(TotpTypeCodeWorkerContext));
|
char* code_buf,
|
||||||
|
uint8_t code_buf_length,
|
||||||
|
FuriMutex* code_buf_update_sync) {
|
||||||
|
TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext));
|
||||||
furi_check(context != NULL);
|
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->thread = furi_thread_alloc();
|
||||||
context->usb_mode_prev = NULL;
|
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_stack_size(context->thread, 1024);
|
||||||
furi_thread_set_context(context->thread, context);
|
furi_thread_set_context(context->thread, context);
|
||||||
furi_thread_set_callback(context->thread, totp_type_code_worker_callback);
|
furi_thread_set_callback(context->thread, totp_type_code_worker_callback);
|
||||||
@@ -98,19 +93,18 @@ TotpTypeCodeWorkerContext* totp_type_code_worker_start() {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context) {
|
void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) {
|
||||||
furi_assert(context != NULL);
|
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_join(context->thread);
|
||||||
furi_thread_free(context->thread);
|
furi_thread_free(context->thread);
|
||||||
furi_mutex_free(context->string_sync);
|
|
||||||
totp_type_code_worker_restore_usb_mode(context);
|
totp_type_code_worker_restore_usb_mode(context);
|
||||||
free(context);
|
free(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_type_code_worker_notify(
|
void totp_usb_type_code_worker_notify(
|
||||||
TotpTypeCodeWorkerContext* context,
|
TotpUsbTypeCodeWorkerContext* context,
|
||||||
TotpTypeCodeWorkerEvent event) {
|
TotpUsbTypeCodeWorkerEvent event) {
|
||||||
furi_assert(context != NULL);
|
furi_assert(context != NULL);
|
||||||
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
|
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
|
||||||
}
|
}
|
||||||
30
applications/external/totp/workers/usb_type_code/usb_type_code.h
vendored
Normal file
30
applications/external/totp/workers/usb_type_code/usb_type_code.h
vendored
Normal 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);
|
||||||
@@ -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);
|
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);
|
furi_assert(loader);
|
||||||
|
|
||||||
FuriString* error_message;
|
FuriString* error_message;
|
||||||
@@ -72,6 +72,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
|
|||||||
|
|
||||||
bool file_selected = false;
|
bool file_selected = false;
|
||||||
bool show_error = true;
|
bool show_error = true;
|
||||||
|
bool retry = false;
|
||||||
do {
|
do {
|
||||||
file_selected = true;
|
file_selected = true;
|
||||||
loader->app = flipper_application_alloc(loader->storage, firmware_api_interface);
|
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 =
|
FlipperApplicationPreloadStatus preload_res =
|
||||||
flipper_application_preload(loader->app, furi_string_get_cstr(loader->fap_path));
|
flipper_application_preload(loader->app, furi_string_get_cstr(loader->fap_path));
|
||||||
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
|
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
|
||||||
const char* err_msg = flipper_application_preload_status_to_string(preload_res);
|
if(preload_res == FlipperApplicationPreloadStatusApiMismatch) {
|
||||||
furi_string_printf(error_message, "Preload failed: %s", err_msg);
|
if(!ignore_mismatch) {
|
||||||
FURI_LOG_E(
|
DialogMessage* message = dialog_message_alloc();
|
||||||
TAG,
|
dialog_message_set_header(message, "API Mismatch", 64, 0, AlignCenter, AlignTop);
|
||||||
"FAP Loader failed to preload %s: %s",
|
dialog_message_set_buttons(message, "Cancel", NULL, "Continue");
|
||||||
furi_string_get_cstr(loader->fap_path),
|
dialog_message_set_text(message, "This app might not\nwork correctly\nContinue anyways?", 64, 32, AlignCenter, AlignCenter);
|
||||||
err_msg);
|
if(dialog_message_show(loader->dialogs, message) == DialogMessageButtonRight) {
|
||||||
break;
|
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");
|
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);
|
flipper_application_free(loader->app);
|
||||||
}
|
}
|
||||||
|
|
||||||
return file_selected;
|
return retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool fap_loader_select_app(FapLoader* loader) {
|
static bool fap_loader_select_app(FapLoader* loader) {
|
||||||
@@ -203,12 +219,16 @@ int32_t fap_loader_app(void* p) {
|
|||||||
if(p) {
|
if(p) {
|
||||||
loader = fap_loader_alloc((const char*)p);
|
loader = fap_loader_alloc((const char*)p);
|
||||||
view_dispatcher_switch_to_view(loader->view_dispatcher, 0);
|
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 {
|
} else {
|
||||||
loader = fap_loader_alloc(EXT_PATH("apps"));
|
loader = fap_loader_alloc(EXT_PATH("apps"));
|
||||||
while(fap_loader_select_app(loader)) {
|
while(fap_loader_select_app(loader)) {
|
||||||
view_dispatcher_switch_to_view(loader->view_dispatcher, 0);
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ typedef enum {
|
|||||||
SubmenuIndexSommer_FM_868,
|
SubmenuIndexSommer_FM_868,
|
||||||
SubmenuIndexDTMNeo433,
|
SubmenuIndexDTMNeo433,
|
||||||
SubmenuIndexGibidi433,
|
SubmenuIndexGibidi433,
|
||||||
|
SubmenuIndexGSN,
|
||||||
SubmenuIndexNiceFlo12bit,
|
SubmenuIndexNiceFlo12bit,
|
||||||
SubmenuIndexNiceFlo24bit,
|
SubmenuIndexNiceFlo24bit,
|
||||||
SubmenuIndexNiceFlorS_433_92,
|
SubmenuIndexNiceFlorS_433_92,
|
||||||
|
|||||||
@@ -152,6 +152,12 @@ void subghz_scene_set_type_on_enter(void* context) {
|
|||||||
SubmenuIndexGibidi433,
|
SubmenuIndexGibidi433,
|
||||||
subghz_scene_set_type_submenu_callback,
|
subghz_scene_set_type_submenu_callback,
|
||||||
subghz);
|
subghz);
|
||||||
|
submenu_add_item(
|
||||||
|
subghz->submenu,
|
||||||
|
"KL: GSN 433MHz",
|
||||||
|
SubmenuIndexGSN,
|
||||||
|
subghz_scene_set_type_submenu_callback,
|
||||||
|
subghz);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
subghz->submenu,
|
subghz->submenu,
|
||||||
"KL: Elmes (PL) 433MHz",
|
"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);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case SubmenuIndexIronLogic:
|
||||||
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
|
||||||
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <littlefs/lfs_util.h> // for lfs_tobe32
|
#include <littlefs/lfs_util.h> // for lfs_tobe32
|
||||||
|
|
||||||
#include "toolbox/sha256.h"
|
#include "toolbox/sha256.h"
|
||||||
#include "toolbox/hmac_sha256.h"
|
#include "hmac_sha256.h"
|
||||||
#include "micro-ecc/uECC.h"
|
#include "micro-ecc/uECC.h"
|
||||||
|
|
||||||
#define TAG "U2F"
|
#define TAG "U2F"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
|
|||||||
|
|
||||||
Canvas* canvas_init() {
|
Canvas* canvas_init() {
|
||||||
Canvas* canvas = malloc(sizeof(Canvas));
|
Canvas* canvas = malloc(sizeof(Canvas));
|
||||||
|
canvas->compress_icon = compress_icon_alloc();
|
||||||
|
|
||||||
// Setup u8g2
|
// Setup u8g2
|
||||||
u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||||
@@ -36,6 +37,7 @@ Canvas* canvas_init() {
|
|||||||
|
|
||||||
void canvas_free(Canvas* canvas) {
|
void canvas_free(Canvas* canvas) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
|
compress_icon_free(canvas->compress_icon);
|
||||||
free(canvas);
|
free(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +223,7 @@ void canvas_draw_bitmap(
|
|||||||
x += canvas->offset_x;
|
x += canvas->offset_x;
|
||||||
y += canvas->offset_y;
|
y += canvas->offset_y;
|
||||||
uint8_t* bitmap_data = NULL;
|
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);
|
u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +238,8 @@ void canvas_draw_icon_animation(
|
|||||||
x += canvas->offset_x;
|
x += canvas->offset_x;
|
||||||
y += canvas->offset_y;
|
y += canvas->offset_y;
|
||||||
uint8_t* icon_data = NULL;
|
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(
|
u8g2_DrawXBM(
|
||||||
&canvas->fb,
|
&canvas->fb,
|
||||||
x,
|
x,
|
||||||
@@ -253,7 +256,7 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
|
|||||||
x += canvas->offset_x;
|
x += canvas->offset_x;
|
||||||
y += canvas->offset_y;
|
y += canvas->offset_y;
|
||||||
uint8_t* icon_data = NULL;
|
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);
|
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;
|
x += canvas->offset_x;
|
||||||
y += canvas->offset_y;
|
y += canvas->offset_y;
|
||||||
uint8_t* icon_data = NULL;
|
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);
|
u8g2_DrawXBM(&canvas->fb, x, y, w, h, icon_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include <u8g2.h>
|
#include <u8g2.h>
|
||||||
|
#include <toolbox/compress.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -21,6 +22,7 @@ struct Canvas {
|
|||||||
uint8_t offset_y;
|
uint8_t offset_y;
|
||||||
uint8_t width;
|
uint8_t width;
|
||||||
uint8_t height;
|
uint8_t height;
|
||||||
|
CompressIcon* compress_icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Allocate memory and initialize canvas
|
/** Allocate memory and initialize canvas
|
||||||
|
|||||||
@@ -250,6 +250,7 @@ static void gui_redraw(Gui* gui) {
|
|||||||
p->callback(
|
p->callback(
|
||||||
canvas_get_buffer(gui->canvas),
|
canvas_get_buffer(gui->canvas),
|
||||||
canvas_get_buffer_size(gui->canvas),
|
canvas_get_buffer_size(gui->canvas),
|
||||||
|
canvas_get_orientation(gui->canvas),
|
||||||
p->context);
|
p->context);
|
||||||
}
|
}
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ typedef enum {
|
|||||||
} GuiLayer;
|
} GuiLayer;
|
||||||
|
|
||||||
/** Gui Canvas Commit Callback */
|
/** 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"
|
#define RECORD_GUI "gui"
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,18 @@ typedef struct {
|
|||||||
uint32_t input_counter;
|
uint32_t input_counter;
|
||||||
} RpcGuiSystem;
|
} RpcGuiSystem;
|
||||||
|
|
||||||
static void
|
static const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = {
|
||||||
rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) {
|
[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(data);
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
|
||||||
@@ -44,6 +54,8 @@ static void
|
|||||||
furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size);
|
furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size);
|
||||||
|
|
||||||
memcpy(buffer, 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);
|
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;
|
RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context;
|
||||||
|
|
||||||
|
uint32_t transmit_time = 0;
|
||||||
while(true) {
|
while(true) {
|
||||||
uint32_t flags =
|
uint32_t flags =
|
||||||
furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever);
|
furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever);
|
||||||
|
|
||||||
if(flags & RpcGuiWorkerFlagTransmit) {
|
if(flags & RpcGuiWorkerFlagTransmit) {
|
||||||
|
transmit_time = furi_get_tick();
|
||||||
rpc_send(rpc_gui->session, rpc_gui->transmit_frame);
|
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) {
|
if(flags & RpcGuiWorkerFlagExit) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule assets/protobuf updated: 6460660237...1f6b4a08c5
@@ -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 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 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`.
|
- To build all FAPs, run `./fbt faps` or `./fbt fap_dist`.
|
||||||
|
|
||||||
## FAP assets
|
## FAP assets
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ Your frequencies will be added after default ones
|
|||||||
```
|
```
|
||||||
310000000,
|
310000000,
|
||||||
315000000,
|
315000000,
|
||||||
433420000,
|
318000000,
|
||||||
|
418000000,
|
||||||
433920000,
|
433920000,
|
||||||
868350000,
|
868350000,
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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`.
|
1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests`.
|
||||||
2. Flash the firmware using your preferred method.
|
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.
|
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.
|
**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
|
## Adding unit tests
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ See [test_index.c](applications/debug/unit_tests/test_index.c) for the complete
|
|||||||
|
|
||||||
#### Entry point
|
#### 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
|
#### 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.
|
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:
|
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).
|
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).
|
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.
|
4. Update the [assets](/assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass.
|
||||||
|
|
||||||
##### Test data format
|
##### Test data format
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,18.2,,
|
Version,+,20.0,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.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.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.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_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_cortex.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_debug.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/mlib/m-variant.h,,
|
||||||
Header,+,lib/one_wire/maxim_crc.h,,
|
Header,+,lib/one_wire/maxim_crc.h,,
|
||||||
Header,+,lib/one_wire/one_wire_host.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/one_wire/one_wire_slave.h,,
|
||||||
Header,+,lib/print/wrappers.h,,
|
Header,+,lib/print/wrappers.h,,
|
||||||
Header,+,lib/toolbox/args.h,,
|
Header,+,lib/toolbox/args.h,,
|
||||||
Header,+,lib/toolbox/crc32_calc.h,,
|
Header,+,lib/toolbox/crc32_calc.h,,
|
||||||
Header,+,lib/toolbox/dir_walk.h,,
|
Header,+,lib/toolbox/dir_walk.h,,
|
||||||
Header,+,lib/toolbox/float_tools.h,,
|
Header,+,lib/toolbox/float_tools.h,,
|
||||||
Header,+,lib/toolbox/hmac_sha256.h,,
|
|
||||||
Header,+,lib/toolbox/manchester_decoder.h,,
|
Header,+,lib/toolbox/manchester_decoder.h,,
|
||||||
Header,+,lib/toolbox/manchester_encoder.h,,
|
Header,+,lib/toolbox/manchester_encoder.h,,
|
||||||
Header,+,lib/toolbox/md5.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/protocols/protocol_dict.h,,
|
||||||
Header,+,lib/toolbox/random_name.h,,
|
Header,+,lib/toolbox/random_name.h,,
|
||||||
Header,+,lib/toolbox/saved_struct.h,,
|
Header,+,lib/toolbox/saved_struct.h,,
|
||||||
|
Header,+,lib/toolbox/sha256.h,,
|
||||||
Header,+,lib/toolbox/stream/buffered_file_stream.h,,
|
Header,+,lib/toolbox/stream/buffered_file_stream.h,,
|
||||||
Header,+,lib/toolbox/stream/file_stream.h,,
|
Header,+,lib/toolbox/stream/file_stream.h,,
|
||||||
Header,+,lib/toolbox/stream/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_suspend_tick,void,
|
||||||
Function,-,furi_hal_clock_switch_to_hsi,void,
|
Function,-,furi_hal_clock_switch_to_hsi,void,
|
||||||
Function,-,furi_hal_clock_switch_to_pll,void,
|
Function,-,furi_hal_clock_switch_to_pll,void,
|
||||||
Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t
|
Function,-,compress_alloc,Compress*,uint16_t
|
||||||
Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
Function,-,compress_decode,_Bool,"Compress*, 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,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||||
Function,-,furi_hal_compress_free,void,FuriHalCompress*
|
Function,-,compress_free,void,Compress*
|
||||||
Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**"
|
Function,-,compress_icon_decode,void,"const uint8_t*, uint8_t**"
|
||||||
Function,-,furi_hal_compress_icon_init,void,
|
Function,-,compress_icon_init,void,
|
||||||
Function,+,furi_hal_console_disable,void,
|
Function,+,furi_hal_console_disable,void,
|
||||||
Function,+,furi_hal_console_enable,void,
|
Function,+,furi_hal_console_enable,void,
|
||||||
Function,+,furi_hal_console_init,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,_Bool,
|
||||||
Function,+,hal_sd_detect_init,void,
|
Function,+,hal_sd_detect_init,void,
|
||||||
Function,+,hal_sd_detect_set_low,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_alloc,IconAnimation*,const Icon*
|
||||||
Function,+,icon_animation_free,void,IconAnimation*
|
Function,+,icon_animation_free,void,IconAnimation*
|
||||||
Function,+,icon_animation_get_height,uint8_t,const 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_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t"
|
||||||
Function,+,onewire_host_reset,_Bool,OneWireHost*
|
Function,+,onewire_host_reset,_Bool,OneWireHost*
|
||||||
Function,+,onewire_host_reset_search,void,OneWireHost*
|
Function,+,onewire_host_reset_search,void,OneWireHost*
|
||||||
Function,+,onewire_host_search,uint8_t,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
|
Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
|
||||||
Function,+,onewire_host_skip,void,OneWireHost*
|
Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool"
|
||||||
Function,+,onewire_host_start,void,OneWireHost*
|
Function,+,onewire_host_start,void,OneWireHost*
|
||||||
Function,+,onewire_host_stop,void,OneWireHost*
|
Function,+,onewire_host_stop,void,OneWireHost*
|
||||||
Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t"
|
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,_Bool,"OneWireSlave*, const uint8_t*, size_t"
|
||||||
Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool"
|
Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool"
|
||||||
Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*"
|
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_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*"
|
||||||
Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*"
|
Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*"
|
||||||
Function,+,onewire_slave_start,void,OneWireSlave*
|
Function,+,onewire_slave_start,void,OneWireSlave*
|
||||||
|
|||||||
|
@@ -1,5 +1,6 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <furi_hal_mpu.h>
|
#include <furi_hal_mpu.h>
|
||||||
|
#include <furi_hal_memory.h>
|
||||||
|
|
||||||
#include <stm32wbxx_ll_cortex.h>
|
#include <stm32wbxx_ll_cortex.h>
|
||||||
|
|
||||||
@@ -7,29 +8,20 @@
|
|||||||
|
|
||||||
void furi_hal_init_early() {
|
void furi_hal_init_early() {
|
||||||
furi_hal_cortex_init_early();
|
furi_hal_cortex_init_early();
|
||||||
|
|
||||||
furi_hal_clock_init_early();
|
furi_hal_clock_init_early();
|
||||||
|
|
||||||
furi_hal_resources_init_early();
|
furi_hal_resources_init_early();
|
||||||
|
|
||||||
furi_hal_os_init();
|
furi_hal_os_init();
|
||||||
|
|
||||||
furi_hal_spi_config_init_early();
|
furi_hal_spi_config_init_early();
|
||||||
|
|
||||||
furi_hal_i2c_init_early();
|
furi_hal_i2c_init_early();
|
||||||
furi_hal_light_init();
|
furi_hal_light_init();
|
||||||
|
|
||||||
furi_hal_rtc_init_early();
|
furi_hal_rtc_init_early();
|
||||||
}
|
}
|
||||||
|
|
||||||
void furi_hal_deinit_early() {
|
void furi_hal_deinit_early() {
|
||||||
furi_hal_rtc_deinit_early();
|
furi_hal_rtc_deinit_early();
|
||||||
|
|
||||||
furi_hal_i2c_deinit_early();
|
furi_hal_i2c_deinit_early();
|
||||||
furi_hal_spi_config_deinit_early();
|
furi_hal_spi_config_deinit_early();
|
||||||
|
|
||||||
furi_hal_resources_deinit_early();
|
furi_hal_resources_deinit_early();
|
||||||
|
|
||||||
furi_hal_clock_deinit_early();
|
furi_hal_clock_deinit_early();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,40 +30,24 @@ void furi_hal_init() {
|
|||||||
furi_hal_clock_init();
|
furi_hal_clock_init();
|
||||||
furi_hal_console_init();
|
furi_hal_console_init();
|
||||||
furi_hal_rtc_init();
|
furi_hal_rtc_init();
|
||||||
|
|
||||||
furi_hal_interrupt_init();
|
furi_hal_interrupt_init();
|
||||||
|
|
||||||
furi_hal_flash_init();
|
furi_hal_flash_init();
|
||||||
|
|
||||||
furi_hal_resources_init();
|
furi_hal_resources_init();
|
||||||
FURI_LOG_I(TAG, "GPIO OK");
|
|
||||||
|
|
||||||
furi_hal_version_init();
|
furi_hal_version_init();
|
||||||
|
|
||||||
furi_hal_spi_config_init();
|
furi_hal_spi_config_init();
|
||||||
furi_hal_spi_dma_init();
|
furi_hal_spi_dma_init();
|
||||||
|
|
||||||
furi_hal_speaker_init();
|
furi_hal_speaker_init();
|
||||||
FURI_LOG_I(TAG, "Speaker OK");
|
|
||||||
|
|
||||||
furi_hal_crypto_init();
|
furi_hal_crypto_init();
|
||||||
|
|
||||||
// USB
|
|
||||||
#ifndef FURI_RAM_EXEC
|
|
||||||
furi_hal_usb_init();
|
|
||||||
FURI_LOG_I(TAG, "USB OK");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
furi_hal_i2c_init();
|
furi_hal_i2c_init();
|
||||||
|
|
||||||
// High Level
|
|
||||||
furi_hal_power_init();
|
furi_hal_power_init();
|
||||||
furi_hal_light_init();
|
furi_hal_light_init();
|
||||||
|
furi_hal_bt_init();
|
||||||
|
furi_hal_memory_init();
|
||||||
|
|
||||||
#ifndef FURI_RAM_EXEC
|
#ifndef FURI_RAM_EXEC
|
||||||
|
furi_hal_usb_init();
|
||||||
furi_hal_vibro_init();
|
furi_hal_vibro_init();
|
||||||
#endif
|
#endif
|
||||||
furi_hal_bt_init();
|
|
||||||
furi_hal_compress_icon_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void furi_hal_switch(void* address) {
|
void furi_hal_switch(void* address) {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <stm32wbxx_ll_rcc.h>
|
#include <stm32wbxx_ll_rcc.h>
|
||||||
#include <stm32wbxx_ll_pwr.h>
|
#include <stm32wbxx_ll_pwr.h>
|
||||||
|
|
||||||
|
#define TAG "FuriHalResources"
|
||||||
|
|
||||||
const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8};
|
const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8};
|
||||||
const GpioPin ibutton_gpio = {.port = GPIOB, .pin = LL_GPIO_PIN_14};
|
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_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||||
NVIC_EnableIRQ(EXTI15_10_IRQn);
|
NVIC_EnableIRQ(EXTI15_10_IRQn);
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Init OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {
|
int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,18.2,,
|
Version,+,20.0,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.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.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.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_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_cortex.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_debug.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/nfc/nfc_device.h,,
|
||||||
Header,+,lib/one_wire/maxim_crc.h,,
|
Header,+,lib/one_wire/maxim_crc.h,,
|
||||||
Header,+,lib/one_wire/one_wire_host.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/one_wire/one_wire_slave.h,,
|
||||||
Header,+,lib/print/wrappers.h,,
|
Header,+,lib/print/wrappers.h,,
|
||||||
Header,+,lib/subghz/blocks/const.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/crc32_calc.h,,
|
||||||
Header,+,lib/toolbox/dir_walk.h,,
|
Header,+,lib/toolbox/dir_walk.h,,
|
||||||
Header,+,lib/toolbox/float_tools.h,,
|
Header,+,lib/toolbox/float_tools.h,,
|
||||||
Header,+,lib/toolbox/hmac_sha256.h,,
|
|
||||||
Header,+,lib/toolbox/manchester_decoder.h,,
|
Header,+,lib/toolbox/manchester_decoder.h,,
|
||||||
Header,+,lib/toolbox/manchester_encoder.h,,
|
Header,+,lib/toolbox/manchester_encoder.h,,
|
||||||
Header,+,lib/toolbox/md5.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/protocols/protocol_dict.h,,
|
||||||
Header,+,lib/toolbox/random_name.h,,
|
Header,+,lib/toolbox/random_name.h,,
|
||||||
Header,+,lib/toolbox/saved_struct.h,,
|
Header,+,lib/toolbox/saved_struct.h,,
|
||||||
|
Header,+,lib/toolbox/sha256.h,,
|
||||||
Header,+,lib/toolbox/stream/buffered_file_stream.h,,
|
Header,+,lib/toolbox/stream/buffered_file_stream.h,,
|
||||||
Header,+,lib/toolbox/stream/file_stream.h,,
|
Header,+,lib/toolbox/stream/file_stream.h,,
|
||||||
Header,+,lib/toolbox/stream/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_alloc,CompositeApiResolver*,
|
||||||
Function,+,composite_api_resolver_free,void,CompositeApiResolver*
|
Function,+,composite_api_resolver_free,void,CompositeApiResolver*
|
||||||
Function,+,composite_api_resolver_get,const ElfApiInterface*,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,-,copysign,double,"double, double"
|
||||||
Function,-,copysignf,float,"float, float"
|
Function,-,copysignf,float,"float, float"
|
||||||
Function,-,copysignl,long double,"long double, long double"
|
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_suspend_tick,void,
|
||||||
Function,-,furi_hal_clock_switch_to_hsi,void,
|
Function,-,furi_hal_clock_switch_to_hsi,void,
|
||||||
Function,-,furi_hal_clock_switch_to_pll,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_disable,void,
|
||||||
Function,+,furi_hal_console_enable,void,
|
Function,+,furi_hal_console_enable,void,
|
||||||
Function,+,furi_hal_console_init,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,_Bool,
|
||||||
Function,+,hal_sd_detect_init,void,
|
Function,+,hal_sd_detect_init,void,
|
||||||
Function,+,hal_sd_detect_set_low,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,-,hypot,double,"double, double"
|
||||||
Function,-,hypotf,float,"float, float"
|
Function,-,hypotf,float,"float, float"
|
||||||
Function,-,hypotl,long double,"long double, long double"
|
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_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t"
|
||||||
Function,+,onewire_host_reset,_Bool,OneWireHost*
|
Function,+,onewire_host_reset,_Bool,OneWireHost*
|
||||||
Function,+,onewire_host_reset_search,void,OneWireHost*
|
Function,+,onewire_host_reset_search,void,OneWireHost*
|
||||||
Function,+,onewire_host_search,uint8_t,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
|
Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
|
||||||
Function,+,onewire_host_skip,void,OneWireHost*
|
Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool"
|
||||||
Function,+,onewire_host_start,void,OneWireHost*
|
Function,+,onewire_host_start,void,OneWireHost*
|
||||||
Function,+,onewire_host_stop,void,OneWireHost*
|
Function,+,onewire_host_stop,void,OneWireHost*
|
||||||
Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t"
|
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,_Bool,"OneWireSlave*, const uint8_t*, size_t"
|
||||||
Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool"
|
Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool"
|
||||||
Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*"
|
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_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*"
|
||||||
Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*"
|
Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*"
|
||||||
Function,+,onewire_slave_start,void,OneWireSlave*
|
Function,+,onewire_slave_start,void,OneWireSlave*
|
||||||
|
|||||||
|
@@ -8,29 +8,20 @@
|
|||||||
|
|
||||||
void furi_hal_init_early() {
|
void furi_hal_init_early() {
|
||||||
furi_hal_cortex_init_early();
|
furi_hal_cortex_init_early();
|
||||||
|
|
||||||
furi_hal_clock_init_early();
|
furi_hal_clock_init_early();
|
||||||
|
|
||||||
furi_hal_resources_init_early();
|
furi_hal_resources_init_early();
|
||||||
|
|
||||||
furi_hal_os_init();
|
furi_hal_os_init();
|
||||||
|
|
||||||
furi_hal_spi_config_init_early();
|
furi_hal_spi_config_init_early();
|
||||||
|
|
||||||
furi_hal_i2c_init_early();
|
furi_hal_i2c_init_early();
|
||||||
furi_hal_light_init();
|
furi_hal_light_init();
|
||||||
|
|
||||||
furi_hal_rtc_init_early();
|
furi_hal_rtc_init_early();
|
||||||
}
|
}
|
||||||
|
|
||||||
void furi_hal_deinit_early() {
|
void furi_hal_deinit_early() {
|
||||||
furi_hal_rtc_deinit_early();
|
furi_hal_rtc_deinit_early();
|
||||||
|
|
||||||
furi_hal_i2c_deinit_early();
|
furi_hal_i2c_deinit_early();
|
||||||
furi_hal_spi_config_deinit_early();
|
furi_hal_spi_config_deinit_early();
|
||||||
|
|
||||||
furi_hal_resources_deinit_early();
|
furi_hal_resources_deinit_early();
|
||||||
|
|
||||||
furi_hal_clock_deinit_early();
|
furi_hal_clock_deinit_early();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,40 +30,23 @@ void furi_hal_init() {
|
|||||||
furi_hal_clock_init();
|
furi_hal_clock_init();
|
||||||
furi_hal_console_init();
|
furi_hal_console_init();
|
||||||
furi_hal_rtc_init();
|
furi_hal_rtc_init();
|
||||||
|
|
||||||
furi_hal_interrupt_init();
|
furi_hal_interrupt_init();
|
||||||
|
|
||||||
furi_hal_flash_init();
|
furi_hal_flash_init();
|
||||||
|
|
||||||
furi_hal_resources_init();
|
furi_hal_resources_init();
|
||||||
FURI_LOG_I(TAG, "GPIO OK");
|
|
||||||
|
|
||||||
furi_hal_version_init();
|
furi_hal_version_init();
|
||||||
|
|
||||||
furi_hal_spi_config_init();
|
furi_hal_spi_config_init();
|
||||||
furi_hal_spi_dma_init();
|
furi_hal_spi_dma_init();
|
||||||
|
|
||||||
furi_hal_ibutton_init();
|
furi_hal_ibutton_init();
|
||||||
FURI_LOG_I(TAG, "iButton OK");
|
|
||||||
furi_hal_speaker_init();
|
furi_hal_speaker_init();
|
||||||
FURI_LOG_I(TAG, "Speaker OK");
|
|
||||||
|
|
||||||
furi_hal_crypto_init();
|
furi_hal_crypto_init();
|
||||||
|
|
||||||
furi_hal_i2c_init();
|
furi_hal_i2c_init();
|
||||||
|
|
||||||
// High Level
|
|
||||||
furi_hal_power_init();
|
furi_hal_power_init();
|
||||||
furi_hal_light_init();
|
furi_hal_light_init();
|
||||||
|
|
||||||
furi_hal_bt_init();
|
furi_hal_bt_init();
|
||||||
furi_hal_memory_init();
|
furi_hal_memory_init();
|
||||||
furi_hal_compress_icon_init();
|
|
||||||
|
|
||||||
#ifndef FURI_RAM_EXEC
|
#ifndef FURI_RAM_EXEC
|
||||||
// USB
|
|
||||||
furi_hal_usb_init();
|
furi_hal_usb_init();
|
||||||
FURI_LOG_I(TAG, "USB OK");
|
|
||||||
furi_hal_vibro_init();
|
furi_hal_vibro_init();
|
||||||
furi_hal_subghz_init();
|
furi_hal_subghz_init();
|
||||||
furi_hal_nfc_init();
|
furi_hal_nfc_init();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
|
#define TAG "FuriHalIbutton"
|
||||||
#define FURI_HAL_IBUTTON_TIMER TIM1
|
#define FURI_HAL_IBUTTON_TIMER TIM1
|
||||||
#define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16
|
#define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16
|
||||||
|
|
||||||
@@ -33,6 +34,8 @@ static void furi_hal_ibutton_emulate_isr() {
|
|||||||
void furi_hal_ibutton_init() {
|
void furi_hal_ibutton_init() {
|
||||||
furi_hal_ibutton = malloc(sizeof(FuriHalIbutton));
|
furi_hal_ibutton = malloc(sizeof(FuriHalIbutton));
|
||||||
furi_hal_ibutton->state = FuriHalIbuttonStateIdle;
|
furi_hal_ibutton->state = FuriHalIbuttonStateIdle;
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Init OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
void furi_hal_ibutton_emulate_start(
|
void furi_hal_ibutton_emulate_start(
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <stm32wbxx_ll_rcc.h>
|
#include <stm32wbxx_ll_rcc.h>
|
||||||
#include <stm32wbxx_ll_pwr.h>
|
#include <stm32wbxx_ll_pwr.h>
|
||||||
|
|
||||||
|
#define TAG "FuriHalResources"
|
||||||
|
|
||||||
const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
|
const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
|
||||||
const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_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_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||||
NVIC_EnableIRQ(EXTI15_10_IRQn);
|
NVIC_EnableIRQ(EXTI15_10_IRQn);
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Init OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {
|
int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ static void furi_hal_usart_init(uint32_t baud) {
|
|||||||
while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1))
|
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));
|
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);
|
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));
|
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 (*cb)(UartIrqEvent ev, uint8_t data, void* ctx),
|
||||||
void* ctx) {
|
void* ctx) {
|
||||||
if(cb == NULL) {
|
if(cb == NULL) {
|
||||||
if(ch == FuriHalUartIdUSART1)
|
if(ch == FuriHalUartIdUSART1) {
|
||||||
NVIC_DisableIRQ(USART1_IRQn);
|
NVIC_DisableIRQ(USART1_IRQn);
|
||||||
else if(ch == FuriHalUartIdLPUART1)
|
LL_USART_DisableIT_RXNE_RXFNE(USART1);
|
||||||
|
} else if(ch == FuriHalUartIdLPUART1) {
|
||||||
NVIC_DisableIRQ(LPUART1_IRQn);
|
NVIC_DisableIRQ(LPUART1_IRQn);
|
||||||
|
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
|
||||||
|
}
|
||||||
irq_cb[ch] = cb;
|
irq_cb[ch] = cb;
|
||||||
irq_ctx[ch] = ctx;
|
irq_ctx[ch] = ctx;
|
||||||
} else {
|
} else {
|
||||||
irq_ctx[ch] = ctx;
|
irq_ctx[ch] = ctx;
|
||||||
irq_cb[ch] = cb;
|
irq_cb[ch] = cb;
|
||||||
if(ch == FuriHalUartIdUSART1)
|
if(ch == FuriHalUartIdUSART1) {
|
||||||
NVIC_EnableIRQ(USART1_IRQn);
|
NVIC_EnableIRQ(USART1_IRQn);
|
||||||
else if(ch == FuriHalUartIdLPUART1)
|
LL_USART_EnableIT_RXNE_RXFNE(USART1);
|
||||||
|
} else if(ch == FuriHalUartIdLPUART1) {
|
||||||
NVIC_EnableIRQ(LPUART1_IRQn);
|
NVIC_EnableIRQ(LPUART1_IRQn);
|
||||||
|
LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "usb_hid.h"
|
#include "usb_hid.h"
|
||||||
|
|
||||||
#define HID_EP_IN 0x81
|
#define HID_EP_IN 0x81
|
||||||
#define HID_EP_OUT 0x01
|
|
||||||
#define HID_EP_SZ 0x10
|
#define HID_EP_SZ 0x10
|
||||||
|
|
||||||
#define HID_INTERVAL 2
|
#define HID_INTERVAL 2
|
||||||
@@ -16,17 +15,15 @@
|
|||||||
#define HID_VID_DEFAULT 0x046D
|
#define HID_VID_DEFAULT 0x046D
|
||||||
#define HID_PID_DEFAULT 0xC529
|
#define HID_PID_DEFAULT 0xC529
|
||||||
|
|
||||||
struct HidIadDescriptor {
|
struct HidIntfDescriptor {
|
||||||
struct usb_iad_descriptor hid_iad;
|
|
||||||
struct usb_interface_descriptor hid;
|
struct usb_interface_descriptor hid;
|
||||||
struct usb_hid_descriptor hid_desc;
|
struct usb_hid_descriptor hid_desc;
|
||||||
struct usb_endpoint_descriptor hid_ep_in;
|
struct usb_endpoint_descriptor hid_ep_in;
|
||||||
struct usb_endpoint_descriptor hid_ep_out;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HidConfigDescriptor {
|
struct HidConfigDescriptor {
|
||||||
struct usb_config_descriptor config;
|
struct usb_config_descriptor config;
|
||||||
struct HidIadDescriptor iad_0;
|
struct HidIntfDescriptor intf_0;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
enum HidReportId {
|
enum HidReportId {
|
||||||
@@ -35,78 +32,98 @@ enum HidReportId {
|
|||||||
ReportIdConsumer = 3,
|
ReportIdConsumer = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* HID report: keyboard+mouse */
|
/* HID report descriptor: keyboard + mouse + consumer control */
|
||||||
static const uint8_t hid_report_desc[] = {
|
static const uint8_t hid_report_desc[] = {
|
||||||
|
// clang-format off
|
||||||
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
HID_USAGE(HID_DESKTOP_KEYBOARD),
|
HID_USAGE(HID_DESKTOP_KEYBOARD),
|
||||||
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
HID_REPORT_ID(ReportIdKeyboard),
|
HID_REPORT_ID(ReportIdKeyboard),
|
||||||
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
// Keyboard report
|
||||||
HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
|
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
||||||
HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
|
HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
|
||||||
HID_LOGICAL_MINIMUM(0),
|
HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
|
||||||
HID_LOGICAL_MAXIMUM(1),
|
HID_LOGICAL_MINIMUM(0),
|
||||||
HID_REPORT_SIZE(1),
|
HID_LOGICAL_MAXIMUM(1),
|
||||||
HID_REPORT_COUNT(8),
|
HID_REPORT_SIZE(1),
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
HID_REPORT_COUNT(8),
|
||||||
HID_REPORT_COUNT(1),
|
// Input - Modifier keys byte
|
||||||
HID_REPORT_SIZE(8),
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
|
||||||
HID_USAGE_PAGE(HID_PAGE_LED),
|
HID_REPORT_COUNT(1),
|
||||||
HID_REPORT_COUNT(8),
|
HID_REPORT_SIZE(8),
|
||||||
HID_REPORT_SIZE(1),
|
// Input - Reserved byte
|
||||||
HID_USAGE_MINIMUM(1),
|
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
HID_USAGE_MAXIMUM(8),
|
|
||||||
HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
HID_USAGE_PAGE(HID_PAGE_LED),
|
||||||
HID_REPORT_COUNT(HID_KB_MAX_KEYS),
|
HID_REPORT_COUNT(8),
|
||||||
HID_REPORT_SIZE(8),
|
HID_REPORT_SIZE(1),
|
||||||
HID_LOGICAL_MINIMUM(0),
|
HID_USAGE_MINIMUM(1),
|
||||||
HID_LOGICAL_MAXIMUM(101),
|
HID_USAGE_MAXIMUM(8),
|
||||||
HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
|
// Output - LEDs
|
||||||
HID_USAGE_MINIMUM(0),
|
HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
HID_USAGE_MAXIMUM(101),
|
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | 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_END_COLLECTION,
|
||||||
|
|
||||||
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
HID_USAGE(HID_DESKTOP_MOUSE),
|
HID_USAGE(HID_DESKTOP_MOUSE),
|
||||||
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
HID_USAGE(HID_DESKTOP_POINTER),
|
HID_USAGE(HID_DESKTOP_POINTER),
|
||||||
HID_COLLECTION(HID_PHYSICAL_COLLECTION),
|
HID_COLLECTION(HID_PHYSICAL_COLLECTION),
|
||||||
HID_REPORT_ID(ReportIdMouse),
|
HID_REPORT_ID(ReportIdMouse),
|
||||||
HID_USAGE_PAGE(HID_PAGE_BUTTON),
|
// Mouse report
|
||||||
HID_USAGE_MINIMUM(1),
|
HID_USAGE_PAGE(HID_PAGE_BUTTON),
|
||||||
HID_USAGE_MAXIMUM(3),
|
HID_USAGE_MINIMUM(1),
|
||||||
HID_LOGICAL_MINIMUM(0),
|
HID_USAGE_MAXIMUM(3),
|
||||||
HID_LOGICAL_MAXIMUM(1),
|
HID_LOGICAL_MINIMUM(0),
|
||||||
HID_REPORT_COUNT(3),
|
HID_LOGICAL_MAXIMUM(1),
|
||||||
HID_REPORT_SIZE(1),
|
HID_REPORT_COUNT(3),
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
HID_REPORT_SIZE(1),
|
||||||
HID_REPORT_SIZE(1),
|
// Input - Mouse keys
|
||||||
HID_REPORT_COUNT(5),
|
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
|
||||||
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
HID_REPORT_SIZE(1),
|
||||||
HID_USAGE(HID_DESKTOP_X),
|
HID_REPORT_COUNT(5),
|
||||||
HID_USAGE(HID_DESKTOP_Y),
|
// Input - Mouse keys padding
|
||||||
HID_USAGE(HID_DESKTOP_WHEEL),
|
HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||||
HID_LOGICAL_MINIMUM(-127),
|
|
||||||
HID_LOGICAL_MAXIMUM(127),
|
HID_USAGE_PAGE(HID_PAGE_DESKTOP),
|
||||||
HID_REPORT_SIZE(8),
|
HID_USAGE(HID_DESKTOP_X),
|
||||||
HID_REPORT_COUNT(3),
|
HID_USAGE(HID_DESKTOP_Y),
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
|
HID_USAGE(HID_DESKTOP_WHEEL),
|
||||||
HID_END_COLLECTION,
|
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_END_COLLECTION,
|
||||||
|
|
||||||
HID_USAGE_PAGE(HID_PAGE_CONSUMER),
|
HID_USAGE_PAGE(HID_PAGE_CONSUMER),
|
||||||
HID_USAGE(HID_CONSUMER_CONTROL),
|
HID_USAGE(HID_CONSUMER_CONTROL),
|
||||||
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||||
HID_REPORT_ID(ReportIdConsumer),
|
HID_REPORT_ID(ReportIdConsumer),
|
||||||
HID_LOGICAL_MINIMUM(0),
|
// Consumer report
|
||||||
HID_RI_LOGICAL_MAXIMUM(16, 0x3FF),
|
HID_LOGICAL_MINIMUM(0),
|
||||||
HID_USAGE_MINIMUM(0),
|
HID_RI_LOGICAL_MAXIMUM(16, 0x3FF),
|
||||||
HID_RI_USAGE_MAXIMUM(16, 0x3FF),
|
HID_USAGE_MINIMUM(0),
|
||||||
HID_REPORT_COUNT(HID_CONSUMER_MAX_KEYS),
|
HID_RI_USAGE_MAXIMUM(16, 0x3FF),
|
||||||
HID_REPORT_SIZE(16),
|
HID_REPORT_COUNT(HID_CONSUMER_MAX_KEYS),
|
||||||
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
|
HID_REPORT_SIZE(16),
|
||||||
|
// Input - Consumer control keys
|
||||||
|
HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
|
||||||
HID_END_COLLECTION,
|
HID_END_COLLECTION,
|
||||||
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Device descriptor */
|
/* Device descriptor */
|
||||||
@@ -114,9 +131,9 @@ static struct usb_device_descriptor hid_device_desc = {
|
|||||||
.bLength = sizeof(struct usb_device_descriptor),
|
.bLength = sizeof(struct usb_device_descriptor),
|
||||||
.bDescriptorType = USB_DTYPE_DEVICE,
|
.bDescriptorType = USB_DTYPE_DEVICE,
|
||||||
.bcdUSB = VERSION_BCD(2, 0, 0),
|
.bcdUSB = VERSION_BCD(2, 0, 0),
|
||||||
.bDeviceClass = USB_CLASS_IAD,
|
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||||
.bDeviceSubClass = USB_SUBCLASS_IAD,
|
.bDeviceSubClass = USB_SUBCLASS_NONE,
|
||||||
.bDeviceProtocol = USB_PROTO_IAD,
|
.bDeviceProtocol = USB_PROTO_NONE,
|
||||||
.bMaxPacketSize0 = USB_EP0_SIZE,
|
.bMaxPacketSize0 = USB_EP0_SIZE,
|
||||||
.idVendor = HID_VID_DEFAULT,
|
.idVendor = HID_VID_DEFAULT,
|
||||||
.idProduct = HID_PID_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,
|
.bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
|
||||||
.bMaxPower = USB_CFG_POWER_MA(100),
|
.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 =
|
.hid =
|
||||||
{
|
{
|
||||||
.bLength = sizeof(struct usb_interface_descriptor),
|
.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
.bDescriptorType = USB_DTYPE_INTERFACE,
|
.bDescriptorType = USB_DTYPE_INTERFACE,
|
||||||
.bInterfaceNumber = 0,
|
.bInterfaceNumber = 0,
|
||||||
.bAlternateSetting = 0,
|
.bAlternateSetting = 0,
|
||||||
.bNumEndpoints = 2,
|
.bNumEndpoints = 1,
|
||||||
.bInterfaceClass = USB_CLASS_HID,
|
.bInterfaceClass = USB_CLASS_HID,
|
||||||
.bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT,
|
.bInterfaceSubClass = USB_HID_SUBCLASS_BOOT,
|
||||||
.bInterfaceProtocol = USB_HID_PROTO_NONBOOT,
|
.bInterfaceProtocol = USB_HID_PROTO_KEYBOARD,
|
||||||
.iInterface = NO_DESCRIPTOR,
|
.iInterface = NO_DESCRIPTOR,
|
||||||
},
|
},
|
||||||
.hid_desc =
|
.hid_desc =
|
||||||
@@ -184,15 +190,6 @@ static const struct HidConfigDescriptor hid_cfg_desc = {
|
|||||||
.wMaxPacketSize = HID_EP_SZ,
|
.wMaxPacketSize = HID_EP_SZ,
|
||||||
.bInterval = HID_INTERVAL,
|
.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 {
|
struct HidReportKB {
|
||||||
uint8_t report_id;
|
uint8_t report_id;
|
||||||
uint8_t mods;
|
struct {
|
||||||
uint8_t reserved;
|
uint8_t mods;
|
||||||
uint8_t btn[HID_KB_MAX_KEYS];
|
uint8_t reserved;
|
||||||
|
uint8_t btn[HID_KB_MAX_KEYS];
|
||||||
|
} boot;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct HidReportConsumer {
|
struct HidReportConsumer {
|
||||||
@@ -256,6 +255,7 @@ static bool hid_connected = false;
|
|||||||
static HidStateCallback callback;
|
static HidStateCallback callback;
|
||||||
static void* cb_ctx;
|
static void* cb_ctx;
|
||||||
static uint8_t led_state;
|
static uint8_t led_state;
|
||||||
|
static bool boot_protocol = false;
|
||||||
|
|
||||||
bool furi_hal_hid_is_connected() {
|
bool furi_hal_hid_is_connected() {
|
||||||
return hid_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) {
|
bool furi_hal_hid_kb_press(uint16_t button) {
|
||||||
for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
||||||
if(hid_report.keyboard.btn[key_nb] == 0) {
|
if(hid_report.keyboard.boot.btn[key_nb] == 0) {
|
||||||
hid_report.keyboard.btn[key_nb] = button & 0xFF;
|
hid_report.keyboard.boot.btn[key_nb] = button & 0xFF;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hid_report.keyboard.mods |= (button >> 8);
|
hid_report.keyboard.boot.mods |= (button >> 8);
|
||||||
return hid_send_report(ReportIdKeyboard);
|
return hid_send_report(ReportIdKeyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool furi_hal_hid_kb_release(uint16_t button) {
|
bool furi_hal_hid_kb_release(uint16_t button) {
|
||||||
for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
||||||
if(hid_report.keyboard.btn[key_nb] == (button & 0xFF)) {
|
if(hid_report.keyboard.boot.btn[key_nb] == (button & 0xFF)) {
|
||||||
hid_report.keyboard.btn[key_nb] = 0;
|
hid_report.keyboard.boot.btn[key_nb] = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hid_report.keyboard.mods &= ~(button >> 8);
|
hid_report.keyboard.boot.mods &= ~(button >> 8);
|
||||||
return hid_send_report(ReportIdKeyboard);
|
return hid_send_report(ReportIdKeyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool furi_hal_hid_kb_release_all() {
|
bool furi_hal_hid_kb_release_all() {
|
||||||
for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) {
|
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);
|
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) {
|
static bool hid_send_report(uint8_t report_id) {
|
||||||
if((hid_semaphore == NULL) || (hid_connected == false)) return false;
|
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);
|
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)
|
if(report_id == ReportIdKeyboard)
|
||||||
usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.keyboard, sizeof(hid_report.keyboard));
|
usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.keyboard, sizeof(hid_report.keyboard));
|
||||||
else if(report_id == ReportIdMouse)
|
else if(report_id == ReportIdMouse)
|
||||||
usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.mouse, sizeof(hid_report.mouse));
|
usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.mouse, sizeof(hid_report.mouse));
|
||||||
else if(report_id == ReportIdConsumer)
|
else if(report_id == ReportIdConsumer)
|
||||||
usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.consumer, sizeof(hid_report.consumer));
|
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) {
|
static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||||
UNUSED(dev);
|
UNUSED(dev);
|
||||||
if(event == usbd_evt_eptx) {
|
if(event == usbd_evt_eptx) {
|
||||||
furi_semaphore_release(hid_semaphore);
|
furi_semaphore_release(hid_semaphore);
|
||||||
|
} else if(boot_protocol == true) {
|
||||||
|
usbd_ep_read(usb_dev, ep, &led_state, sizeof(led_state));
|
||||||
} else {
|
} else {
|
||||||
struct HidReportLED leds;
|
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;
|
led_state = leds.led_state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -464,18 +472,15 @@ static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) {
|
|||||||
switch(cfg) {
|
switch(cfg) {
|
||||||
case 0:
|
case 0:
|
||||||
/* deconfiguring device */
|
/* deconfiguring device */
|
||||||
usbd_ep_deconfig(dev, HID_EP_OUT);
|
|
||||||
usbd_ep_deconfig(dev, HID_EP_IN);
|
usbd_ep_deconfig(dev, HID_EP_IN);
|
||||||
usbd_reg_endpoint(dev, HID_EP_OUT, 0);
|
|
||||||
usbd_reg_endpoint(dev, HID_EP_IN, 0);
|
usbd_reg_endpoint(dev, HID_EP_IN, 0);
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
case 1:
|
case 1:
|
||||||
/* configuring device */
|
/* configuring device */
|
||||||
usbd_ep_config(dev, HID_EP_IN, USB_EPTYPE_INTERRUPT, HID_EP_SZ);
|
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_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);
|
usbd_ep_write(dev, HID_EP_IN, 0, 0);
|
||||||
|
boot_protocol = false; /* BIOS will SET_PROTOCOL if it wants this */
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
default:
|
default:
|
||||||
return usbd_fail;
|
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:
|
case USB_HID_SETIDLE:
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
case USB_HID_GETREPORT:
|
case USB_HID_GETREPORT:
|
||||||
dev->status.data_ptr = &hid_report;
|
if(boot_protocol == true) {
|
||||||
dev->status.data_count = sizeof(hid_report);
|
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;
|
return usbd_ack;
|
||||||
default:
|
default:
|
||||||
return usbd_fail;
|
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) {
|
req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) {
|
||||||
switch(req->wValue >> 8) {
|
switch(req->wValue >> 8) {
|
||||||
case USB_DTYPE_HID:
|
case USB_DTYPE_HID:
|
||||||
dev->status.data_ptr = (uint8_t*)&(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.iad_0.hid_desc);
|
dev->status.data_count = sizeof(hid_cfg_desc.intf_0.hid_desc);
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
case USB_DTYPE_HID_REPORT:
|
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_ptr = (uint8_t*)hid_report_desc;
|
||||||
dev->status.data_count = sizeof(hid_report_desc);
|
dev->status.data_count = sizeof(hid_report_desc);
|
||||||
return usbd_ack;
|
return usbd_ack;
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
#include <alt_boot.h>
|
#include <alt_boot.h>
|
||||||
#include <u8g2_glue.h>
|
#include <u8g2_glue.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
|
#include <toolbox/compress.h>
|
||||||
|
|
||||||
void flipper_boot_dfu_show_splash() {
|
void flipper_boot_dfu_show_splash() {
|
||||||
// Initialize
|
// Initialize
|
||||||
furi_hal_compress_icon_init();
|
CompressIcon* compress_icon = compress_icon_alloc();
|
||||||
|
|
||||||
u8g2_t* fb = malloc(sizeof(u8g2_t));
|
u8g2_t* fb = malloc(sizeof(u8g2_t));
|
||||||
memset(fb, 0, sizeof(u8g2_t));
|
memset(fb, 0, sizeof(u8g2_t));
|
||||||
@@ -15,13 +16,15 @@ void flipper_boot_dfu_show_splash() {
|
|||||||
u8g2_InitDisplay(fb);
|
u8g2_InitDisplay(fb);
|
||||||
u8g2_SetDrawColor(fb, 0x01);
|
u8g2_SetDrawColor(fb, 0x01);
|
||||||
uint8_t* splash_data = NULL;
|
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_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data);
|
||||||
u8g2_SetFont(fb, u8g2_font_helvB08_tr);
|
u8g2_SetFont(fb, u8g2_font_helvB08_tr);
|
||||||
u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode");
|
u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode");
|
||||||
u8g2_DrawStr(fb, 2, 21, "DFU Started");
|
u8g2_DrawStr(fb, 2, 21, "DFU Started");
|
||||||
u8g2_SetPowerSave(fb, 0);
|
u8g2_SetPowerSave(fb, 0);
|
||||||
u8g2_SendBuffer(fb);
|
u8g2_SendBuffer(fb);
|
||||||
|
|
||||||
|
compress_icon_free(compress_icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flipper_boot_dfu_exec() {
|
void flipper_boot_dfu_exec() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <alt_boot.h>
|
#include <alt_boot.h>
|
||||||
#include <u8g2_glue.h>
|
#include <u8g2_glue.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
|
#include <toolbox/compress.h>
|
||||||
|
|
||||||
#define COUNTER_VALUE (136U)
|
#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_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||||
u8g2_InitDisplay(fb);
|
u8g2_InitDisplay(fb);
|
||||||
|
|
||||||
furi_hal_compress_icon_init();
|
CompressIcon* compress_icon = compress_icon_alloc();
|
||||||
uint8_t* splash_data = NULL;
|
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_ClearBuffer(fb);
|
||||||
u8g2_SetDrawColor(fb, 0x01);
|
u8g2_SetDrawColor(fb, 0x01);
|
||||||
@@ -38,6 +39,7 @@ void flipper_boot_recovery_exec() {
|
|||||||
u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data);
|
u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data);
|
||||||
u8g2_SendBuffer(fb);
|
u8g2_SendBuffer(fb);
|
||||||
u8g2_SetPowerSave(fb, 0);
|
u8g2_SetPowerSave(fb, 0);
|
||||||
|
compress_icon_free(compress_icon);
|
||||||
|
|
||||||
size_t counter = COUNTER_VALUE;
|
size_t counter = COUNTER_VALUE;
|
||||||
while(counter) {
|
while(counter) {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ struct STOP_EXTERNING_ME {};
|
|||||||
#include <furi_hal_vibro.h>
|
#include <furi_hal_vibro.h>
|
||||||
#include <furi_hal_usb.h>
|
#include <furi_hal_usb.h>
|
||||||
#include <furi_hal_usb_hid.h>
|
#include <furi_hal_usb_hid.h>
|
||||||
#include <furi_hal_compress.h>
|
|
||||||
#include <furi_hal_uart.h>
|
#include <furi_hal_uart.h>
|
||||||
#include <furi_hal_info.h>
|
#include <furi_hal_info.h>
|
||||||
#include <furi_hal_random.h>
|
#include <furi_hal_random.h>
|
||||||
|
|||||||
@@ -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
4
lib/err.h
Normal 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
1
lib/heatshrink
Submodule
Submodule lib/heatshrink added at 7398ccc916
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <flipper_format.h>
|
|
||||||
|
|
||||||
#include <one_wire/one_wire_host.h>
|
#include <one_wire/one_wire_host.h>
|
||||||
#include <one_wire/one_wire_slave.h>
|
#include <one_wire/one_wire_slave.h>
|
||||||
|
|
||||||
|
#include <flipper_format/flipper_format.h>
|
||||||
|
|
||||||
#define DALLAS_COMMON_MANUFACTURER_NAME "Dallas"
|
#define DALLAS_COMMON_MANUFACTURER_NAME "Dallas"
|
||||||
|
|
||||||
#define DALLAS_COMMON_CMD_READ_ROM 0x33U
|
#define DALLAS_COMMON_CMD_READ_ROM 0x33U
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
#include "../protocol_common_i.h"
|
#include "../protocol_common_i.h"
|
||||||
|
|
||||||
#include <flipper_format.h>
|
|
||||||
|
|
||||||
#include <one_wire/one_wire_host.h>
|
#include <one_wire/one_wire_host.h>
|
||||||
#include <one_wire/one_wire_slave.h>
|
#include <one_wire/one_wire_slave.h>
|
||||||
|
|
||||||
|
#include <flipper_format/flipper_format.h>
|
||||||
|
|
||||||
typedef bool (*iButtonProtocolDallasReadWriteFunc)(OneWireHost*, iButtonProtocolData*);
|
typedef bool (*iButtonProtocolDallasReadWriteFunc)(OneWireHost*, iButtonProtocolData*);
|
||||||
typedef void (*iButtonProtocolDallasEmulateFunc)(OneWireSlave*, iButtonProtocolData*);
|
typedef void (*iButtonProtocolDallasEmulateFunc)(OneWireSlave*, iButtonProtocolData*);
|
||||||
typedef bool (*iButtonProtocolDallasSaveFunc)(FlipperFormat*, const iButtonProtocolData*);
|
typedef bool (*iButtonProtocolDallasSaveFunc)(FlipperFormat*, const iButtonProtocolData*);
|
||||||
|
|||||||
@@ -67,6 +67,14 @@ bool dallas_ds1420_write_blank(OneWireHost* host, iButtonProtocolData* protocol_
|
|||||||
tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
|
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) {
|
static bool dallas_ds1420_command_callback(uint8_t command, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
DS1420ProtocolData* data = context;
|
DS1420ProtocolData* data = context;
|
||||||
@@ -92,7 +100,7 @@ void dallas_ds1420_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data
|
|||||||
DS1420ProtocolData* data = protocol_data;
|
DS1420ProtocolData* data = protocol_data;
|
||||||
data->state.bus = bus;
|
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);
|
onewire_slave_set_command_callback(bus, dallas_ds1420_command_callback, protocol_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const iButtonProtocolDallasBase ibutton_protocol_ds1971 = {
|
|||||||
.name = DS1971_FAMILY_NAME,
|
.name = DS1971_FAMILY_NAME,
|
||||||
|
|
||||||
.read = dallas_ds1971_read,
|
.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,
|
.write_copy = dallas_ds1971_write_copy,
|
||||||
.emulate = dallas_ds1971_emulate,
|
.emulate = dallas_ds1971_emulate,
|
||||||
.save = dallas_ds1971_save,
|
.save = dallas_ds1971_save,
|
||||||
@@ -76,7 +76,7 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
|||||||
DS1971ProtocolData* data = protocol_data;
|
DS1971ProtocolData* data = protocol_data;
|
||||||
|
|
||||||
onewire_host_reset(host);
|
onewire_host_reset(host);
|
||||||
onewire_host_skip(host);
|
onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM);
|
||||||
// Starting writing from address 0x0000
|
// Starting writing from address 0x0000
|
||||||
onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH);
|
onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH);
|
||||||
onewire_host_write(host, 0x00);
|
onewire_host_write(host, 0x00);
|
||||||
@@ -87,7 +87,7 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
|||||||
bool pad_valid = false;
|
bool pad_valid = false;
|
||||||
if(onewire_host_reset(host)) {
|
if(onewire_host_reset(host)) {
|
||||||
pad_valid = true;
|
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, DALLAS_COMMON_CMD_READ_SCRATCH);
|
||||||
onewire_host_write(host, 0x00);
|
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
|
// Copy scratchpad to memory and confirm
|
||||||
if(pad_valid) {
|
if(pad_valid) {
|
||||||
if(onewire_host_reset(host)) {
|
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, DALLAS_COMMON_CMD_COPY_SCRATCH);
|
||||||
onewire_host_write(host, DS1971_CMD_FINALIZATION);
|
onewire_host_write(host, DS1971_CMD_FINALIZATION);
|
||||||
|
|
||||||
@@ -114,10 +114,16 @@ bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
|||||||
return pad_valid;
|
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);
|
furi_assert(context);
|
||||||
DS1971ProtocolData* data = 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) {
|
static bool dallas_ds1971_command_callback(uint8_t command, void* context) {
|
||||||
|
|||||||
@@ -67,6 +67,14 @@ bool dallas_ds1990_write_blank(OneWireHost* host, iButtonProtocolData* protocol_
|
|||||||
tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
|
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) {
|
static bool dallas_ds1990_command_callback(uint8_t command, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
DS1990ProtocolData* data = context;
|
DS1990ProtocolData* data = context;
|
||||||
@@ -92,7 +100,7 @@ void dallas_ds1990_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data
|
|||||||
DS1990ProtocolData* data = protocol_data;
|
DS1990ProtocolData* data = protocol_data;
|
||||||
data->state.bus = bus;
|
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);
|
onewire_slave_set_command_callback(bus, dallas_ds1990_command_callback, protocol_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,10 +87,16 @@ bool dallas_ds1992_write_copy(OneWireHost* host, iButtonProtocolData* protocol_d
|
|||||||
DS1992_SRAM_DATA_SIZE);
|
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);
|
furi_assert(context);
|
||||||
DS1992ProtocolData* data = 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) {
|
static bool dallas_ds1992_command_callback(uint8_t command, void* context) {
|
||||||
|
|||||||
@@ -63,24 +63,54 @@ const iButtonProtocolDallasBase ibutton_protocol_ds1996 = {
|
|||||||
|
|
||||||
bool dallas_ds1996_read(OneWireHost* host, iButtonProtocolData* protocol_data) {
|
bool dallas_ds1996_read(OneWireHost* host, iButtonProtocolData* protocol_data) {
|
||||||
DS1996ProtocolData* data = protocol_data;
|
DS1996ProtocolData* data = protocol_data;
|
||||||
return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) &&
|
bool success = false;
|
||||||
dallas_common_read_mem(host, 0, data->sram_data, DS1996_SRAM_DATA_SIZE);
|
|
||||||
|
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) {
|
bool dallas_ds1996_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) {
|
||||||
DS1996ProtocolData* data = protocol_data;
|
DS1996ProtocolData* data = protocol_data;
|
||||||
return dallas_common_write_mem(
|
bool success = false;
|
||||||
host,
|
|
||||||
DS1996_COPY_SCRATCH_TIMEOUT_US,
|
do {
|
||||||
DS1996_SRAM_PAGE_SIZE,
|
if(!onewire_host_reset(host)) break;
|
||||||
data->sram_data,
|
|
||||||
DS1996_SRAM_DATA_SIZE);
|
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);
|
furi_assert(context);
|
||||||
DS1996ProtocolData* data = context;
|
DS1996ProtocolData* data = context;
|
||||||
data->state.command_state = DallasCommonCommandStateIdle;
|
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) {
|
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) {
|
} else if(data->state.command_state == DallasCommonCommandStateRomCmd) {
|
||||||
data->state.command_state = DallasCommonCommandStateMemCmd;
|
data->state.command_state = DallasCommonCommandStateMemCmd;
|
||||||
dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE);
|
return dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE);
|
||||||
return false;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -120,8 +149,17 @@ static bool dallas_ds1996_command_callback(uint8_t command, void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM:
|
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:
|
case DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM:
|
||||||
/* TODO: Overdrive mode support */
|
/* TODO: Match ROM command support */
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,14 @@ bool ds_generic_write_blank(OneWireHost* host, iButtonProtocolData* protocol_dat
|
|||||||
return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
|
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) {
|
static bool ds_generic_command_callback(uint8_t command, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
DallasGenericProtocolData* data = context;
|
DallasGenericProtocolData* data = context;
|
||||||
@@ -85,7 +93,7 @@ void ds_generic_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) {
|
|||||||
DallasGenericProtocolData* data = protocol_data;
|
DallasGenericProtocolData* data = protocol_data;
|
||||||
data->state.bus = bus;
|
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);
|
onewire_slave_set_command_callback(bus, ds_generic_command_callback, protocol_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ for lib in libs_recurse:
|
|||||||
sources += libenv.GlobRecursive("*.c*", lib)
|
sources += libenv.GlobRecursive("*.c*", lib)
|
||||||
|
|
||||||
libs_plain = [
|
libs_plain = [
|
||||||
"heatshrink",
|
|
||||||
"nanopb",
|
"nanopb",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -47,6 +46,12 @@ for lib in libs_plain:
|
|||||||
source=True,
|
source=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sources += Glob(
|
||||||
|
"heatshrink/heatshrink_*.c*",
|
||||||
|
exclude=GLOB_FILE_EXCLUSION,
|
||||||
|
source=True,
|
||||||
|
)
|
||||||
|
|
||||||
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
||||||
libenv.Install("${LIB_DIST_DIR}", lib)
|
libenv.Install("${LIB_DIST_DIR}", lib)
|
||||||
Return("lib")
|
Return("lib")
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ env.Append(
|
|||||||
"#/lib/one_wire",
|
"#/lib/one_wire",
|
||||||
],
|
],
|
||||||
SDK_HEADERS=[
|
SDK_HEADERS=[
|
||||||
File("one_wire_host_timing.h"),
|
|
||||||
File("one_wire_host.h"),
|
File("one_wire_host.h"),
|
||||||
File("one_wire_slave.h"),
|
File("one_wire_slave.h"),
|
||||||
File("maxim_crc.h"),
|
File("maxim_crc.h"),
|
||||||
|
|||||||
@@ -1,10 +1,54 @@
|
|||||||
#include <furi.h>
|
#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.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 {
|
struct OneWireHost {
|
||||||
const GpioPin* gpio_pin;
|
const GpioPin* gpio_pin;
|
||||||
|
const OneWireHostTimings* timings;
|
||||||
unsigned char saved_rom[8]; /** < global search state */
|
unsigned char saved_rom[8]; /** < global search state */
|
||||||
uint8_t last_discrepancy;
|
uint8_t last_discrepancy;
|
||||||
uint8_t last_family_discrepancy;
|
uint8_t last_family_discrepancy;
|
||||||
@@ -15,6 +59,7 @@ OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin) {
|
|||||||
OneWireHost* host = malloc(sizeof(OneWireHost));
|
OneWireHost* host = malloc(sizeof(OneWireHost));
|
||||||
host->gpio_pin = gpio_pin;
|
host->gpio_pin = gpio_pin;
|
||||||
onewire_host_reset_search(host);
|
onewire_host_reset_search(host);
|
||||||
|
onewire_host_set_overdrive(host, false);
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +72,8 @@ bool onewire_host_reset(OneWireHost* host) {
|
|||||||
uint8_t r;
|
uint8_t r;
|
||||||
uint8_t retries = 125;
|
uint8_t retries = 125;
|
||||||
|
|
||||||
|
const OneWireHostTimings* timings = host->timings;
|
||||||
|
|
||||||
// wait until the gpio is high
|
// wait until the gpio is high
|
||||||
furi_hal_gpio_write(host->gpio_pin, true);
|
furi_hal_gpio_write(host->gpio_pin, true);
|
||||||
do {
|
do {
|
||||||
@@ -35,19 +82,19 @@ bool onewire_host_reset(OneWireHost* host) {
|
|||||||
} while(!furi_hal_gpio_read(host->gpio_pin));
|
} while(!furi_hal_gpio_read(host->gpio_pin));
|
||||||
|
|
||||||
// pre delay
|
// pre delay
|
||||||
furi_delay_us(OWH_RESET_DELAY_PRE);
|
furi_delay_us(timings->g);
|
||||||
|
|
||||||
// drive low
|
// drive low
|
||||||
furi_hal_gpio_write(host->gpio_pin, false);
|
furi_hal_gpio_write(host->gpio_pin, false);
|
||||||
furi_delay_us(OWH_RESET_DRIVE);
|
furi_delay_us(timings->h);
|
||||||
|
|
||||||
// release
|
// release
|
||||||
furi_hal_gpio_write(host->gpio_pin, true);
|
furi_hal_gpio_write(host->gpio_pin, true);
|
||||||
furi_delay_us(OWH_RESET_RELEASE);
|
furi_delay_us(timings->i);
|
||||||
|
|
||||||
// read and post delay
|
// read and post delay
|
||||||
r = !furi_hal_gpio_read(host->gpio_pin);
|
r = !furi_hal_gpio_read(host->gpio_pin);
|
||||||
furi_delay_us(OWH_RESET_DELAY_POST);
|
furi_delay_us(timings->j);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -55,17 +102,19 @@ bool onewire_host_reset(OneWireHost* host) {
|
|||||||
bool onewire_host_read_bit(OneWireHost* host) {
|
bool onewire_host_read_bit(OneWireHost* host) {
|
||||||
bool result;
|
bool result;
|
||||||
|
|
||||||
|
const OneWireHostTimings* timings = host->timings;
|
||||||
|
|
||||||
// drive low
|
// drive low
|
||||||
furi_hal_gpio_write(host->gpio_pin, false);
|
furi_hal_gpio_write(host->gpio_pin, false);
|
||||||
furi_delay_us(OWH_READ_DRIVE);
|
furi_delay_us(timings->a);
|
||||||
|
|
||||||
// release
|
// release
|
||||||
furi_hal_gpio_write(host->gpio_pin, true);
|
furi_hal_gpio_write(host->gpio_pin, true);
|
||||||
furi_delay_us(OWH_READ_RELEASE);
|
furi_delay_us(timings->e);
|
||||||
|
|
||||||
// read and post delay
|
// read and post delay
|
||||||
result = furi_hal_gpio_read(host->gpio_pin);
|
result = furi_hal_gpio_read(host->gpio_pin);
|
||||||
furi_delay_us(OWH_READ_DELAY_POST);
|
furi_delay_us(timings->f);
|
||||||
|
|
||||||
return result;
|
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) {
|
void onewire_host_write_bit(OneWireHost* host, bool value) {
|
||||||
|
const OneWireHostTimings* timings = host->timings;
|
||||||
|
|
||||||
if(value) {
|
if(value) {
|
||||||
// drive low
|
// drive low
|
||||||
furi_hal_gpio_write(host->gpio_pin, false);
|
furi_hal_gpio_write(host->gpio_pin, false);
|
||||||
furi_delay_us(OWH_WRITE_1_DRIVE);
|
furi_delay_us(timings->a);
|
||||||
|
|
||||||
// release
|
// release
|
||||||
furi_hal_gpio_write(host->gpio_pin, true);
|
furi_hal_gpio_write(host->gpio_pin, true);
|
||||||
furi_delay_us(OWH_WRITE_1_RELEASE);
|
furi_delay_us(timings->b);
|
||||||
} else {
|
} else {
|
||||||
// drive low
|
// drive low
|
||||||
furi_hal_gpio_write(host->gpio_pin, false);
|
furi_hal_gpio_write(host->gpio_pin, false);
|
||||||
furi_delay_us(OWH_WRITE_0_DRIVE);
|
furi_delay_us(timings->c);
|
||||||
|
|
||||||
// release
|
// release
|
||||||
furi_hal_gpio_write(host->gpio_pin, true);
|
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) {
|
void onewire_host_start(OneWireHost* host) {
|
||||||
furi_hal_gpio_write(host->gpio_pin, true);
|
furi_hal_gpio_write(host->gpio_pin, true);
|
||||||
furi_hal_gpio_init(host->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
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;
|
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 id_bit_number;
|
||||||
uint8_t last_zero, rom_byte_number, search_result;
|
uint8_t last_zero, rom_byte_number, search_result;
|
||||||
uint8_t id_bit, cmp_id_bit;
|
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;
|
return search_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onewire_host_set_overdrive(OneWireHost* host, bool set) {
|
||||||
|
host->timings = set ? &onewire_host_timings_overdrive : &onewire_host_timings_normal;
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,114 +15,115 @@ extern "C" {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OneWireHostSearchModeConditional = 0, /**< Search for alarmed device */
|
OneWireHostSearchModeConditional = 0, /**< Search for alarmed device */
|
||||||
OneWireHostSearchModeNormal = 1, /**< Search all devices */
|
OneWireHostSearchModeNormal = 1, /**< Search for all devices */
|
||||||
} OneWireHostSearchMode;
|
} OneWireHostSearchMode;
|
||||||
|
|
||||||
typedef struct OneWireHost OneWireHost;
|
typedef struct OneWireHost OneWireHost;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate onewire host bus
|
* Allocate OneWireHost instance
|
||||||
* @param pin
|
* @param [in] gpio_pin connection pin
|
||||||
* @return OneWireHost*
|
* @return pointer to OneWireHost instance
|
||||||
*/
|
*/
|
||||||
OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin);
|
OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deallocate onewire host bus
|
* Destroy OneWireHost instance, free resources
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
*/
|
*/
|
||||||
void onewire_host_free(OneWireHost* host);
|
void onewire_host_free(OneWireHost* host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset bus
|
* Reset the 1-Wire bus
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @return bool
|
* @return true if presence was detected, false otherwise
|
||||||
*/
|
*/
|
||||||
bool onewire_host_reset(OneWireHost* host);
|
bool onewire_host_reset(OneWireHost* host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read one bit
|
* Read one bit
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @return bool
|
* @return received bit value
|
||||||
*/
|
*/
|
||||||
bool onewire_host_read_bit(OneWireHost* host);
|
bool onewire_host_read_bit(OneWireHost* host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read one byte
|
* Read one byte
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @return uint8_t
|
* @return received byte value
|
||||||
*/
|
*/
|
||||||
uint8_t onewire_host_read(OneWireHost* host);
|
uint8_t onewire_host_read(OneWireHost* host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read many bytes
|
* Read one or more bytes
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @param buffer
|
* @param [out] buffer received data buffer
|
||||||
* @param count
|
* @param [in] count number of bytes to read
|
||||||
*/
|
*/
|
||||||
void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count);
|
void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write one bit
|
* Write one bit
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @param value
|
* @param value bit value to write
|
||||||
*/
|
*/
|
||||||
void onewire_host_write_bit(OneWireHost* host, bool value);
|
void onewire_host_write_bit(OneWireHost* host, bool value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write one byte
|
* Write one byte
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @param value
|
* @param value byte value to write
|
||||||
*/
|
*/
|
||||||
void onewire_host_write(OneWireHost* host, uint8_t value);
|
void onewire_host_write(OneWireHost* host, uint8_t value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write many bytes
|
* Write one or more bytes
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @param buffer
|
* @param [in] buffer pointer to the data to write
|
||||||
* @param count
|
* @param [in] count size of the data to write
|
||||||
*/
|
*/
|
||||||
void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count);
|
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
|
* Start working with the bus
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
*/
|
*/
|
||||||
void onewire_host_start(OneWireHost* host);
|
void onewire_host_start(OneWireHost* host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop working with the bus
|
* Stop working with the bus
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
*/
|
*/
|
||||||
void onewire_host_stop(OneWireHost* host);
|
void onewire_host_stop(OneWireHost* host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Reset previous search results
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
*/
|
*/
|
||||||
void onewire_host_reset_search(OneWireHost* host);
|
void onewire_host_reset_search(OneWireHost* host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Set the family code to search for
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @param family_code
|
* @param [in] family_code device family code
|
||||||
*/
|
*/
|
||||||
void onewire_host_target_search(OneWireHost* host, uint8_t family_code);
|
void onewire_host_target_search(OneWireHost* host, uint8_t family_code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Search for devices on the 1-Wire bus
|
||||||
* @param host
|
* @param [in] host pointer to OneWireHost instance
|
||||||
* @param newAddr
|
* @param [out] new_addr pointer to the buffer to contain the unique ROM of the found device
|
||||||
* @param mode
|
* @param [in] mode search mode
|
||||||
* @return uint8_t
|
* @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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -3,20 +3,7 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|
||||||
#define ONEWIRE_TRSTL_MIN 270 /* Minimum Reset Low time */
|
#define TH_TIMEOUT_MAX 15000 /* Maximum time before general timeout */
|
||||||
#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 */
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OneWireSlaveErrorNone = 0,
|
OneWireSlaveErrorNone = 0,
|
||||||
@@ -26,10 +13,29 @@ typedef enum {
|
|||||||
OneWireSlaveErrorTimeout,
|
OneWireSlaveErrorTimeout,
|
||||||
} OneWireSlaveError;
|
} 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 {
|
struct OneWireSlave {
|
||||||
const GpioPin* gpio_pin;
|
const GpioPin* gpio_pin;
|
||||||
|
const OneWireSlaveTimings* timings;
|
||||||
OneWireSlaveError error;
|
OneWireSlaveError error;
|
||||||
|
|
||||||
|
bool is_first_reset;
|
||||||
|
bool is_short_reset;
|
||||||
|
|
||||||
OneWireSlaveResetCallback reset_callback;
|
OneWireSlaveResetCallback reset_callback;
|
||||||
OneWireSlaveCommandCallback command_callback;
|
OneWireSlaveCommandCallback command_callback;
|
||||||
OneWireSlaveResultCallback result_callback;
|
OneWireSlaveResultCallback result_callback;
|
||||||
@@ -39,42 +45,72 @@ struct OneWireSlave {
|
|||||||
void* command_callback_context;
|
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 ***********************/
|
/*********************** PRIVATE ***********************/
|
||||||
|
|
||||||
static uint32_t
|
static bool
|
||||||
onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) {
|
onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time_us, const bool pin_value) {
|
||||||
uint32_t start = DWT->CYCCNT;
|
const uint32_t time_start = DWT->CYCCNT;
|
||||||
uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond();
|
const uint32_t time_ticks = time_us * furi_hal_cortex_instructions_per_microsecond();
|
||||||
uint32_t time_captured;
|
|
||||||
|
uint32_t time_elapsed;
|
||||||
|
|
||||||
do { //-V1044
|
do { //-V1044
|
||||||
time_captured = DWT->CYCCNT;
|
time_elapsed = DWT->CYCCNT - time_start;
|
||||||
if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) {
|
if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) {
|
||||||
uint32_t remaining_time = time_ticks - (time_captured - start);
|
return time_ticks >= time_elapsed;
|
||||||
remaining_time /= furi_hal_cortex_instructions_per_microsecond();
|
|
||||||
return remaining_time;
|
|
||||||
}
|
}
|
||||||
} 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)
|
// 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
|
// wait while master delay presence check
|
||||||
furi_delay_us(ONEWIRE_TPDH_TYP);
|
furi_delay_us(timings->tpdh_typ);
|
||||||
|
|
||||||
// show presence
|
// show presence
|
||||||
furi_hal_gpio_write(bus->gpio_pin, false);
|
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);
|
furi_hal_gpio_write(bus->gpio_pin, true);
|
||||||
|
|
||||||
// somebody also can show presence
|
// 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
|
// 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;
|
bus->error = OneWireSlaveErrorPresenceConflict;
|
||||||
return false;
|
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) {
|
static inline bool onewire_slave_receive_and_process_command(OneWireSlave* bus) {
|
||||||
/* Reset condition detected, send a presence pulse and reset protocol state */
|
/* Reset condition detected, send a presence pulse and reset protocol state */
|
||||||
if(bus->error == OneWireSlaveErrorResetInProgress) {
|
if(bus->error == OneWireSlaveErrorResetInProgress) {
|
||||||
if(onewire_slave_show_presence(bus)) {
|
if(!bus->is_first_reset) {
|
||||||
bus->error = OneWireSlaveErrorNone;
|
/* 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) {
|
furi_assert(bus->reset_callback);
|
||||||
bus->reset_callback(bus->reset_callback_context);
|
|
||||||
|
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) {
|
} else if(bus->error == OneWireSlaveErrorNone) {
|
||||||
uint8_t command;
|
uint8_t command;
|
||||||
if(!onewire_slave_receive(bus, &command, 1)) {
|
if(onewire_slave_receive(bus, &command, sizeof(command))) {
|
||||||
/* Upon failure, request an additional iteration to
|
furi_assert(bus->command_callback);
|
||||||
choose the appropriate action by checking bus->error */
|
if(bus->command_callback(command, bus->command_callback_context)) {
|
||||||
return true;
|
return true;
|
||||||
} else if(bus->command_callback) {
|
}
|
||||||
return bus->command_callback(command, bus->command_callback_context);
|
|
||||||
} else {
|
|
||||||
bus->error = OneWireSlaveErrorInvalidCommand;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (bus->error == OneWireSlaveErrorResetInProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -115,9 +160,6 @@ static inline bool onewire_slave_bus_start(OneWireSlave* bus) {
|
|||||||
FURI_CRITICAL_ENTER();
|
FURI_CRITICAL_ENTER();
|
||||||
furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
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))
|
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 =
|
const uint32_t pulse_length =
|
||||||
(DWT->CYCCNT - pulse_start) / furi_hal_cortex_instructions_per_microsecond();
|
(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);
|
const bool result = onewire_slave_bus_start(bus);
|
||||||
|
|
||||||
if(result && bus->result_callback != NULL) {
|
if(result && bus->result_callback != NULL) {
|
||||||
@@ -158,6 +208,7 @@ OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) {
|
|||||||
OneWireSlave* bus = malloc(sizeof(OneWireSlave));
|
OneWireSlave* bus = malloc(sizeof(OneWireSlave));
|
||||||
|
|
||||||
bus->gpio_pin = gpio_pin;
|
bus->gpio_pin = gpio_pin;
|
||||||
|
bus->timings = &onewire_slave_timings_normal;
|
||||||
bus->error = OneWireSlaveErrorNone;
|
bus->error = OneWireSlaveErrorNone;
|
||||||
|
|
||||||
return bus;
|
return bus;
|
||||||
@@ -205,52 +256,45 @@ void onewire_slave_set_result_callback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool onewire_slave_receive_bit(OneWireSlave* bus) {
|
bool onewire_slave_receive_bit(OneWireSlave* bus) {
|
||||||
|
const OneWireSlaveTimings* timings = bus->timings;
|
||||||
// wait while bus is low
|
// wait while bus is low
|
||||||
uint32_t time = ONEWIRE_TSLOT_MAX;
|
if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) {
|
||||||
time = onewire_slave_wait_while_gpio_is(bus, time, false);
|
|
||||||
if(time == 0) {
|
|
||||||
bus->error = OneWireSlaveErrorResetInProgress;
|
bus->error = OneWireSlaveErrorResetInProgress;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait while bus is high
|
// wait while bus is high
|
||||||
time = ONEWIRE_TH_TIMEOUT;
|
if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) {
|
||||||
time = onewire_slave_wait_while_gpio_is(bus, time, true);
|
|
||||||
if(time == 0) {
|
|
||||||
bus->error = OneWireSlaveErrorTimeout;
|
bus->error = OneWireSlaveErrorTimeout;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait a time of zero
|
// wait a time of zero
|
||||||
time = ONEWIRE_TW1L_MAX;
|
return onewire_slave_wait_while_gpio_is(bus, timings->tw1l_max, false);
|
||||||
time = onewire_slave_wait_while_gpio_is(bus, time, false);
|
|
||||||
|
|
||||||
return (time > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool onewire_slave_send_bit(OneWireSlave* bus, bool value) {
|
bool onewire_slave_send_bit(OneWireSlave* bus, bool value) {
|
||||||
|
const OneWireSlaveTimings* timings = bus->timings;
|
||||||
// wait while bus is low
|
// wait while bus is low
|
||||||
uint32_t time = ONEWIRE_TSLOT_MAX;
|
if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) {
|
||||||
time = onewire_slave_wait_while_gpio_is(bus, time, false);
|
|
||||||
if(time == 0) {
|
|
||||||
bus->error = OneWireSlaveErrorResetInProgress;
|
bus->error = OneWireSlaveErrorResetInProgress;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait while bus is high
|
// wait while bus is high
|
||||||
time = ONEWIRE_TH_TIMEOUT;
|
if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) {
|
||||||
time = onewire_slave_wait_while_gpio_is(bus, time, true);
|
|
||||||
if(time == 0) {
|
|
||||||
bus->error = OneWireSlaveErrorTimeout;
|
bus->error = OneWireSlaveErrorTimeout;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// choose write time
|
// choose write time
|
||||||
|
uint32_t time;
|
||||||
|
|
||||||
if(!value) {
|
if(!value) {
|
||||||
furi_hal_gpio_write(bus->gpio_pin, false);
|
furi_hal_gpio_write(bus->gpio_pin, false);
|
||||||
time = ONEWIRE_TRL_TMSR_MAX;
|
time = timings->trl_tmsr_max;
|
||||||
} else {
|
} else {
|
||||||
time = ONEWIRE_TSLOT_MIN;
|
time = timings->tslot_min;
|
||||||
}
|
}
|
||||||
|
|
||||||
// hold line for ZERO or ONE time
|
// 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;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,68 +18,85 @@ extern "C" {
|
|||||||
typedef struct OneWireDevice OneWireDevice;
|
typedef struct OneWireDevice OneWireDevice;
|
||||||
typedef struct OneWireSlave OneWireSlave;
|
typedef struct OneWireSlave OneWireSlave;
|
||||||
|
|
||||||
typedef void (*OneWireSlaveResetCallback)(void* context);
|
typedef bool (*OneWireSlaveResetCallback)(bool is_short, void* context);
|
||||||
typedef void (*OneWireSlaveResultCallback)(void* context);
|
|
||||||
typedef bool (*OneWireSlaveCommandCallback)(uint8_t command, void* context);
|
typedef bool (*OneWireSlaveCommandCallback)(uint8_t command, void* context);
|
||||||
|
typedef void (*OneWireSlaveResultCallback)(void* context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate onewire slave
|
* Allocate OneWireSlave instance
|
||||||
* @param gpio_pin
|
* @param [in] gpio_pin connection pin
|
||||||
* @return OneWireSlave*
|
* @return pointer to OneWireSlave instance
|
||||||
*/
|
*/
|
||||||
OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin);
|
OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free onewire slave
|
* Destroy OneWireSlave instance, free resources
|
||||||
* @param bus
|
* @param [in] bus pointer to OneWireSlave instance
|
||||||
*/
|
*/
|
||||||
void onewire_slave_free(OneWireSlave* bus);
|
void onewire_slave_free(OneWireSlave* bus);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start working with the bus
|
* Start working with the bus
|
||||||
* @param bus
|
* @param [in] bus pointer to OneWireSlave instance
|
||||||
*/
|
*/
|
||||||
void onewire_slave_start(OneWireSlave* bus);
|
void onewire_slave_start(OneWireSlave* bus);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop working with the bus
|
* Stop working with the bus
|
||||||
* @param bus
|
* @param [in] bus pointer to OneWireSlave instance
|
||||||
*/
|
*/
|
||||||
void onewire_slave_stop(OneWireSlave* bus);
|
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);
|
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);
|
bool onewire_slave_send_bit(OneWireSlave* bus, bool value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send data
|
* Send one or more bytes of data
|
||||||
* @param bus
|
* @param [in] bus pointer to OneWireSlave instance
|
||||||
* @param data
|
* @param [in] data pointer to the data to send
|
||||||
* @param data_size
|
* @param [in] data_size size of the data to send
|
||||||
* @return bool
|
* @return true on success, false on failure
|
||||||
*/
|
*/
|
||||||
bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size);
|
bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive data
|
* Receive one or more bytes of data
|
||||||
* @param bus
|
* @param [in] bus pointer to OneWireSlave instance
|
||||||
* @param data
|
* @param [out] data pointer to the receive buffer
|
||||||
* @param data_size
|
* @param [in] data_size number of bytes to receive
|
||||||
* @return bool
|
* @return true on success, false on failure
|
||||||
*/
|
*/
|
||||||
bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size);
|
bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a callback to be called on each reset
|
* Enable overdrive mode
|
||||||
* @param bus
|
* @param [in] bus pointer to OneWireSlave instance
|
||||||
* @param callback
|
* @param [in] set true to turn overdrive on, false to turn it off
|
||||||
* @param context
|
*/
|
||||||
|
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(
|
void onewire_slave_set_reset_callback(
|
||||||
OneWireSlave* bus,
|
OneWireSlave* bus,
|
||||||
@@ -87,10 +104,13 @@ void onewire_slave_set_reset_callback(
|
|||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a callback to be called on each command
|
* Set a callback function to be called on each command.
|
||||||
* @param bus
|
* The return value of the callback determines whether further operation
|
||||||
* @param callback
|
* is possible. As a rule of thumb, return true unless a critical error happened.
|
||||||
* @param context
|
*
|
||||||
|
* @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(
|
void onewire_slave_set_command_callback(
|
||||||
OneWireSlave* bus,
|
OneWireSlave* bus,
|
||||||
@@ -99,9 +119,9 @@ void onewire_slave_set_command_callback(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a callback to report emulation success
|
* Set a callback to report emulation success
|
||||||
* @param bus
|
* @param [in] bus pointer to OneWireSlave instance
|
||||||
* @param result_cb
|
* @param [in] result_cb pointer to a callback function
|
||||||
* @param context
|
* @param [in] context additional parameter to be passed to the callback
|
||||||
*/
|
*/
|
||||||
void onewire_slave_set_result_callback(
|
void onewire_slave_set_result_callback(
|
||||||
OneWireSlave* bus,
|
OneWireSlave* bus,
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ static const uint32_t subghz_frequency_list[] = {
|
|||||||
static const uint32_t subghz_hopper_frequency_list[] = {
|
static const uint32_t subghz_hopper_frequency_list[] = {
|
||||||
310000000,
|
310000000,
|
||||||
315000000,
|
315000000,
|
||||||
433420000,
|
318000000,
|
||||||
|
418000000,
|
||||||
433920000,
|
433920000,
|
||||||
868350000,
|
868350000,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ env.Append(
|
|||||||
File("manchester_encoder.h"),
|
File("manchester_encoder.h"),
|
||||||
File("path.h"),
|
File("path.h"),
|
||||||
File("random_name.h"),
|
File("random_name.h"),
|
||||||
File("hmac_sha256.h"),
|
File("sha256.h"),
|
||||||
File("crc32_calc.h"),
|
File("crc32_calc.h"),
|
||||||
File("dir_walk.h"),
|
File("dir_walk.h"),
|
||||||
File("md5.h"),
|
File("md5.h"),
|
||||||
|
|||||||
@@ -1,115 +1,112 @@
|
|||||||
#include <furi_hal_compress.h>
|
#include "compress.h"
|
||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <lib/heatshrink/heatshrink_encoder.h>
|
#include <lib/heatshrink/heatshrink_encoder.h>
|
||||||
#include <lib/heatshrink/heatshrink_decoder.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)
|
/** Defines encoder and decoder lookahead buffer size */
|
||||||
#define FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE (1024)
|
#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 {
|
typedef struct {
|
||||||
uint8_t is_compressed;
|
uint8_t is_compressed;
|
||||||
uint8_t reserved;
|
uint8_t reserved;
|
||||||
uint16_t compressed_buff_size;
|
uint16_t compressed_buff_size;
|
||||||
} FuriHalCompressHeader;
|
} CompressHeader;
|
||||||
|
|
||||||
typedef struct {
|
_Static_assert(sizeof(CompressHeader) == 4, "Incorrect CompressHeader size");
|
||||||
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;
|
|
||||||
|
|
||||||
struct FuriHalCompress {
|
struct CompressIcon {
|
||||||
heatshrink_encoder* encoder;
|
|
||||||
heatshrink_decoder* decoder;
|
heatshrink_decoder* decoder;
|
||||||
uint8_t* compress_buff;
|
uint8_t decoded_buff[COMPRESS_ICON_DECODED_BUFF_SIZE];
|
||||||
uint16_t compress_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) {
|
return instance;
|
||||||
furi_assert(compress);
|
|
||||||
heatshrink_encoder_reset(compress->encoder);
|
|
||||||
heatshrink_decoder_reset(compress->decoder);
|
|
||||||
memset(compress->compress_buff, 0, compress->compress_buff_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void furi_hal_compress_icon_init() {
|
void compress_icon_free(CompressIcon* instance) {
|
||||||
icon_decoder = malloc(sizeof(FuriHalCompressIcon));
|
furi_assert(instance);
|
||||||
icon_decoder->decoder = heatshrink_decoder_alloc(
|
heatshrink_decoder_free(instance->decoder);
|
||||||
icon_decoder->compress_buff,
|
free(instance);
|
||||||
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 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(icon_data);
|
||||||
furi_assert(decoded_buff);
|
furi_assert(decoded_buff);
|
||||||
|
|
||||||
FuriHalCompressHeader* header = (FuriHalCompressHeader*)icon_data;
|
CompressHeader* header = (CompressHeader*)icon_data;
|
||||||
if(header->is_compressed) {
|
if(header->is_compressed) {
|
||||||
size_t data_processed = 0;
|
size_t data_processed = 0;
|
||||||
heatshrink_decoder_sink(
|
heatshrink_decoder_sink(
|
||||||
icon_decoder->decoder,
|
instance->decoder,
|
||||||
(uint8_t*)&icon_data[4],
|
(uint8_t*)&icon_data[sizeof(CompressHeader)],
|
||||||
header->compressed_buff_size,
|
header->compressed_buff_size,
|
||||||
&data_processed);
|
&data_processed);
|
||||||
while(1) {
|
while(1) {
|
||||||
HSD_poll_res res = heatshrink_decoder_poll(
|
HSD_poll_res res = heatshrink_decoder_poll(
|
||||||
icon_decoder->decoder,
|
instance->decoder,
|
||||||
icon_decoder->decoded_buff,
|
instance->decoded_buff,
|
||||||
sizeof(icon_decoder->decoded_buff),
|
sizeof(instance->decoded_buff),
|
||||||
&data_processed);
|
&data_processed);
|
||||||
furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE));
|
furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE));
|
||||||
if(res != HSDR_POLL_MORE) {
|
if(res != HSDR_POLL_MORE) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
heatshrink_decoder_reset(icon_decoder->decoder);
|
heatshrink_decoder_reset(instance->decoder);
|
||||||
memset(icon_decoder->compress_buff, 0, sizeof(icon_decoder->compress_buff));
|
*decoded_buff = instance->decoded_buff;
|
||||||
*decoded_buff = icon_decoder->decoded_buff;
|
|
||||||
} else {
|
} else {
|
||||||
*decoded_buff = (uint8_t*)&icon_data[1];
|
*decoded_buff = (uint8_t*)&icon_data[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size) {
|
struct Compress {
|
||||||
FuriHalCompress* compress = malloc(sizeof(FuriHalCompress));
|
heatshrink_encoder* encoder;
|
||||||
compress->compress_buff = malloc(compress_buff_size + FURI_HAL_COMPRESS_EXP_BUFF_SIZE);
|
heatshrink_decoder* decoder;
|
||||||
compress->encoder = heatshrink_encoder_alloc(
|
};
|
||||||
compress->compress_buff,
|
|
||||||
FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG,
|
static void compress_reset(Compress* compress) {
|
||||||
FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
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->decoder = heatshrink_decoder_alloc(
|
||||||
compress->compress_buff,
|
compress_buff_size, COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
||||||
compress_buff_size,
|
|
||||||
FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG,
|
|
||||||
FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
|
||||||
|
|
||||||
return compress;
|
return compress;
|
||||||
}
|
}
|
||||||
|
|
||||||
void furi_hal_compress_free(FuriHalCompress* compress) {
|
void compress_free(Compress* compress) {
|
||||||
furi_assert(compress);
|
furi_assert(compress);
|
||||||
|
|
||||||
heatshrink_encoder_free(compress->encoder);
|
heatshrink_encoder_free(compress->encoder);
|
||||||
heatshrink_decoder_free(compress->decoder);
|
heatshrink_decoder_free(compress->decoder);
|
||||||
free(compress->compress_buff);
|
|
||||||
free(compress);
|
free(compress);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool furi_hal_compress_encode(
|
bool compress_encode(
|
||||||
FuriHalCompress* compress,
|
Compress* compress,
|
||||||
uint8_t* data_in,
|
uint8_t* data_in,
|
||||||
size_t data_in_size,
|
size_t data_in_size,
|
||||||
uint8_t* data_out,
|
uint8_t* data_out,
|
||||||
@@ -126,7 +123,7 @@ bool furi_hal_compress_encode(
|
|||||||
HSE_finish_res finish_res;
|
HSE_finish_res finish_res;
|
||||||
bool encode_failed = false;
|
bool encode_failed = false;
|
||||||
size_t sunk = 0;
|
size_t sunk = 0;
|
||||||
size_t res_buff_size = sizeof(FuriHalCompressHeader);
|
size_t res_buff_size = sizeof(CompressHeader);
|
||||||
|
|
||||||
// Sink data to encoding buffer
|
// Sink data to encoding buffer
|
||||||
while((sunk < data_in_size) && !encode_failed) {
|
while((sunk < data_in_size) && !encode_failed) {
|
||||||
@@ -174,7 +171,7 @@ bool furi_hal_compress_encode(
|
|||||||
bool result = true;
|
bool result = true;
|
||||||
// Write encoded data to output buffer if compression is efficient. Else - write header and original data
|
// 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)) {
|
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};
|
.is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size};
|
||||||
memcpy(data_out, &header, sizeof(header));
|
memcpy(data_out, &header, sizeof(header));
|
||||||
*data_res_size = res_buff_size;
|
*data_res_size = res_buff_size;
|
||||||
@@ -186,13 +183,13 @@ bool furi_hal_compress_encode(
|
|||||||
*data_res_size = 0;
|
*data_res_size = 0;
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
furi_hal_compress_reset(compress);
|
compress_reset(compress);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool furi_hal_compress_decode(
|
bool compress_decode(
|
||||||
FuriHalCompress* compress,
|
Compress* compress,
|
||||||
uint8_t* data_in,
|
uint8_t* data_in,
|
||||||
size_t data_in_size,
|
size_t data_in_size,
|
||||||
uint8_t* data_out,
|
uint8_t* data_out,
|
||||||
@@ -212,11 +209,11 @@ bool furi_hal_compress_decode(
|
|||||||
size_t res_buff_size = 0;
|
size_t res_buff_size = 0;
|
||||||
size_t poll_size = 0;
|
size_t poll_size = 0;
|
||||||
|
|
||||||
FuriHalCompressHeader* header = (FuriHalCompressHeader*)data_in;
|
CompressHeader* header = (CompressHeader*)data_in;
|
||||||
if(header->is_compressed) {
|
if(header->is_compressed) {
|
||||||
// Sink data to decoding buffer
|
// Sink data to decoding buffer
|
||||||
size_t compressed_size = header->compressed_buff_size;
|
size_t compressed_size = header->compressed_buff_size;
|
||||||
size_t sunk = sizeof(FuriHalCompressHeader);
|
size_t sunk = sizeof(CompressHeader);
|
||||||
while(sunk < compressed_size && !decode_failed) {
|
while(sunk < compressed_size && !decode_failed) {
|
||||||
sink_res = heatshrink_decoder_sink(
|
sink_res = heatshrink_decoder_sink(
|
||||||
compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size);
|
compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size);
|
||||||
@@ -258,7 +255,7 @@ bool furi_hal_compress_decode(
|
|||||||
} else {
|
} else {
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
furi_hal_compress_reset(compress);
|
compress_reset(compress);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
96
lib/toolbox/compress.h
Normal file
96
lib/toolbox/compress.h
Normal 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
|
||||||
Reference in New Issue
Block a user