Просмотр исходного кода

Merge branch 'flipperdevices:dev' into feature_wifi_marauder_app

0xchocolate 2 лет назад
Родитель
Сommit
c45075c4bb
100 измененных файлов с 1330 добавлено и 2780 удалено
  1. 3 0
      .github/CODEOWNERS
  2. 4 12
      .github/workflows/build.yml
  3. 2 5
      .github/workflows/lint_and_submodule_check.yml
  4. 2 5
      .github/workflows/merge_report.yml
  5. 2 5
      .github/workflows/pvs_studio.yml
  6. 3 6
      .github/workflows/unit_tests.yml
  7. 4 11
      .github/workflows/updater_test.yml
  8. 3 0
      .gitmodules
  9. 11 2
      applications/examples/example_thermo/example_thermo.c
  10. 2 3
      applications/external/nfc_magic/lib/magic/magic.c
  11. 5 3
      applications/external/nfc_magic/nfc_magic_worker.c
  12. 1 0
      applications/main/application.fam
  13. 1 1
      applications/main/bad_usb/bad_usb_app_i.h
  14. 184 144
      applications/main/bad_usb/helpers/ducky_script.c
  15. 2 25
      applications/main/bad_usb/helpers/ducky_script.h
  16. 177 0
      applications/main/bad_usb/helpers/ducky_script_commands.c
  17. 69 0
      applications/main/bad_usb/helpers/ducky_script_i.h
  18. 79 0
      applications/main/bad_usb/helpers/ducky_script_keycodes.c
  19. 0 327
      applications/main/bad_usb/mnemonic.c
  20. 0 96
      applications/main/bad_usb/mnemonic.h
  21. 1 1
      applications/main/bad_usb/scenes/bad_usb_scene_config.c
  22. 3 1
      applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c
  23. 1 1
      applications/main/bad_usb/scenes/bad_usb_scene_work.c
  24. 7 2
      applications/main/bad_usb/views/bad_usb_view.c
  25. 1 1
      applications/main/bad_usb/views/bad_usb_view.h
  26. 15 4
      applications/main/fap_loader/fap_loader_app.c
  27. 3 61
      applications/main/ibutton/ibutton_cli.c
  28. 2 2
      applications/main/ibutton/ibutton_i.h
  29. 2 1
      applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
  30. 8 0
      applications/main/nfc/views/dict_attack.c
  31. 2 0
      applications/main/nfc/views/dict_attack.h
  32. 14 0
      applications/main/onewire/application.fam
  33. 72 0
      applications/main/onewire/onewire_cli.c
  34. 0 0
      applications/main/u2f/hmac_sha256.c
  35. 0 0
      applications/main/u2f/hmac_sha256.h
  36. 1 1
      applications/main/u2f/u2f.c
  37. 6 3
      applications/services/gui/canvas.c
  38. 2 0
      applications/services/gui/canvas_i.h
  39. 1 0
      applications/services/gui/gui.c
  40. 5 1
      applications/services/gui/gui.h
  41. 1 1
      applications/services/gui/modules/submenu.c
  42. 24 2
      applications/services/rpc/rpc_gui.c
  43. 19 42
      applications/services/storage/storage_glue.c
  44. 1 1
      applications/services/storage/storage_glue.h
  45. 22 6
      applications/services/storage/storage_processing.c
  46. 3 1
      applications/services/storage/storages/storage_ext.c
  47. 1 1
      assets/protobuf
  48. 69 46
      debug/flipperapps.py
  49. 3 1
      documentation/AppsOnSDCard.md
  50. 6 6
      documentation/UnitTests.md
  51. 46 36
      documentation/file_formats/BadUsbScriptFormat.md
  52. 7 6
      documentation/file_formats/iButtonFileFormat.md
  53. 39 12
      firmware/targets/f18/api_symbols.csv
  54. 5 35
      firmware/targets/f18/furi_hal/furi_hal.c
  55. 4 0
      firmware/targets/f18/furi_hal/furi_hal_resources.c
  56. 3 2
      firmware/targets/f18/target.json
  57. 8 18
      firmware/targets/f7/api_symbols.csv
  58. 6 41
      firmware/targets/f7/fatfs/fatfs.c
  59. 10 40
      firmware/targets/f7/fatfs/fatfs.h
  60. 1 1
      firmware/targets/f7/fatfs/ffconf.h
  61. 0 116
      firmware/targets/f7/fatfs/syscall.c
  62. 107 123
      firmware/targets/f7/fatfs/user_diskio.c
  63. 3 37
      firmware/targets/f7/fatfs/user_diskio.h
  64. 0 32
      firmware/targets/f7/furi_hal/furi_hal.c
  65. 3 0
      firmware/targets/f7/furi_hal/furi_hal_ibutton.c
  66. 4 0
      firmware/targets/f7/furi_hal/furi_hal_resources.c
  67. 13 6
      firmware/targets/f7/furi_hal/furi_hal_uart.c
  68. 133 117
      firmware/targets/f7/furi_hal/furi_hal_usb_hid.c
  69. 5 2
      firmware/targets/f7/src/dfu.c
  70. 4 2
      firmware/targets/f7/src/recovery.c
  71. 1 1
      firmware/targets/f7/src/update.c
  72. 2 1
      firmware/targets/f7/target.json
  73. 0 1
      firmware/targets/furi_hal_include/furi_hal.h
  74. 0 87
      firmware/targets/furi_hal_include/furi_hal_compress.h
  75. 5 0
      firmware/targets/furi_hal_include/furi_hal_usb_hid.h
  76. 1 0
      lib/SConscript
  77. 4 0
      lib/err.h
  78. 3 2
      lib/flipper_application/elf/elf_file.c
  79. 39 8
      lib/flipper_application/flipper_application.c
  80. 1 0
      lib/heatshrink
  81. 0 20
      lib/heatshrink/heatshrink_common.h
  82. 0 28
      lib/heatshrink/heatshrink_config.h
  83. 0 364
      lib/heatshrink/heatshrink_decoder.c
  84. 0 100
      lib/heatshrink/heatshrink_decoder.h
  85. 0 602
      lib/heatshrink/heatshrink_encoder.c
  86. 0 109
      lib/heatshrink/heatshrink_encoder.h
  87. 24 0
      lib/ibutton/SConscript
  88. 0 0
      lib/ibutton/ibutton_key.c
  89. 0 0
      lib/ibutton/ibutton_key.h
  90. 0 0
      lib/ibutton/ibutton_key_i.h
  91. 0 0
      lib/ibutton/ibutton_protocols.c
  92. 0 0
      lib/ibutton/ibutton_protocols.h
  93. 0 0
      lib/ibutton/ibutton_worker.c
  94. 0 0
      lib/ibutton/ibutton_worker.h
  95. 0 0
      lib/ibutton/ibutton_worker_i.h
  96. 0 0
      lib/ibutton/ibutton_worker_modes.c
  97. 0 0
      lib/ibutton/protocols/blanks/rw1990.c
  98. 0 0
      lib/ibutton/protocols/blanks/rw1990.h
  99. 0 0
      lib/ibutton/protocols/blanks/tm2004.c
  100. 0 0
      lib/ibutton/protocols/blanks/tm2004.h

+ 3 - 0
.github/CODEOWNERS

@@ -44,6 +44,9 @@
 
 
 /applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov
 /applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov
 
 
+# Firmware targets
+/firmware/ @skotopes @DrZlo13 @hedger @nminaylov
+
 # Assets
 # Assets
 /assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
 /assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
 
 

+ 4 - 12
.github/workflows/build.yml

@@ -18,11 +18,8 @@ jobs:
   main:
   main:
     runs-on: [self-hosted,FlipperZeroShell]
     runs-on: [self-hosted,FlipperZeroShell]
     steps:
     steps:
-      - name: 'Decontaminate previous build leftovers'
-        run: |
-          if [ -d .git ]; then
-            git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
-          fi
+      - name: 'Wipe workspace'
+        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; 
 
 
       - name: 'Checkout code'
       - name: 'Checkout code'
         uses: actions/checkout@v3
         uses: actions/checkout@v3
@@ -166,13 +163,8 @@ jobs:
     if: ${{ !startsWith(github.ref, 'refs/tags') }}
     if: ${{ !startsWith(github.ref, 'refs/tags') }}
     runs-on: [self-hosted,FlipperZeroShell]
     runs-on: [self-hosted,FlipperZeroShell]
     steps:
     steps:
-      - name: 'Decontaminate previous build leftovers'
-        run: |
-          if [ -d .git ]
-          then
-            git submodule status \
-              || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
-          fi
+      - name: 'Wipe workspace'
+        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; 
 
 
       - name: 'Checkout code'
       - name: 'Checkout code'
         uses: actions/checkout@v3
         uses: actions/checkout@v3

+ 2 - 5
.github/workflows/lint_and_submodule_check.yml

@@ -18,11 +18,8 @@ jobs:
   lint_sources_check_submodules:
   lint_sources_check_submodules:
     runs-on: [self-hosted,FlipperZeroShell]
     runs-on: [self-hosted,FlipperZeroShell]
     steps:
     steps:
-      - name: 'Decontaminate previous build leftovers'
-        run: |
-          if [ -d .git ]; then
-            git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
-          fi
+      - name: 'Wipe workspace'
+        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; 
 
 
       - name: 'Checkout code'
       - name: 'Checkout code'
         uses: actions/checkout@v3
         uses: actions/checkout@v3

+ 2 - 5
.github/workflows/merge_report.yml

@@ -12,11 +12,8 @@ jobs:
   merge_report:
   merge_report:
     runs-on: [self-hosted,FlipperZeroShell]
     runs-on: [self-hosted,FlipperZeroShell]
     steps:
     steps:
-      - name: 'Decontaminate previous build leftovers'
-        run: |
-          if [ -d .git ]; then
-            git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
-          fi
+      - name: 'Wipe workspace'
+        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; 
 
 
       - name: 'Checkout code'
       - name: 'Checkout code'
         uses: actions/checkout@v3
         uses: actions/checkout@v3

+ 2 - 5
.github/workflows/pvs_studio.yml

@@ -19,11 +19,8 @@ jobs:
     if: ${{ !github.event.pull_request.head.repo.fork }}
     if: ${{ !github.event.pull_request.head.repo.fork }}
     runs-on: [self-hosted, FlipperZeroShell]
     runs-on: [self-hosted, FlipperZeroShell]
     steps:
     steps:
-      - name: 'Decontaminate previous build leftovers'
-        run: |
-          if [ -d .git ]; then
-            git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
-          fi
+      - name: 'Wipe workspace'
+        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; 
 
 
       - name: 'Checkout code'
       - name: 'Checkout code'
         uses: actions/checkout@v3
         uses: actions/checkout@v3

+ 3 - 6
.github/workflows/unit_tests.yml

@@ -12,11 +12,8 @@ jobs:
   run_units_on_bench:
   run_units_on_bench:
     runs-on: [self-hosted, FlipperZeroUnitTest]
     runs-on: [self-hosted, FlipperZeroUnitTest]
     steps:
     steps:
-      - name: 'Decontaminate previous build leftovers'
-        run: |
-          if [ -d .git ]; then
-            git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
-          fi
+      - name: 'Wipe workspace'
+        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; 
 
 
       - name: Checkout code
       - name: Checkout code
         uses: actions/checkout@v3
         uses: actions/checkout@v3
@@ -32,7 +29,7 @@ jobs:
       - name: 'Flash unit tests firmware'
       - name: 'Flash unit tests firmware'
         id: flashing
         id: flashing
         if: success()
         if: success()
-        run: |                               
+        run: |
           ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
           ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
 
 
       - name: 'Wait for flipper and format ext'
       - name: 'Wait for flipper and format ext'

+ 4 - 11
.github/workflows/updater_test.yml

@@ -12,11 +12,8 @@ jobs:
   test_updater_on_bench:
   test_updater_on_bench:
     runs-on: [self-hosted, FlipperZeroUpdaterTest]
     runs-on: [self-hosted, FlipperZeroUpdaterTest]
     steps:
     steps:
-      - name: 'Decontaminate previous build leftovers'
-        run: |
-          if [ -d .git ]; then
-            git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
-          fi
+      - name: 'Wipe workspace'
+        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; 
 
 
       - name: Checkout code
       - name: Checkout code
         uses: actions/checkout@v3
         uses: actions/checkout@v3
@@ -52,12 +49,8 @@ jobs:
         run: |
         run: |
           echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT
           echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT
 
 
-      - name: 'Decontaminate previous build leftovers'
-        if: failure()
-        run: |
-          if [ -d .git ]; then
-            git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
-          fi
+      - name: 'Wipe workspace'
+        run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; 
 
 
       - name: 'Checkout latest release'
       - name: 'Checkout latest release'
         uses: actions/checkout@v3
         uses: actions/checkout@v3

+ 3 - 0
.gitmodules

@@ -31,3 +31,6 @@
 [submodule "applications/external/dap_link/lib/free-dap"]
 [submodule "applications/external/dap_link/lib/free-dap"]
 	path = applications/external/dap_link/lib/free-dap
 	path = applications/external/dap_link/lib/free-dap
 	url = https://github.com/ataradov/free-dap.git
 	url = https://github.com/ataradov/free-dap.git
+[submodule "lib/heatshrink"]
+	path = lib/heatshrink
+	url = https://github.com/flipperdevices/heatshrink.git

+ 11 - 2
applications/examples/example_thermo/example_thermo.c

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

+ 2 - 3
applications/external/nfc_magic/lib/magic/magic.c

@@ -6,8 +6,7 @@
 
 
 #define MAGIC_CMD_WUPA (0x40)
 #define MAGIC_CMD_WUPA (0x40)
 #define MAGIC_CMD_WIPE (0x41)
 #define MAGIC_CMD_WIPE (0x41)
-#define MAGIC_CMD_READ (0x43)
-#define MAGIC_CMD_WRITE (0x43)
+#define MAGIC_CMD_ACCESS (0x43)
 
 
 #define MAGIC_MIFARE_READ_CMD (0x30)
 #define MAGIC_MIFARE_READ_CMD (0x30)
 #define MAGIC_MIFARE_WRITE_CMD (0xA0)
 #define MAGIC_MIFARE_WRITE_CMD (0xA0)
@@ -70,7 +69,7 @@ bool magic_data_access_cmd() {
     FuriHalNfcReturn ret = 0;
     FuriHalNfcReturn ret = 0;
 
 
     do {
     do {
-        tx_data[0] = MAGIC_CMD_WRITE;
+        tx_data[0] = MAGIC_CMD_ACCESS;
         ret = furi_hal_nfc_ll_txrx_bits(
         ret = furi_hal_nfc_ll_txrx_bits(
             tx_data,
             tx_data,
             8,
             8,

+ 5 - 3
applications/external/nfc_magic/nfc_magic_worker.c

@@ -85,15 +85,17 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) {
                 card_found_notified = true;
                 card_found_notified = true;
             }
             }
             furi_hal_nfc_sleep();
             furi_hal_nfc_sleep();
-
             if(!magic_wupa()) {
             if(!magic_wupa()) {
-                FURI_LOG_E(TAG, "Not Magic card");
+                FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)");
                 nfc_magic_worker->callback(
                 nfc_magic_worker->callback(
                     NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
                     NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
                 break;
                 break;
             }
             }
+            furi_hal_nfc_sleep();
+        }
+        if(magic_wupa()) {
             if(!magic_data_access_cmd()) {
             if(!magic_data_access_cmd()) {
-                FURI_LOG_E(TAG, "Not Magic card");
+                FURI_LOG_E(TAG, "No card response to data access command (not a magic card)");
                 nfc_magic_worker->callback(
                 nfc_magic_worker->callback(
                     NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
                     NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
                 break;
                 break;

+ 1 - 0
applications/main/application.fam

@@ -4,6 +4,7 @@ App(
     apptype=FlipperAppType.METAPACKAGE,
     apptype=FlipperAppType.METAPACKAGE,
     provides=[
     provides=[
         "gpio",
         "gpio",
+        "onewire",
         "ibutton",
         "ibutton",
         "infrared",
         "infrared",
         "lfrfid",
         "lfrfid",

+ 1 - 1
applications/main/bad_usb/bad_usb_app_i.h

@@ -2,7 +2,7 @@
 
 
 #include "bad_usb_app.h"
 #include "bad_usb_app.h"
 #include "scenes/bad_usb_scene.h"
 #include "scenes/bad_usb_scene.h"
-#include "bad_usb_script.h"
+#include "helpers/ducky_script.h"
 
 
 #include <gui/gui.h>
 #include <gui/gui.h>
 #include <assets_icons.h>
 #include <assets_icons.h>

+ 184 - 144
applications/main/bad_usb/bad_usb_script.c → applications/main/bad_usb/helpers/ducky_script.c

@@ -5,17 +5,13 @@
 #include <lib/toolbox/args.h>
 #include <lib/toolbox/args.h>
 #include <furi_hal_usb_hid.h>
 #include <furi_hal_usb_hid.h>
 #include <storage/storage.h>
 #include <storage/storage.h>
-#include "bad_usb_script.h"
-#include "mnemonic.h"
+#include "ducky_script.h"
+#include "ducky_script_i.h"
 #include <dolphin/dolphin.h>
 #include <dolphin/dolphin.h>
 
 
 #define TAG "BadUSB"
 #define TAG "BadUSB"
 #define WORKER_TAG TAG "Worker"
 #define WORKER_TAG TAG "Worker"
 
 
-#define SCRIPT_STATE_ERROR (-1)
-#define SCRIPT_STATE_END (-2)
-#define SCRIPT_STATE_NEXT_LINE (-3)
-
 #define BADUSB_ASCII_TO_KEY(script, x) \
 #define BADUSB_ASCII_TO_KEY(script, x) \
     (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
     (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
 
 
@@ -26,87 +22,20 @@ typedef enum {
     WorkerEvtDisconnect = (1 << 3),
     WorkerEvtDisconnect = (1 << 3),
 } WorkerEvtFlags;
 } WorkerEvtFlags;
 
 
-typedef struct {
-    char* name;
-    uint16_t keycode;
-} DuckyKey;
-
-static const DuckyKey ducky_keys[] = {
-    {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT},
-    {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT},
-    {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
-    {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
-    {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
-    {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL},
-
-    {"CTRL", KEY_MOD_LEFT_CTRL},
-    {"CONTROL", KEY_MOD_LEFT_CTRL},
-    {"SHIFT", KEY_MOD_LEFT_SHIFT},
-    {"ALT", KEY_MOD_LEFT_ALT},
-    {"GUI", KEY_MOD_LEFT_GUI},
-    {"WINDOWS", KEY_MOD_LEFT_GUI},
-
-    {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW},
-    {"DOWN", HID_KEYBOARD_DOWN_ARROW},
-    {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
-    {"LEFT", HID_KEYBOARD_LEFT_ARROW},
-    {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW},
-    {"RIGHT", HID_KEYBOARD_RIGHT_ARROW},
-    {"UPARROW", HID_KEYBOARD_UP_ARROW},
-    {"UP", HID_KEYBOARD_UP_ARROW},
-
-    {"ENTER", HID_KEYBOARD_RETURN},
-    {"BREAK", HID_KEYBOARD_PAUSE},
-    {"PAUSE", HID_KEYBOARD_PAUSE},
-    {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
-    {"DELETE", HID_KEYBOARD_DELETE_FORWARD},
-    {"BACKSPACE", HID_KEYBOARD_DELETE},
-    {"END", HID_KEYBOARD_END},
-    {"ESC", HID_KEYBOARD_ESCAPE},
-    {"ESCAPE", HID_KEYBOARD_ESCAPE},
-    {"HOME", HID_KEYBOARD_HOME},
-    {"INSERT", HID_KEYBOARD_INSERT},
-    {"NUMLOCK", HID_KEYPAD_NUMLOCK},
-    {"PAGEUP", HID_KEYBOARD_PAGE_UP},
-    {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
-    {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
-    {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
-    {"SPACE", HID_KEYBOARD_SPACEBAR},
-    {"TAB", HID_KEYBOARD_TAB},
-    {"MENU", HID_KEYBOARD_APPLICATION},
-    {"APP", HID_KEYBOARD_APPLICATION},
-
-    {"F1", HID_KEYBOARD_F1},
-    {"F2", HID_KEYBOARD_F2},
-    {"F3", HID_KEYBOARD_F3},
-    {"F4", HID_KEYBOARD_F4},
-    {"F5", HID_KEYBOARD_F5},
-    {"F6", HID_KEYBOARD_F6},
-    {"F7", HID_KEYBOARD_F7},
-    {"F8", HID_KEYBOARD_F8},
-    {"F9", HID_KEYBOARD_F9},
-    {"F10", HID_KEYBOARD_F10},
-    {"F11", HID_KEYBOARD_F11},
-    {"F12", HID_KEYBOARD_F12},
-};
-
-static const char ducky_cmd_comment[] = {"REM"};
 static const char ducky_cmd_id[] = {"ID"};
 static const char ducky_cmd_id[] = {"ID"};
-static const char ducky_cmd_delay[] = {"DELAY "};
-static const char ducky_cmd_string[] = {"STRING "};
-static const char ducky_cmd_stringln[] = {"STRINGLN "};
-static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "};
-static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "};
-static const char ducky_cmd_stringdelay_1[] = {"STRINGDELAY "};
-static const char ducky_cmd_stringdelay_2[] = {"STRING_DELAY "};
-static const char ducky_cmd_repeat[] = {"REPEAT "};
-static const char ducky_cmd_sysrq[] = {"SYSRQ "};
-static const char ducky_cmd_hold[] = {"HOLD "};
-static const char ducky_cmd_release[] = {"RELEASE "};
-
-static const char ducky_cmd_altchar[] = {"ALTCHAR "};
-static const char ducky_cmd_altstr_1[] = {"ALTSTRING "};
-static const char ducky_cmd_altstr_2[] = {"ALTCODE "};
+
+static const uint8_t numpad_keys[10] = {
+    HID_KEYPAD_0,
+    HID_KEYPAD_1,
+    HID_KEYPAD_2,
+    HID_KEYPAD_3,
+    HID_KEYPAD_4,
+    HID_KEYPAD_5,
+    HID_KEYPAD_6,
+    HID_KEYPAD_7,
+    HID_KEYPAD_8,
+    HID_KEYPAD_9,
+};
 
 
 uint32_t ducky_get_command_len(const char* line) {
 uint32_t ducky_get_command_len(const char* line) {
     uint32_t len = strlen(line);
     uint32_t len = strlen(line);
@@ -121,76 +50,150 @@ bool ducky_is_line_end(const char chr) {
 }
 }
 
 
 uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) {
 uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) {
-    for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
-        size_t key_cmd_len = strlen(ducky_keys[i].name);
-        if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
-           (ducky_is_line_end(param[key_cmd_len]))) {
-            return ducky_keys[i].keycode;
-        }
+    uint16_t keycode = ducky_get_keycode_by_name(param);
+    if(keycode != HID_KEYBOARD_NONE) {
+        return keycode;
     }
     }
+
     if((accept_chars) && (strlen(param) > 0)) {
     if((accept_chars) && (strlen(param) > 0)) {
         return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF);
         return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF);
     }
     }
     return 0;
     return 0;
 }
 }
 
 
-static int32_t
-    ducky_parse_line(BadUsbScript* bad_usb, FuriString* line, char* error, size_t error_len) {
+bool ducky_get_number(const char* param, uint32_t* val) {
+    uint32_t value = 0;
+    if(sscanf(param, "%lu", &value) == 1) {
+        *val = value;
+        return true;
+    }
+    return false;
+}
+
+void ducky_numlock_on() {
+    if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) {
+        furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
+        furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);
+    }
+}
+bool ducky_numpad_press(const char num) {
+    if((num < '0') || (num > '9')) return false;
+
+    uint16_t key = numpad_keys[num - '0'];
+    furi_hal_hid_kb_press(key);
+    furi_hal_hid_kb_release(key);
+
+    return true;
+}
+
+bool ducky_altchar(const char* charcode) {
+    uint8_t i = 0;
+    bool state = false;
+
+    furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT);
+
+    while(!ducky_is_line_end(charcode[i])) {
+        state = ducky_numpad_press(charcode[i]);
+        if(state == false) break;
+        i++;
+    }
+
+    furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT);
+    return state;
+}
+
+bool ducky_altstring(const char* param) {
+    uint32_t i = 0;
+    bool state = false;
+
+    while(param[i] != '\0') {
+        if((param[i] < ' ') || (param[i] > '~')) {
+            i++;
+            continue; // Skip non-printable chars
+        }
+
+        char temp_str[4];
+        snprintf(temp_str, 4, "%u", param[i]);
+
+        state = ducky_altchar(temp_str);
+        if(state == false) break;
+        i++;
+    }
+    return state;
+}
+
+int32_t ducky_error(BadUsbScript* bad_usb, const char* text, ...) {
+    va_list args;
+    va_start(args, text);
+
+    vsnprintf(bad_usb->st.error, sizeof(bad_usb->st.error), text, args);
+
+    va_end(args);
+    return SCRIPT_STATE_ERROR;
+}
+
+bool ducky_string(BadUsbScript* bad_usb, const char* param) {
+    uint32_t i = 0;
+
+    while(param[i] != '\0') {
+        if(param[i] != '\n') {
+            uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]);
+            if(keycode != HID_KEYBOARD_NONE) {
+                furi_hal_hid_kb_press(keycode);
+                furi_hal_hid_kb_release(keycode);
+            }
+        } else {
+            furi_hal_hid_kb_press(HID_KEYBOARD_RETURN);
+            furi_hal_hid_kb_release(HID_KEYBOARD_RETURN);
+        }
+        i++;
+    }
+    bad_usb->stringdelay = 0;
+    return true;
+}
+
+static bool ducky_string_next(BadUsbScript* bad_usb) {
+    if(bad_usb->string_print_pos >= furi_string_size(bad_usb->string_print)) {
+        return true;
+    }
+
+    char print_char = furi_string_get_char(bad_usb->string_print, bad_usb->string_print_pos);
+
+    if(print_char != '\n') {
+        uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, print_char);
+        if(keycode != HID_KEYBOARD_NONE) {
+            furi_hal_hid_kb_press(keycode);
+            furi_hal_hid_kb_release(keycode);
+        }
+    } else {
+        furi_hal_hid_kb_press(HID_KEYBOARD_RETURN);
+        furi_hal_hid_kb_release(HID_KEYBOARD_RETURN);
+    }
+
+    bad_usb->string_print_pos++;
+
+    return false;
+}
+
+static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
     uint32_t line_len = furi_string_size(line);
     uint32_t line_len = furi_string_size(line);
     const char* line_tmp = furi_string_get_cstr(line);
     const char* line_tmp = furi_string_get_cstr(line);
-    const char* ducky_cmd_table[] = {
-        ducky_cmd_comment,
-        ducky_cmd_id,
-        ducky_cmd_delay,
-        ducky_cmd_string,
-        ducky_cmd_defdelay_1,
-        ducky_cmd_defdelay_2,
-        ducky_cmd_stringdelay_1,
-        ducky_cmd_stringdelay_2,
-        ducky_cmd_repeat,
-        ducky_cmd_sysrq,
-        ducky_cmd_altchar,
-        ducky_cmd_altstr_1,
-        ducky_cmd_altstr_2,
-        ducky_cmd_stringln,
-        ducky_cmd_hold,
-        ducky_cmd_release,
-        NULL};
-    int32_t (*fnc_ptr[])(BadUsbScript*, FuriString*, const char*, char*, size_t) = {
-        &ducky_fnc_noop,
-        &ducky_fnc_noop,
-        &ducky_fnc_delay,
-        &ducky_fnc_string,
-        &ducky_fnc_defdelay,
-        &ducky_fnc_defdelay,
-        &ducky_fnc_strdelay,
-        &ducky_fnc_strdelay,
-        &ducky_fnc_repeat,
-        &ducky_fnc_sysrq,
-        &ducky_fnc_altchar,
-        &ducky_fnc_altstring,
-        &ducky_fnc_altstring,
-        &ducky_fnc_stringln,
-        &ducky_fnc_hold,
-        &ducky_fnc_release,
-        NULL};
 
 
     if(line_len == 0) {
     if(line_len == 0) {
         return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
         return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
     }
     }
     FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
     FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
+
     // Ducky Lang Functions
     // Ducky Lang Functions
-    for(size_t i = 0; ducky_cmd_table[i]; i++) {
-        if(strncmp(line_tmp, ducky_cmd_table[i], strlen(ducky_cmd_table[i])) == 0)
-            return ((fnc_ptr[i])(bad_usb, line, line_tmp, error, error_len));
+    int32_t cmd_result = ducky_execute_cmd(bad_usb, line_tmp);
+    if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) {
+        return cmd_result;
     }
     }
+
     // Special keys + modifiers
     // Special keys + modifiers
     uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
     uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
     if(key == HID_KEYBOARD_NONE) {
     if(key == HID_KEYBOARD_NONE) {
-        if(error != NULL) {
-            snprintf(error, error_len, "No keycode defined for %s", line_tmp);
-        }
-        return SCRIPT_STATE_ERROR;
+        return ducky_error(bad_usb, "No keycode defined for %s", line_tmp);
     }
     }
     if((key & 0xFF00) != 0) {
     if((key & 0xFF00) != 0) {
         // It's a modifier key
         // It's a modifier key
@@ -199,7 +202,7 @@ static int32_t
     }
     }
     furi_hal_hid_kb_press(key);
     furi_hal_hid_kb_press(key);
     furi_hal_hid_kb_release(key);
     furi_hal_hid_kb_release(key);
-    return (0);
+    return 0;
 }
 }
 
 
 static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
 static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
@@ -277,8 +280,7 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
 
 
     if(bad_usb->repeat_cnt > 0) {
     if(bad_usb->repeat_cnt > 0) {
         bad_usb->repeat_cnt--;
         bad_usb->repeat_cnt--;
-        delay_val = ducky_parse_line(
-            bad_usb, bad_usb->line_prev, bad_usb->st.error, sizeof(bad_usb->st.error));
+        delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev);
         if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
         if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
             return 0;
             return 0;
         } else if(delay_val < 0) { // Script error
         } else if(delay_val < 0) { // Script error
@@ -313,10 +315,11 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
                 bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
                 bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
                 bad_usb->buf_start = i + 1;
                 bad_usb->buf_start = i + 1;
                 furi_string_trim(bad_usb->line);
                 furi_string_trim(bad_usb->line);
-                delay_val = ducky_parse_line(
-                    bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error));
+                delay_val = ducky_parse_line(bad_usb, bad_usb->line);
                 if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
                 if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
                     return 0;
                     return 0;
+                } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
+                    return delay_val;
                 } else if(delay_val < 0) {
                 } else if(delay_val < 0) {
                     bad_usb->st.error_line = bad_usb->st.line_cur;
                     bad_usb->st.error_line = bad_usb->st.line_cur;
                     FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
                     FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
@@ -339,10 +342,11 @@ static void bad_usb_hid_state_callback(bool state, void* context) {
     furi_assert(context);
     furi_assert(context);
     BadUsbScript* bad_usb = context;
     BadUsbScript* bad_usb = context;
 
 
-    if(state == true)
+    if(state == true) {
         furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect);
         furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect);
-    else
+    } else {
         furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect);
         furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect);
+    }
 }
 }
 
 
 static uint32_t bad_usb_flags_get(uint32_t flags_mask, uint32_t timeout) {
 static uint32_t bad_usb_flags_get(uint32_t flags_mask, uint32_t timeout) {
@@ -368,6 +372,7 @@ static int32_t bad_usb_worker(void* context) {
     File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
     File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
     bad_usb->line = furi_string_alloc();
     bad_usb->line = furi_string_alloc();
     bad_usb->line_prev = furi_string_alloc();
     bad_usb->line_prev = furi_string_alloc();
+    bad_usb->string_print = furi_string_alloc();
 
 
     furi_hal_hid_set_state_callback(bad_usb_hid_state_callback, bad_usb);
     furi_hal_hid_set_state_callback(bad_usb_hid_state_callback, bad_usb);
 
 
@@ -420,6 +425,7 @@ static int32_t bad_usb_worker(void* context) {
                 bad_usb->defdelay = 0;
                 bad_usb->defdelay = 0;
                 bad_usb->stringdelay = 0;
                 bad_usb->stringdelay = 0;
                 bad_usb->repeat_cnt = 0;
                 bad_usb->repeat_cnt = 0;
+                bad_usb->key_hold_nb = 0;
                 bad_usb->file_end = false;
                 bad_usb->file_end = false;
                 storage_file_seek(script_file, 0, true);
                 storage_file_seek(script_file, 0, true);
                 worker_state = BadUsbStateRunning;
                 worker_state = BadUsbStateRunning;
@@ -492,12 +498,17 @@ static int32_t bad_usb_worker(void* context) {
                     delay_val = 0;
                     delay_val = 0;
                     worker_state = BadUsbStateScriptError;
                     worker_state = BadUsbStateScriptError;
                     bad_usb->st.state = worker_state;
                     bad_usb->st.state = worker_state;
+                    furi_hal_hid_kb_release_all();
                 } else if(delay_val == SCRIPT_STATE_END) { // End of script
                 } else if(delay_val == SCRIPT_STATE_END) { // End of script
                     delay_val = 0;
                     delay_val = 0;
                     worker_state = BadUsbStateIdle;
                     worker_state = BadUsbStateIdle;
                     bad_usb->st.state = BadUsbStateDone;
                     bad_usb->st.state = BadUsbStateDone;
                     furi_hal_hid_kb_release_all();
                     furi_hal_hid_kb_release_all();
                     continue;
                     continue;
+                } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays
+                    delay_val = bad_usb->defdelay;
+                    bad_usb->string_print_pos = 0;
+                    worker_state = BadUsbStateStringDelay;
                 } else if(delay_val > 1000) {
                 } else if(delay_val > 1000) {
                     bad_usb->st.state = BadUsbStateDelay; // Show long delays
                     bad_usb->st.state = BadUsbStateDelay; // Show long delays
                     bad_usb->st.delay_remain = delay_val / 1000;
                     bad_usb->st.delay_remain = delay_val / 1000;
@@ -505,7 +516,35 @@ static int32_t bad_usb_worker(void* context) {
             } else {
             } else {
                 furi_check((flags & FuriFlagError) == 0);
                 furi_check((flags & FuriFlagError) == 0);
             }
             }
+        } else if(worker_state == BadUsbStateStringDelay) { // State: print string with delays
+            uint32_t flags = furi_thread_flags_wait(
+                WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
+                FuriFlagWaitAny,
+                bad_usb->stringdelay);
 
 
+            if(!(flags & FuriFlagError)) {
+                if(flags & WorkerEvtEnd) {
+                    break;
+                } else if(flags & WorkerEvtToggle) {
+                    worker_state = BadUsbStateIdle; // Stop executing script
+                    furi_hal_hid_kb_release_all();
+                } else if(flags & WorkerEvtDisconnect) {
+                    worker_state = BadUsbStateNotConnected; // USB disconnected
+                    furi_hal_hid_kb_release_all();
+                }
+                bad_usb->st.state = worker_state;
+                continue;
+            } else if(
+                (flags == (unsigned)FuriFlagErrorTimeout) ||
+                (flags == (unsigned)FuriFlagErrorResource)) {
+                bool string_end = ducky_string_next(bad_usb);
+                if(string_end) {
+                    bad_usb->stringdelay = 0;
+                    worker_state = BadUsbStateRunning;
+                }
+            } else {
+                furi_check((flags & FuriFlagError) == 0);
+            }
         } else if(
         } else if(
             (worker_state == BadUsbStateFileError) ||
             (worker_state == BadUsbStateFileError) ||
             (worker_state == BadUsbStateScriptError)) { // State: error
             (worker_state == BadUsbStateScriptError)) { // State: error
@@ -524,6 +563,7 @@ static int32_t bad_usb_worker(void* context) {
     storage_file_free(script_file);
     storage_file_free(script_file);
     furi_string_free(bad_usb->line);
     furi_string_free(bad_usb->line);
     furi_string_free(bad_usb->line_prev);
     furi_string_free(bad_usb->line_prev);
+    furi_string_free(bad_usb->string_print);
 
 
     FURI_LOG_I(WORKER_TAG, "End");
     FURI_LOG_I(WORKER_TAG, "End");
 
 

+ 2 - 25
applications/main/bad_usb/bad_usb_script.h → applications/main/bad_usb/helpers/ducky_script.h

@@ -7,8 +7,6 @@ extern "C" {
 #include <furi.h>
 #include <furi.h>
 #include <furi_hal.h>
 #include <furi_hal.h>
 
 
-#define FILE_BUFFER_LEN 16
-
 typedef enum {
 typedef enum {
     BadUsbStateInit,
     BadUsbStateInit,
     BadUsbStateNotConnected,
     BadUsbStateNotConnected,
@@ -16,6 +14,7 @@ typedef enum {
     BadUsbStateWillRun,
     BadUsbStateWillRun,
     BadUsbStateRunning,
     BadUsbStateRunning,
     BadUsbStateDelay,
     BadUsbStateDelay,
+    BadUsbStateStringDelay,
     BadUsbStateDone,
     BadUsbStateDone,
     BadUsbStateScriptError,
     BadUsbStateScriptError,
     BadUsbStateFileError,
     BadUsbStateFileError,
@@ -30,23 +29,7 @@ typedef struct {
     char error[64];
     char error[64];
 } BadUsbState;
 } BadUsbState;
 
 
-typedef struct BadUsbScript {
-    FuriHalUsbHidConfig hid_cfg;
-    BadUsbState st;
-    FuriString* file_path;
-    uint32_t defdelay;
-    uint16_t layout[128];
-    uint32_t stringdelay;
-    FuriThread* thread;
-    uint8_t file_buf[FILE_BUFFER_LEN + 1];
-    uint8_t buf_start;
-    uint8_t buf_len;
-    bool file_end;
-    FuriString* line;
-
-    FuriString* line_prev;
-    uint32_t repeat_cnt;
-} BadUsbScript;
+typedef struct BadUsbScript BadUsbScript;
 
 
 BadUsbScript* bad_usb_script_open(FuriString* file_path);
 BadUsbScript* bad_usb_script_open(FuriString* file_path);
 
 
@@ -62,12 +45,6 @@ void bad_usb_script_toggle(BadUsbScript* bad_usb);
 
 
 BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb);
 BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb);
 
 
-uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars);
-
-uint32_t ducky_get_command_len(const char* line);
-
-bool ducky_is_line_end(const char chr);
-
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 177 - 0
applications/main/bad_usb/helpers/ducky_script_commands.c

@@ -0,0 +1,177 @@
+#include <furi_hal.h>
+#include <furi_hal_usb_hid.h>
+#include "ducky_script.h"
+#include "ducky_script_i.h"
+
+typedef int32_t (*DuckyCmdCallback)(BadUsbScript* bad_usb, const char* line, int32_t param);
+
+typedef struct {
+    char* name;
+    DuckyCmdCallback callback;
+    int32_t param;
+} DuckyCmd;
+
+static int32_t ducky_fnc_delay(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    uint32_t delay_val = 0;
+    bool state = ducky_get_number(line, &delay_val);
+    if((state) && (delay_val > 0)) {
+        return (int32_t)delay_val;
+    }
+
+    return ducky_error(bad_usb, "Invalid number %s", line);
+}
+
+static int32_t ducky_fnc_defdelay(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    bool state = ducky_get_number(line, &bad_usb->defdelay);
+    if(!state) {
+        return ducky_error(bad_usb, "Invalid number %s", line);
+    }
+    return 0;
+}
+
+static int32_t ducky_fnc_strdelay(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    bool state = ducky_get_number(line, &bad_usb->stringdelay);
+    if(!state) {
+        return ducky_error(bad_usb, "Invalid number %s", line);
+    }
+    return 0;
+}
+
+static int32_t ducky_fnc_string(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    line = &line[ducky_get_command_len(line) + 1];
+    furi_string_set_str(bad_usb->string_print, line);
+    if(param == 1) {
+        furi_string_cat(bad_usb->string_print, "\n");
+    }
+
+    if(bad_usb->stringdelay == 0) { // stringdelay not set - run command immidiately
+        bool state = ducky_string(bad_usb, furi_string_get_cstr(bad_usb->string_print));
+        if(!state) {
+            return ducky_error(bad_usb, "Invalid string %s", line);
+        }
+    } else { // stringdelay is set - run command in thread to keep handling external events
+        return SCRIPT_STATE_STRING_START;
+    }
+
+    return 0;
+}
+
+static int32_t ducky_fnc_repeat(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    bool state = ducky_get_number(line, &bad_usb->repeat_cnt);
+    if((!state) || (bad_usb->repeat_cnt == 0)) {
+        return ducky_error(bad_usb, "Invalid number %s", line);
+    }
+    return 0;
+}
+
+static int32_t ducky_fnc_sysrq(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    uint16_t key = ducky_get_keycode(bad_usb, line, true);
+    furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
+    furi_hal_hid_kb_press(key);
+    furi_hal_hid_kb_release_all();
+    return 0;
+}
+
+static int32_t ducky_fnc_altchar(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    ducky_numlock_on();
+    bool state = ducky_altchar(line);
+    if(!state) {
+        return ducky_error(bad_usb, "Invalid altchar %s", line);
+    }
+    return 0;
+}
+
+static int32_t ducky_fnc_altstring(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    ducky_numlock_on();
+    bool state = ducky_altstring(line);
+    if(!state) {
+        return ducky_error(bad_usb, "Invalid altstring %s", line);
+    }
+    return 0;
+}
+
+static int32_t ducky_fnc_hold(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    uint16_t key = ducky_get_keycode(bad_usb, line, true);
+    if(key == HID_KEYBOARD_NONE) {
+        return ducky_error(bad_usb, "No keycode defined for %s", line);
+    }
+    bad_usb->key_hold_nb++;
+    if(bad_usb->key_hold_nb > (HID_KB_MAX_KEYS - 1)) {
+        return ducky_error(bad_usb, "Too many keys are hold");
+    }
+    furi_hal_hid_kb_press(key);
+    return 0;
+}
+
+static int32_t ducky_fnc_release(BadUsbScript* bad_usb, const char* line, int32_t param) {
+    UNUSED(param);
+
+    line = &line[ducky_get_command_len(line) + 1];
+    uint16_t key = ducky_get_keycode(bad_usb, line, true);
+    if(key == HID_KEYBOARD_NONE) {
+        return ducky_error(bad_usb, "No keycode defined for %s", line);
+    }
+    if(bad_usb->key_hold_nb == 0) {
+        return ducky_error(bad_usb, "No keys are hold");
+    }
+    bad_usb->key_hold_nb--;
+    furi_hal_hid_kb_release(key);
+    return 0;
+}
+
+static const DuckyCmd ducky_commands[] = {
+    {"REM ", NULL, -1},
+    {"ID ", NULL, -1},
+    {"DELAY ", ducky_fnc_delay, -1},
+    {"STRING ", ducky_fnc_string, 0},
+    {"STRINGLN ", ducky_fnc_string, 1},
+    {"DEFAULT_DELAY ", ducky_fnc_defdelay, -1},
+    {"DEFAULTDELAY ", ducky_fnc_defdelay, -1},
+    {"STRINGDELAY ", ducky_fnc_strdelay, -1},
+    {"STRING_DELAY ", ducky_fnc_strdelay, -1},
+    {"REPEAT ", ducky_fnc_repeat, -1},
+    {"SYSRQ ", ducky_fnc_sysrq, -1},
+    {"ALTCHAR ", ducky_fnc_altchar, -1},
+    {"ALTSTRING ", ducky_fnc_altstring, -1},
+    {"ALTCODE ", ducky_fnc_altstring, -1},
+    {"HOLD ", ducky_fnc_hold, -1},
+    {"RELEASE ", ducky_fnc_release, -1},
+};
+
+int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) {
+    for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) {
+        if(strncmp(line, ducky_commands[i].name, strlen(ducky_commands[i].name)) == 0) {
+            if(ducky_commands[i].callback == NULL) {
+                return 0;
+            } else {
+                return ((ducky_commands[i].callback)(bad_usb, line, ducky_commands[i].param));
+            }
+        }
+    }
+
+    return SCRIPT_STATE_CMD_UNKNOWN;
+}

+ 69 - 0
applications/main/bad_usb/helpers/ducky_script_i.h

@@ -0,0 +1,69 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <furi.h>
+#include <furi_hal.h>
+#include "ducky_script.h"
+
+#define SCRIPT_STATE_ERROR (-1)
+#define SCRIPT_STATE_END (-2)
+#define SCRIPT_STATE_NEXT_LINE (-3)
+#define SCRIPT_STATE_CMD_UNKNOWN (-4)
+#define SCRIPT_STATE_STRING_START (-5)
+
+#define FILE_BUFFER_LEN 16
+
+struct BadUsbScript {
+    FuriHalUsbHidConfig hid_cfg;
+    FuriThread* thread;
+    BadUsbState st;
+
+    FuriString* file_path;
+    uint8_t file_buf[FILE_BUFFER_LEN + 1];
+    uint8_t buf_start;
+    uint8_t buf_len;
+    bool file_end;
+
+    uint32_t defdelay;
+    uint32_t stringdelay;
+    uint16_t layout[128];
+
+    FuriString* line;
+    FuriString* line_prev;
+    uint32_t repeat_cnt;
+    uint8_t key_hold_nb;
+
+    FuriString* string_print;
+    size_t string_print_pos;
+};
+
+uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars);
+
+uint32_t ducky_get_command_len(const char* line);
+
+bool ducky_is_line_end(const char chr);
+
+uint16_t ducky_get_keycode_by_name(const char* param);
+
+bool ducky_get_number(const char* param, uint32_t* val);
+
+void ducky_numlock_on(void);
+
+bool ducky_numpad_press(const char num);
+
+bool ducky_altchar(const char* charcode);
+
+bool ducky_altstring(const char* param);
+
+bool ducky_string(BadUsbScript* bad_usb, const char* param);
+
+int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line);
+
+int32_t ducky_error(BadUsbScript* bad_usb, const char* text, ...);
+
+#ifdef __cplusplus
+}
+#endif

+ 79 - 0
applications/main/bad_usb/helpers/ducky_script_keycodes.c

@@ -0,0 +1,79 @@
+#include <furi_hal.h>
+#include <furi_hal_usb_hid.h>
+#include "ducky_script_i.h"
+
+typedef struct {
+    char* name;
+    uint16_t keycode;
+} DuckyKey;
+
+static const DuckyKey ducky_keys[] = {
+    {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT},
+    {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT},
+    {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
+    {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
+    {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
+    {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL},
+
+    {"CTRL", KEY_MOD_LEFT_CTRL},
+    {"CONTROL", KEY_MOD_LEFT_CTRL},
+    {"SHIFT", KEY_MOD_LEFT_SHIFT},
+    {"ALT", KEY_MOD_LEFT_ALT},
+    {"GUI", KEY_MOD_LEFT_GUI},
+    {"WINDOWS", KEY_MOD_LEFT_GUI},
+
+    {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW},
+    {"DOWN", HID_KEYBOARD_DOWN_ARROW},
+    {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
+    {"LEFT", HID_KEYBOARD_LEFT_ARROW},
+    {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW},
+    {"RIGHT", HID_KEYBOARD_RIGHT_ARROW},
+    {"UPARROW", HID_KEYBOARD_UP_ARROW},
+    {"UP", HID_KEYBOARD_UP_ARROW},
+
+    {"ENTER", HID_KEYBOARD_RETURN},
+    {"BREAK", HID_KEYBOARD_PAUSE},
+    {"PAUSE", HID_KEYBOARD_PAUSE},
+    {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
+    {"DELETE", HID_KEYBOARD_DELETE_FORWARD},
+    {"BACKSPACE", HID_KEYBOARD_DELETE},
+    {"END", HID_KEYBOARD_END},
+    {"ESC", HID_KEYBOARD_ESCAPE},
+    {"ESCAPE", HID_KEYBOARD_ESCAPE},
+    {"HOME", HID_KEYBOARD_HOME},
+    {"INSERT", HID_KEYBOARD_INSERT},
+    {"NUMLOCK", HID_KEYPAD_NUMLOCK},
+    {"PAGEUP", HID_KEYBOARD_PAGE_UP},
+    {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
+    {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
+    {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
+    {"SPACE", HID_KEYBOARD_SPACEBAR},
+    {"TAB", HID_KEYBOARD_TAB},
+    {"MENU", HID_KEYBOARD_APPLICATION},
+    {"APP", HID_KEYBOARD_APPLICATION},
+
+    {"F1", HID_KEYBOARD_F1},
+    {"F2", HID_KEYBOARD_F2},
+    {"F3", HID_KEYBOARD_F3},
+    {"F4", HID_KEYBOARD_F4},
+    {"F5", HID_KEYBOARD_F5},
+    {"F6", HID_KEYBOARD_F6},
+    {"F7", HID_KEYBOARD_F7},
+    {"F8", HID_KEYBOARD_F8},
+    {"F9", HID_KEYBOARD_F9},
+    {"F10", HID_KEYBOARD_F10},
+    {"F11", HID_KEYBOARD_F11},
+    {"F12", HID_KEYBOARD_F12},
+};
+
+uint16_t ducky_get_keycode_by_name(const char* param) {
+    for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
+        size_t key_cmd_len = strlen(ducky_keys[i].name);
+        if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
+           (ducky_is_line_end(param[key_cmd_len]))) {
+            return ducky_keys[i].keycode;
+        }
+    }
+
+    return HID_KEYBOARD_NONE;
+}

+ 0 - 327
applications/main/bad_usb/mnemonic.c

@@ -1,327 +0,0 @@
-#include <furi_hal.h>
-#include <furi_hal_usb_hid.h>
-#include "mnemonic.h"
-
-#define TAG "BadUSB"
-#define WORKER_TAG TAG "Worker"
-
-#define FILE_BUFFER_LEN 16
-#define SCRIPT_STATE_ERROR (-1)
-#define SCRIPT_STATE_END (-2)
-#define SCRIPT_STATE_NEXT_LINE (-3)
-
-#define BADUSB_ASCII_TO_KEY(script, x) \
-    (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
-
-static const uint8_t numpad_keys[10] = {
-    HID_KEYPAD_0,
-    HID_KEYPAD_1,
-    HID_KEYPAD_2,
-    HID_KEYPAD_3,
-    HID_KEYPAD_4,
-    HID_KEYPAD_5,
-    HID_KEYPAD_6,
-    HID_KEYPAD_7,
-    HID_KEYPAD_8,
-    HID_KEYPAD_9,
-};
-
-static bool ducky_get_number(const char* param, uint32_t* val) {
-    uint32_t value = 0;
-    if(sscanf(param, "%lu", &value) == 1) {
-        *val = value;
-        return true;
-    }
-    return false;
-}
-
-static void ducky_numlock_on() {
-    if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) {
-        furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
-        furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);
-    }
-}
-static bool ducky_numpad_press(const char num) {
-    if((num < '0') || (num > '9')) return false;
-
-    uint16_t key = numpad_keys[num - '0'];
-    furi_hal_hid_kb_press(key);
-    furi_hal_hid_kb_release(key);
-
-    return true;
-}
-
-static bool ducky_altchar(const char* charcode) {
-    uint8_t i = 0;
-    bool state = false;
-
-    FURI_LOG_I(WORKER_TAG, "char %s", charcode);
-
-    furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT);
-
-    while(!ducky_is_line_end(charcode[i])) {
-        state = ducky_numpad_press(charcode[i]);
-        if(state == false) break;
-        i++;
-    }
-
-    furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT);
-    return state;
-}
-
-static bool ducky_altstring(const char* param) {
-    uint32_t i = 0;
-    bool state = false;
-
-    while(param[i] != '\0') {
-        if((param[i] < ' ') || (param[i] > '~')) {
-            i++;
-            continue; // Skip non-printable chars
-        }
-
-        char temp_str[4];
-        snprintf(temp_str, 4, "%u", param[i]);
-
-        state = ducky_altchar(temp_str);
-        if(state == false) break;
-        i++;
-    }
-    return state;
-}
-
-static bool ducky_string(BadUsbScript* bad_usb, const char* param) {
-    uint32_t i = 0;
-
-    while(param[i] != '\0') {
-        uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]);
-        if(keycode != HID_KEYBOARD_NONE) {
-            furi_hal_hid_kb_press(keycode);
-            furi_hal_hid_kb_release(keycode);
-            if(bad_usb->stringdelay > 0) {
-                furi_delay_ms(bad_usb->stringdelay);
-            }
-        }
-        i++;
-    }
-    bad_usb->stringdelay = 0;
-    return true;
-}
-
-int32_t ducky_fnc_noop(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    (void)bad_usb;
-    (void)line;
-    (void)line_tmp;
-    (void)error;
-    (void)error_len;
-    return (0);
-}
-
-int32_t ducky_fnc_delay(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    bool state = false;
-    (void)bad_usb;
-    (void)line;
-
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    uint32_t delay_val = 0;
-    state = ducky_get_number(line_tmp, &delay_val);
-    if((state) && (delay_val > 0)) {
-        return (int32_t)delay_val;
-    }
-    if(error != NULL) {
-        snprintf(error, error_len, "Invalid number %s", line_tmp);
-    }
-    return SCRIPT_STATE_ERROR;
-}
-
-int32_t ducky_fnc_defdelay(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    bool state = false;
-    (void)line;
-
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    state = ducky_get_number(line_tmp, &bad_usb->defdelay);
-    if(!state && error != NULL) {
-        snprintf(error, error_len, "Invalid number %s", line_tmp);
-    }
-    return (state) ? (0) : SCRIPT_STATE_ERROR;
-}
-
-int32_t ducky_fnc_strdelay(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    bool state = false;
-    (void)line;
-
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    state = ducky_get_number(line_tmp, &bad_usb->stringdelay);
-    if((state) && (bad_usb->stringdelay > 0)) {
-        return state;
-    }
-    if(error != NULL) {
-        snprintf(error, error_len, "Invalid number %s", line_tmp);
-    }
-    return SCRIPT_STATE_ERROR;
-}
-
-int32_t ducky_fnc_string(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    bool state = false;
-    (void)line;
-
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    state = ducky_string(bad_usb, line_tmp);
-    if(!state && error != NULL) {
-        snprintf(error, error_len, "Invalid string %s", line_tmp);
-    }
-    return (state) ? (0) : SCRIPT_STATE_ERROR;
-}
-
-int32_t ducky_fnc_repeat(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    bool state = false;
-    (void)line;
-
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt);
-    if(!state && error != NULL) {
-        snprintf(error, error_len, "Invalid number %s", line_tmp);
-    }
-    return (state) ? (0) : SCRIPT_STATE_ERROR;
-}
-
-int32_t ducky_fnc_sysrq(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    (void)error;
-    (void)error_len;
-    (void)line;
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true);
-    furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
-    furi_hal_hid_kb_press(key);
-    furi_hal_hid_kb_release_all();
-    return (0);
-}
-
-int32_t ducky_fnc_altchar(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    bool state = false;
-    (void)bad_usb;
-    (void)line;
-
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    ducky_numlock_on();
-    state = ducky_altchar(line_tmp);
-    if(!state && error != NULL) {
-        snprintf(error, error_len, "Invalid altchar %s", line_tmp);
-    }
-    return (state) ? (0) : SCRIPT_STATE_ERROR;
-}
-
-int32_t ducky_fnc_altstring(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    bool state = false;
-    (void)bad_usb;
-    (void)line;
-
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    ducky_numlock_on();
-    state = ducky_altstring(line_tmp);
-    if(!state && error != NULL) {
-        snprintf(error, error_len, "Invalid altstring %s", line_tmp);
-    }
-    return (state) ? (0) : SCRIPT_STATE_ERROR;
-}
-
-int32_t ducky_fnc_stringln(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    bool state = false;
-    (void)line;
-
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    state = ducky_string(bad_usb, line_tmp);
-    if(!state && error != NULL) {
-        snprintf(error, error_len, "Invalid string %s", line_tmp);
-    }
-    furi_hal_hid_kb_press(HID_KEYBOARD_RETURN);
-    furi_hal_hid_kb_release(HID_KEYBOARD_RETURN);
-    return (state) ? (0) : SCRIPT_STATE_ERROR;
-}
-
-int32_t ducky_fnc_hold(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    (void)line;
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true);
-    if(key == HID_KEYBOARD_NONE) {
-        if(error != NULL) {
-            snprintf(error, error_len, "No keycode defined for %s", line_tmp);
-        }
-        return SCRIPT_STATE_ERROR;
-    }
-    furi_hal_hid_kb_press(key);
-    return (0);
-}
-
-int32_t ducky_fnc_release(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len) {
-    (void)line;
-    line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
-    uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true);
-    if(key == HID_KEYBOARD_NONE) {
-        if(error != NULL) {
-            snprintf(error, error_len, "No keycode defined for %s", line_tmp);
-        }
-        return SCRIPT_STATE_ERROR;
-    }
-    furi_hal_hid_kb_release(key);
-    return (0);
-}

+ 0 - 96
applications/main/bad_usb/mnemonic.h

@@ -1,96 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "bad_usb_script.h"
-
-// A no opperation function
-int32_t ducky_fnc_noop(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// DELAY
-int32_t ducky_fnc_delay(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// DEFAULTDELAY
-int32_t ducky_fnc_defdelay(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// STRINGDELAY
-int32_t ducky_fnc_strdelay(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// STRING
-int32_t ducky_fnc_string(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// STRINGLN
-int32_t ducky_fnc_stringln(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// REPEAT
-int32_t ducky_fnc_repeat(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// SYSRQ
-int32_t ducky_fnc_sysrq(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// ALTCHAR
-int32_t ducky_fnc_altchar(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// ALTSTRING
-int32_t ducky_fnc_altstring(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// HOLD
-int32_t ducky_fnc_hold(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-// RELEASE
-int32_t ducky_fnc_release(
-    BadUsbScript* bad_usb,
-    FuriString* line,
-    const char* line_tmp,
-    char* error,
-    size_t error_len);
-
-#ifdef __cplusplus
-}
-#endif

+ 1 - 1
applications/main/bad_usb/scenes/bad_usb_scene_config.c

@@ -17,7 +17,7 @@ void bad_usb_scene_config_on_enter(void* context) {
 
 
     submenu_add_item(
     submenu_add_item(
         submenu,
         submenu,
-        "Keyboard Layout",
+        "Keyboard Layout (global)",
         SubmenuIndexKeyboardLayout,
         SubmenuIndexKeyboardLayout,
         bad_usb_scene_config_submenu_callback,
         bad_usb_scene_config_submenu_callback,
         bad_usb);
         bad_usb);

+ 3 - 1
applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c

@@ -33,8 +33,10 @@ void bad_usb_scene_config_layout_on_enter(void* context) {
 
 
     if(bad_usb_layout_select(bad_usb)) {
     if(bad_usb_layout_select(bad_usb)) {
         bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout);
         bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout);
+        scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneWork);
+    } else {
+        scene_manager_previous_scene(bad_usb->scene_manager);
     }
     }
-    scene_manager_previous_scene(bad_usb->scene_manager);
 }
 }
 
 
 bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
 bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) {

+ 1 - 1
applications/main/bad_usb/scenes/bad_usb_scene_work.c

@@ -1,4 +1,4 @@
-#include "../bad_usb_script.h"
+#include "../helpers/ducky_script.h"
 #include "../bad_usb_app_i.h"
 #include "../bad_usb_app_i.h"
 #include "../views/bad_usb_view.h"
 #include "../views/bad_usb_view.h"
 #include <furi_hal.h>
 #include <furi_hal.h>

+ 7 - 2
applications/main/bad_usb/views/bad_usb_view.c

@@ -1,5 +1,5 @@
 #include "bad_usb_view.h"
 #include "bad_usb_view.h"
-#include "../bad_usb_script.h"
+#include "../helpers/ducky_script.h"
 #include <toolbox/path.h>
 #include <toolbox/path.h>
 #include <gui/elements.h>
 #include <gui/elements.h>
 #include <assets_icons.h>
 #include <assets_icons.h>
@@ -79,7 +79,12 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
         canvas_draw_str_aligned(
         canvas_draw_str_aligned(
             canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
             canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
         furi_string_reset(disp_str);
         furi_string_reset(disp_str);
-        canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error);
+
+        furi_string_set_str(disp_str, model->state.error);
+        elements_string_fit_width(canvas, disp_str, canvas_width(canvas));
+        canvas_draw_str_aligned(
+            canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
+        furi_string_reset(disp_str);
     } else if(model->state.state == BadUsbStateIdle) {
     } else if(model->state.state == BadUsbStateIdle) {
         canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
         canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
         canvas_set_font(canvas, FontBigNumbers);
         canvas_set_font(canvas, FontBigNumbers);

+ 1 - 1
applications/main/bad_usb/views/bad_usb_view.h

@@ -1,7 +1,7 @@
 #pragma once
 #pragma once
 
 
 #include <gui/view.h>
 #include <gui/view.h>
-#include "../bad_usb_script.h"
+#include "../helpers/ducky_script.h"
 
 
 typedef struct BadUsb BadUsb;
 typedef struct BadUsb BadUsb;
 typedef void (*BadUsbButtonCallback)(InputKey key, void* context);
 typedef void (*BadUsbButtonCallback)(InputKey key, void* context);

+ 15 - 4
applications/main/fap_loader/fap_loader_app.c

@@ -1,16 +1,17 @@
+#include "fap_loader_app.h"
+
 #include <furi.h>
 #include <furi.h>
-#include <gui/gui.h>
+
 #include <assets_icons.h>
 #include <assets_icons.h>
+#include <gui/gui.h>
 #include <gui/view_dispatcher.h>
 #include <gui/view_dispatcher.h>
-#include <storage/storage.h>
 #include <gui/modules/loading.h>
 #include <gui/modules/loading.h>
 #include <dialogs/dialogs.h>
 #include <dialogs/dialogs.h>
 #include <toolbox/path.h>
 #include <toolbox/path.h>
 #include <flipper_application/flipper_application.h>
 #include <flipper_application/flipper_application.h>
 #include <loader/firmware_api/firmware_api.h>
 #include <loader/firmware_api/firmware_api.h>
-#include "fap_loader_app.h"
 
 
-#define TAG "fap_loader_app"
+#define TAG "FapLoader"
 
 
 struct FapLoader {
 struct FapLoader {
     FlipperApplication* app;
     FlipperApplication* app;
@@ -22,6 +23,8 @@ struct FapLoader {
     Loading* loading;
     Loading* loading;
 };
 };
 
 
+volatile bool fap_loader_debug_active = false;
+
 bool fap_loader_load_name_and_icon(
 bool fap_loader_load_name_and_icon(
     FuriString* path,
     FuriString* path,
     Storage* storage,
     Storage* storage,
@@ -107,6 +110,14 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
 
 
         FuriThread* thread = flipper_application_spawn(loader->app, NULL);
         FuriThread* thread = flipper_application_spawn(loader->app, NULL);
 
 
+        /* This flag is set by the debugger - to break on app start */
+        if(fap_loader_debug_active) {
+            FURI_LOG_W(TAG, "Triggering BP for debugger");
+            /* After hitting this, you can set breakpoints in your .fap's code
+             * Note that you have to toggle breakpoints that were set before */
+            __asm volatile("bkpt 0");
+        }
+
         FuriString* app_name = furi_string_alloc();
         FuriString* app_name = furi_string_alloc();
         path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name);
         path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name);
         furi_thread_set_appid(thread, furi_string_get_cstr(app_name));
         furi_thread_set_appid(thread, furi_string_get_cstr(app_name));

+ 3 - 61
applications/main/ibutton/ibutton_cli.c

@@ -4,25 +4,20 @@
 #include <cli/cli.h>
 #include <cli/cli.h>
 #include <toolbox/args.h>
 #include <toolbox/args.h>
 
 
-#include <one_wire/one_wire_host.h>
-
-#include <one_wire/ibutton/ibutton_key.h>
-#include <one_wire/ibutton/ibutton_worker.h>
-#include <one_wire/ibutton/ibutton_protocols.h>
+#include <ibutton/ibutton_key.h>
+#include <ibutton/ibutton_worker.h>
+#include <ibutton/ibutton_protocols.h>
 
 
 static void ibutton_cli(Cli* cli, FuriString* args, void* context);
 static void ibutton_cli(Cli* cli, FuriString* args, void* context);
-static void onewire_cli(Cli* cli, FuriString* args, void* context);
 
 
 // app cli function
 // app cli function
 void ibutton_on_system_start() {
 void ibutton_on_system_start() {
 #ifdef SRV_CLI
 #ifdef SRV_CLI
     Cli* cli = furi_record_open(RECORD_CLI);
     Cli* cli = furi_record_open(RECORD_CLI);
     cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli);
     cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli);
-    cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli);
     furi_record_close(RECORD_CLI);
     furi_record_close(RECORD_CLI);
 #else
 #else
     UNUSED(ibutton_cli);
     UNUSED(ibutton_cli);
-    UNUSED(onewire_cli);
 #endif
 #endif
 }
 }
 
 
@@ -257,56 +252,3 @@ void ibutton_cli(Cli* cli, FuriString* args, void* context) {
 
 
     furi_string_free(cmd);
     furi_string_free(cmd);
 }
 }
-
-static void onewire_cli_print_usage() {
-    printf("Usage:\r\n");
-    printf("onewire search\r\n");
-};
-
-static void onewire_cli_search(Cli* cli) {
-    UNUSED(cli);
-    OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio);
-    uint8_t address[8];
-    bool done = false;
-
-    printf("Search started\r\n");
-
-    onewire_host_start(onewire);
-    furi_hal_power_enable_otg();
-
-    while(!done) {
-        if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {
-            printf("Search finished\r\n");
-            onewire_host_reset_search(onewire);
-            done = true;
-        } else {
-            printf("Found: ");
-            for(uint8_t i = 0; i < 8; i++) {
-                printf("%02X", address[i]);
-            }
-            printf("\r\n");
-        }
-        furi_delay_ms(100);
-    }
-
-    furi_hal_power_disable_otg();
-    onewire_host_free(onewire);
-}
-
-void onewire_cli(Cli* cli, FuriString* args, void* context) {
-    UNUSED(context);
-    FuriString* cmd;
-    cmd = furi_string_alloc();
-
-    if(!args_read_string_and_trim(args, cmd)) {
-        furi_string_free(cmd);
-        onewire_cli_print_usage();
-        return;
-    }
-
-    if(furi_string_cmp_str(cmd, "search") == 0) {
-        onewire_cli_search(cli);
-    }
-
-    furi_string_free(cmd);
-}

+ 2 - 2
applications/main/ibutton/ibutton_i.h

@@ -7,8 +7,8 @@
 #include <gui/scene_manager.h>
 #include <gui/scene_manager.h>
 #include <gui/view_dispatcher.h>
 #include <gui/view_dispatcher.h>
 
 
-#include <one_wire/ibutton/ibutton_worker.h>
-#include <one_wire/ibutton/ibutton_protocols.h>
+#include <ibutton/ibutton_worker.h>
+#include <ibutton/ibutton_protocols.h>
 
 
 #include <rpc/rpc_app.h>
 #include <rpc/rpc_app.h>
 #include <storage/storage.h>
 #include <storage/storage.h>

+ 2 - 1
applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c

@@ -115,7 +115,8 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
                 consumed = true;
                 consumed = true;
             }
             }
         } else if(event.event == NfcWorkerEventAborted) {
         } else if(event.event == NfcWorkerEventAborted) {
-            if(state == DictAttackStateUserDictInProgress) {
+            if(state == DictAttackStateUserDictInProgress &&
+               dict_attack_get_card_state(nfc->dict_attack)) {
                 nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state);
                 nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state);
                 consumed = true;
                 consumed = true;
             } else {
             } else {

+ 8 - 0
applications/main/nfc/views/dict_attack.c

@@ -11,6 +11,7 @@ struct DictAttack {
     View* view;
     View* view;
     DictAttackCallback callback;
     DictAttackCallback callback;
     void* context;
     void* context;
+    bool card_present;
 };
 };
 
 
 typedef struct {
 typedef struct {
@@ -162,6 +163,7 @@ void dict_attack_set_header(DictAttack* dict_attack, const char* header) {
 
 
 void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) {
 void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) {
     furi_assert(dict_attack);
     furi_assert(dict_attack);
+    dict_attack->card_present = true;
     with_view_model(
     with_view_model(
         dict_attack->view,
         dict_attack->view,
         DictAttackViewModel * model,
         DictAttackViewModel * model,
@@ -175,6 +177,7 @@ void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type)
 
 
 void dict_attack_set_card_removed(DictAttack* dict_attack) {
 void dict_attack_set_card_removed(DictAttack* dict_attack) {
     furi_assert(dict_attack);
     furi_assert(dict_attack);
+    dict_attack->card_present = false;
     with_view_model(
     with_view_model(
         dict_attack->view,
         dict_attack->view,
         DictAttackViewModel * model,
         DictAttackViewModel * model,
@@ -182,6 +185,11 @@ void dict_attack_set_card_removed(DictAttack* dict_attack) {
         true);
         true);
 }
 }
 
 
+bool dict_attack_get_card_state(DictAttack* dict_attack) {
+    furi_assert(dict_attack);
+    return dict_attack->card_present;
+}
+
 void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) {
 void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) {
     furi_assert(dict_attack);
     furi_assert(dict_attack);
     with_view_model(
     with_view_model(

+ 2 - 0
applications/main/nfc/views/dict_attack.h

@@ -25,6 +25,8 @@ void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type);
 
 
 void dict_attack_set_card_removed(DictAttack* dict_attack);
 void dict_attack_set_card_removed(DictAttack* dict_attack);
 
 
+bool dict_attack_get_card_state(DictAttack* dict_attack);
+
 void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read);
 void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read);
 
 
 void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found);
 void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found);

+ 14 - 0
applications/main/onewire/application.fam

@@ -0,0 +1,14 @@
+App(
+    appid="onewire",
+    name="1-Wire",
+    apptype=FlipperAppType.METAPACKAGE,
+    provides=["onewire_start"],
+)
+
+App(
+    appid="onewire_start",
+    apptype=FlipperAppType.STARTUP,
+    entry_point="onewire_on_system_start",
+    requires=["onewire"],
+    order=60,
+)

+ 72 - 0
applications/main/onewire/onewire_cli.c

@@ -0,0 +1,72 @@
+#include <furi.h>
+#include <furi_hal.h>
+
+#include <cli/cli.h>
+#include <toolbox/args.h>
+
+#include <one_wire/one_wire_host.h>
+
+static void onewire_cli(Cli* cli, FuriString* args, void* context);
+
+void onewire_on_system_start() {
+#ifdef SRV_CLI
+    Cli* cli = furi_record_open(RECORD_CLI);
+    cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli);
+    furi_record_close(RECORD_CLI);
+#else
+    UNUSED(onewire_cli);
+#endif
+}
+
+static void onewire_cli_print_usage() {
+    printf("Usage:\r\n");
+    printf("onewire search\r\n");
+};
+
+static void onewire_cli_search(Cli* cli) {
+    UNUSED(cli);
+    OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio);
+    uint8_t address[8];
+    bool done = false;
+
+    printf("Search started\r\n");
+
+    onewire_host_start(onewire);
+    furi_hal_power_enable_otg();
+
+    while(!done) {
+        if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {
+            printf("Search finished\r\n");
+            onewire_host_reset_search(onewire);
+            done = true;
+        } else {
+            printf("Found: ");
+            for(uint8_t i = 0; i < 8; i++) {
+                printf("%02X", address[i]);
+            }
+            printf("\r\n");
+        }
+        furi_delay_ms(100);
+    }
+
+    furi_hal_power_disable_otg();
+    onewire_host_free(onewire);
+}
+
+void onewire_cli(Cli* cli, FuriString* args, void* context) {
+    UNUSED(context);
+    FuriString* cmd;
+    cmd = furi_string_alloc();
+
+    if(!args_read_string_and_trim(args, cmd)) {
+        furi_string_free(cmd);
+        onewire_cli_print_usage();
+        return;
+    }
+
+    if(furi_string_cmp_str(cmd, "search") == 0) {
+        onewire_cli_search(cli);
+    }
+
+    furi_string_free(cmd);
+}

+ 0 - 0
lib/toolbox/hmac_sha256.c → applications/main/u2f/hmac_sha256.c


+ 0 - 0
lib/toolbox/hmac_sha256.h → applications/main/u2f/hmac_sha256.h


+ 1 - 1
applications/main/u2f/u2f.c

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

+ 6 - 3
applications/services/gui/canvas.c

@@ -17,6 +17,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);
@@ -35,6 +36,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);
 }
 }
 
 
@@ -218,7 +220,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);
 }
 }
 
 
@@ -233,7 +235,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,
@@ -250,7 +253,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);
 }
 }
 
 

+ 2 - 0
applications/services/gui/canvas_i.h

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

+ 1 - 0
applications/services/gui/gui.c

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

+ 5 - 1
applications/services/gui/gui.h

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

+ 1 - 1
applications/services/gui/modules/submenu.c

@@ -98,7 +98,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
 
 
             FuriString* disp_str;
             FuriString* disp_str;
             disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label);
             disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label);
-            elements_string_fit_width(canvas, disp_str, item_width - 20);
+            elements_string_fit_width(canvas, disp_str, item_width - (6 * 2));
 
 
             canvas_draw_str(
             canvas_draw_str(
                 canvas,
                 canvas,

+ 24 - 2
applications/services/rpc/rpc_gui.c

@@ -33,8 +33,18 @@ typedef struct {
     uint32_t input_counter;
     uint32_t input_counter;
 } RpcGuiSystem;
 } RpcGuiSystem;
 
 
-static void
-    rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) {
+static const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = {
+    [CanvasOrientationHorizontal] = PB_Gui_ScreenOrientation_HORIZONTAL,
+    [CanvasOrientationHorizontalFlip] = PB_Gui_ScreenOrientation_HORIZONTAL_FLIP,
+    [CanvasOrientationVertical] = PB_Gui_ScreenOrientation_VERTICAL,
+    [CanvasOrientationVerticalFlip] = PB_Gui_ScreenOrientation_VERTICAL_FLIP,
+};
+
+static void rpc_system_gui_screen_stream_frame_callback(
+    uint8_t* data,
+    size_t size,
+    CanvasOrientation orientation,
+    void* context) {
     furi_assert(data);
     furi_assert(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;
         }
         }

+ 19 - 42
applications/services/storage/storage_glue.c

@@ -73,29 +73,34 @@ uint32_t storage_data_get_timestamp(StorageData* storage) {
 
 
 /****************** storage glue ******************/
 /****************** storage glue ******************/
 
 
-bool storage_has_file(const File* file, StorageData* storage_data) {
-    bool result = false;
+static StorageFile* storage_get_file(const File* file, StorageData* storage) {
+    StorageFile* storage_file_ref = NULL;
 
 
     StorageFileList_it_t it;
     StorageFileList_it_t it;
-    for(StorageFileList_it(it, storage_data->files); !StorageFileList_end_p(it);
+    for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it);
         StorageFileList_next(it)) {
         StorageFileList_next(it)) {
-        const StorageFile* storage_file = StorageFileList_cref(it);
+        StorageFile* storage_file = StorageFileList_ref(it);
 
 
         if(storage_file->file->file_id == file->file_id) {
         if(storage_file->file->file_id == file->file_id) {
-            result = true;
+            storage_file_ref = storage_file;
             break;
             break;
         }
         }
     }
     }
 
 
-    return result;
+    return storage_file_ref;
+}
+
+bool storage_has_file(const File* file, StorageData* storage) {
+    return storage_get_file(file, storage) != NULL;
 }
 }
 
 
-bool storage_path_already_open(FuriString* path, StorageFileList_t array) {
+bool storage_path_already_open(FuriString* path, StorageData* storage) {
     bool open = false;
     bool open = false;
 
 
     StorageFileList_it_t it;
     StorageFileList_it_t it;
 
 
-    for(StorageFileList_it(it, array); !StorageFileList_end_p(it); StorageFileList_next(it)) {
+    for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it);
+        StorageFileList_next(it)) {
         const StorageFile* storage_file = StorageFileList_cref(it);
         const StorageFile* storage_file = StorageFileList_cref(it);
 
 
         if(furi_string_cmp(storage_file->path, path) == 0) {
         if(furi_string_cmp(storage_file->path, path) == 0) {
@@ -108,43 +113,15 @@ bool storage_path_already_open(FuriString* path, StorageFileList_t array) {
 }
 }
 
 
 void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage) {
 void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage) {
-    StorageFile* founded_file = NULL;
-
-    StorageFileList_it_t it;
-
-    for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it);
-        StorageFileList_next(it)) {
-        StorageFile* storage_file = StorageFileList_ref(it);
-
-        if(storage_file->file->file_id == file->file_id) {
-            founded_file = storage_file;
-            break;
-        }
-    }
-
-    furi_check(founded_file != NULL);
-
-    founded_file->file_data = file_data;
+    StorageFile* storage_file_ref = storage_get_file(file, storage);
+    furi_check(storage_file_ref != NULL);
+    storage_file_ref->file_data = file_data;
 }
 }
 
 
 void* storage_get_storage_file_data(const File* file, StorageData* storage) {
 void* storage_get_storage_file_data(const File* file, StorageData* storage) {
-    const StorageFile* founded_file = NULL;
-
-    StorageFileList_it_t it;
-
-    for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it);
-        StorageFileList_next(it)) {
-        const StorageFile* storage_file = StorageFileList_cref(it);
-
-        if(storage_file->file->file_id == file->file_id) {
-            founded_file = storage_file;
-            break;
-        }
-    }
-
-    furi_check(founded_file != NULL);
-
-    return founded_file->file_data;
+    StorageFile* storage_file_ref = storage_get_file(file, storage);
+    furi_check(storage_file_ref != NULL);
+    return storage_file_ref->file_data;
 }
 }
 
 
 void storage_push_storage_file(File* file, FuriString* path, StorageData* storage) {
 void storage_push_storage_file(File* file, FuriString* path, StorageData* storage) {

+ 1 - 1
applications/services/storage/storage_glue.h

@@ -60,7 +60,7 @@ struct StorageData {
 };
 };
 
 
 bool storage_has_file(const File* file, StorageData* storage_data);
 bool storage_has_file(const File* file, StorageData* storage_data);
-bool storage_path_already_open(FuriString* path, StorageFileList_t files);
+bool storage_path_already_open(FuriString* path, StorageData* storage_data);
 
 
 void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage);
 void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage);
 void* storage_get_storage_file_data(const File* file, StorageData* storage);
 void* storage_get_storage_file_data(const File* file, StorageData* storage);

+ 22 - 6
applications/services/storage/storage_processing.c

@@ -2,6 +2,17 @@
 #include <m-list.h>
 #include <m-list.h>
 #include <m-dict.h>
 #include <m-dict.h>
 
 
+#define STORAGE_PATH_PREFIX_LEN 4u
+_Static_assert(
+    sizeof(STORAGE_ANY_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1,
+    "Any path prefix len mismatch");
+_Static_assert(
+    sizeof(STORAGE_EXT_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1,
+    "Ext path prefix len mismatch");
+_Static_assert(
+    sizeof(STORAGE_INT_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1,
+    "Int path prefix len mismatch");
+
 #define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn;
 #define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn;
 
 
 static bool storage_type_is_valid(StorageType type) {
 static bool storage_type_is_valid(StorageType type) {
@@ -26,13 +37,19 @@ static StorageData* get_storage_by_file(File* file, StorageData* storages) {
 
 
 static const char* cstr_path_without_vfs_prefix(FuriString* path) {
 static const char* cstr_path_without_vfs_prefix(FuriString* path) {
     const char* path_cstr = furi_string_get_cstr(path);
     const char* path_cstr = furi_string_get_cstr(path);
-    return path_cstr + MIN(4u, strlen(path_cstr));
+    return path_cstr + MIN(STORAGE_PATH_PREFIX_LEN, strlen(path_cstr));
 }
 }
 
 
 static StorageType storage_get_type_by_path(FuriString* path) {
 static StorageType storage_get_type_by_path(FuriString* path) {
     StorageType type = ST_ERROR;
     StorageType type = ST_ERROR;
     const char* path_cstr = furi_string_get_cstr(path);
     const char* path_cstr = furi_string_get_cstr(path);
 
 
+    if(furi_string_size(path) > STORAGE_PATH_PREFIX_LEN) {
+        if(path_cstr[STORAGE_PATH_PREFIX_LEN] != '/') {
+            return ST_ERROR;
+        }
+    }
+
     if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
     if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
         type = ST_EXT;
         type = ST_EXT;
     } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
     } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
@@ -43,7 +60,6 @@ static StorageType storage_get_type_by_path(FuriString* path) {
 
 
     return type;
     return type;
 }
 }
-
 static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) {
 static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) {
     if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) {
     if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) {
         switch(real_storage) {
         switch(real_storage) {
@@ -61,7 +77,7 @@ static void storage_path_change_to_real_storage(FuriString* path, StorageType re
     }
     }
 }
 }
 
 
-FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) {
+static FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) {
     StorageType type = storage_get_type_by_path(path);
     StorageType type = storage_get_type_by_path(path);
 
 
     if(storage_type_is_valid(type)) {
     if(storage_type_is_valid(type)) {
@@ -95,7 +111,7 @@ bool storage_process_file_open(
     file->error_id = storage_get_data(app, path, &storage);
     file->error_id = storage_get_data(app, path, &storage);
 
 
     if(file->error_id == FSE_OK) {
     if(file->error_id == FSE_OK) {
-        if(storage_path_already_open(path, storage->files)) {
+        if(storage_path_already_open(path, storage)) {
             file->error_id = FSE_ALREADY_OPEN;
             file->error_id = FSE_ALREADY_OPEN;
         } else {
         } else {
             if(access_mode & FSAM_WRITE) {
             if(access_mode & FSAM_WRITE) {
@@ -252,7 +268,7 @@ bool storage_process_dir_open(Storage* app, File* file, FuriString* path) {
     file->error_id = storage_get_data(app, path, &storage);
     file->error_id = storage_get_data(app, path, &storage);
 
 
     if(file->error_id == FSE_OK) {
     if(file->error_id == FSE_OK) {
-        if(storage_path_already_open(path, storage->files)) {
+        if(storage_path_already_open(path, storage)) {
             file->error_id = FSE_ALREADY_OPEN;
             file->error_id = FSE_ALREADY_OPEN;
         } else {
         } else {
             storage_push_storage_file(file, path, storage);
             storage_push_storage_file(file, path, storage);
@@ -341,7 +357,7 @@ static FS_Error storage_process_common_remove(Storage* app, FuriString* path) {
     FS_Error ret = storage_get_data(app, path, &storage);
     FS_Error ret = storage_get_data(app, path, &storage);
 
 
     do {
     do {
-        if(storage_path_already_open(path, storage->files)) {
+        if(storage_path_already_open(path, storage)) {
             ret = FSE_ALREADY_OPEN;
             ret = FSE_ALREADY_OPEN;
             break;
             break;
         }
         }

+ 3 - 1
applications/services/storage/storages/storage_ext.c

@@ -618,8 +618,10 @@ static const FS_Api fs_api = {
 };
 };
 
 
 void storage_ext_init(StorageData* storage) {
 void storage_ext_init(StorageData* storage) {
+    fatfs_init();
+
     SDData* sd_data = malloc(sizeof(SDData));
     SDData* sd_data = malloc(sizeof(SDData));
-    sd_data->fs = &USERFatFS;
+    sd_data->fs = &fatfs_object;
     sd_data->path = "0:/";
     sd_data->path = "0:/";
     sd_data->sd_was_present = true;
     sd_data->sd_was_present = true;
 
 

+ 1 - 1
assets/protobuf

@@ -1 +1 @@
-Subproject commit 6460660237005d02d5c223835659b40e373bade9
+Subproject commit 1f6b4a08c5d05c2b17926a3ba79f60109638932f

+ 69 - 46
debug/flipperapps.py

@@ -2,7 +2,6 @@ from dataclasses import dataclass
 from typing import Optional, Tuple, Dict, ClassVar
 from typing import Optional, Tuple, Dict, ClassVar
 import struct
 import struct
 import posixpath
 import posixpath
-import os
 import zlib
 import zlib
 
 
 import gdb
 import gdb
@@ -66,9 +65,9 @@ class AppState:
     def get_gdb_unload_command(self) -> str:
     def get_gdb_unload_command(self) -> str:
         return f"remove-symbol-file -a 0x{self.text_address:08x}"
         return f"remove-symbol-file -a 0x{self.text_address:08x}"
 
 
-    def is_loaded_in_gdb(self, gdb_app) -> bool:
-        # Avoid constructing full app wrapper for comparison
-        return self.entry_address == int(gdb_app["state"]["entry"])
+    @staticmethod
+    def get_gdb_app_ep(app) -> int:
+        return int(app["state"]["entry"])
 
 
     @staticmethod
     @staticmethod
     def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]:
     def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]:
@@ -79,10 +78,10 @@ class AppState:
         crc32 = struct.unpack("<I", section_data[-4:])[0]
         crc32 = struct.unpack("<I", section_data[-4:])[0]
         return (elf_name, crc32)
         return (elf_name, crc32)
 
 
-    @staticmethod
-    def from_gdb(gdb_app: "AppState") -> "AppState":
+    @classmethod
+    def from_gdb(cls, gdb_app: "AppState") -> "AppState":
         state = AppState(str(gdb_app["manifest"]["name"].string()))
         state = AppState(str(gdb_app["manifest"]["name"].string()))
-        state.entry_address = int(gdb_app["state"]["entry"])
+        state.entry_address = cls.get_gdb_app_ep(gdb_app)
 
 
         app_state = gdb_app["state"]
         app_state = gdb_app["state"]
         if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]):
         if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]):
@@ -123,59 +122,83 @@ class SetFapDebugElfRoot(gdb.Command):
         try:
         try:
             global helper
             global helper
             print(f"Set '{arg}' as debug info lookup path for Flipper external apps")
             print(f"Set '{arg}' as debug info lookup path for Flipper external apps")
-            helper.attach_fw()
+            helper.attach_to_fw()
             gdb.events.stop.connect(helper.handle_stop)
             gdb.events.stop.connect(helper.handle_stop)
+            gdb.events.exited.connect(helper.handle_exit)
         except gdb.error as e:
         except gdb.error as e:
             print(f"Support for Flipper external apps debug is not available: {e}")
             print(f"Support for Flipper external apps debug is not available: {e}")
 
 
 
 
-SetFapDebugElfRoot()
-
-
-class FlipperAppDebugHelper:
+class FlipperAppStateHelper:
     def __init__(self):
     def __init__(self):
-        self.app_ptr = None
         self.app_type_ptr = None
         self.app_type_ptr = None
-        self.current_app: AppState = None
+        self.app_list_ptr = None
+        self.app_list_entry_type = None
+        self._current_apps: list[AppState] = []
 
 
-    def attach_fw(self) -> None:
-        self.app_ptr = gdb.lookup_global_symbol("last_loaded_app")
-        self.app_type_ptr = gdb.lookup_type("FlipperApplication").pointer()
-        self._check_app_state()
-
-    def _check_app_state(self) -> None:
-        app_ptr_value = self.app_ptr.value()
-        if not app_ptr_value and self.current_app:
-            # There is an ELF loaded in GDB, but nothing is running on the device
-            self._unload_debug_elf()
-        elif app_ptr_value:
-            # There is an app running on the device
-            loaded_app = app_ptr_value.cast(self.app_type_ptr).dereference()
-
-            if self.current_app and not self.current_app.is_loaded_in_gdb(loaded_app):
-                # Currently loaded ELF is not the one running on the device
-                self._unload_debug_elf()
-
-            if not self.current_app:
-                # Load ELF for the app running on the device
-                self._load_debug_elf(loaded_app)
-
-    def _unload_debug_elf(self) -> None:
+    def _walk_app_list(self, list_head):
+        while list_head:
+            if app := list_head["data"]:
+                yield app.dereference()
+            list_head = list_head["next"]
+
+    def _exec_gdb_command(self, command: str) -> bool:
         try:
         try:
-            gdb.execute(self.current_app.get_gdb_unload_command())
+            gdb.execute(command)
+            return True
         except gdb.error as e:
         except gdb.error as e:
-            print(f"Failed to unload debug ELF: {e} (might not be an error)")
-        self.current_app = None
+            print(f"Failed to execute GDB command '{command}': {e}")
+            return False
 
 
-    def _load_debug_elf(self, app_object) -> None:
-        self.current_app = AppState.from_gdb(app_object)
+    def _sync_apps(self) -> None:
+        self.set_debug_mode(True)
+        if not (app_list := self.app_list_ptr.value()):
+            print("Reset app loader state")
+            for app in self._current_apps:
+                self._exec_gdb_command(app.get_gdb_unload_command())
+            self._current_apps = []
+            return
+
+        loaded_apps: dict[int, gdb.Value] = dict(
+            (AppState.get_gdb_app_ep(app), app)
+            for app in self._walk_app_list(app_list[0])
+        )
 
 
-        if self.current_app.is_debug_available():
-            gdb.execute(self.current_app.get_gdb_load_command())
+        for app in self._current_apps.copy():
+            if app.entry_address not in loaded_apps:
+                print(f"Application {app.name} is no longer loaded")
+                if not self._exec_gdb_command(app.get_gdb_unload_command()):
+                    print(f"Failed to unload debug info for {app.name}")
+                self._current_apps.remove(app)
+
+        for entry_point, app in loaded_apps.items():
+            if entry_point not in set(app.entry_address for app in self._current_apps):
+                new_app_state = AppState.from_gdb(app)
+                print(f"New application loaded. Adding debug info")
+                if self._exec_gdb_command(new_app_state.get_gdb_load_command()):
+                    self._current_apps.append(new_app_state)
+                else:
+                    print(f"Failed to load debug info for {new_app_state}")
+
+    def attach_to_fw(self) -> None:
+        print("Attaching to Flipper firmware")
+        self.app_list_ptr = gdb.lookup_global_symbol(
+            "flipper_application_loaded_app_list"
+        )
+        self.app_type_ptr = gdb.lookup_type("FlipperApplication").pointer()
+        self.app_list_entry_type = gdb.lookup_type("struct FlipperApplicationList_s")
 
 
     def handle_stop(self, event) -> None:
     def handle_stop(self, event) -> None:
-        self._check_app_state()
+        self._sync_apps()
 
 
+    def handle_exit(self, event) -> None:
+        self.set_debug_mode(False)
 
 
-helper = FlipperAppDebugHelper()
+    def set_debug_mode(self, mode: bool) -> None:
+        gdb.execute(f"set variable fap_loader_debug_active = {int(mode)}")
+
+
+# Init additional 'fap-set-debug-elf-root' command and set up hooks
+SetFapDebugElfRoot()
+helper = FlipperAppStateHelper()
 print("Support for Flipper external apps debug is loaded")
 print("Support for Flipper external apps debug is loaded")

+ 3 - 1
documentation/AppsOnSDCard.md

@@ -13,7 +13,7 @@ FAPs are created and developed the same way as internal applications that are pa
 To build your application as a FAP, create a folder with your app's source code in `applications_user`, then write its code the way you'd do when creating a regular built-in application. Then configure its `application.fam` manifest, and set its _apptype_ to FlipperAppType.EXTERNAL. See [Application Manifests](./AppManifests.md#application-definition) for more details.
 To build your application 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
@@ -32,6 +32,8 @@ Images and animated icons should follow the same [naming convention](../assets/R
 
 
 With it, you can debug FAPs as if they were a part of the main firmware — inspect variables, set breakpoints, step through the code, etc.
 With it, you can debug FAPs as if they were a part of the main firmware — inspect variables, set breakpoints, step through the code, etc.
 
 
+If debugging session is active, firmware will trigger a breakpoint after loading a FAP it into memory, but before running any code from it. This allows you to set breakpoints in the FAP's code. Note that any breakpoints set before the FAP is loaded may need re-setting after the FAP is actually loaded, since before loading it debugger cannot know the exact address of the FAP's code.
+
 ### Setting up debugging environment
 ### Setting up debugging environment
 
 
 The debugging support script looks up debugging information in the latest firmware build directory (`build/latest`). That directory is symlinked by `fbt` to the latest firmware configuration (Debug or Release) build directory when you run `./fbt` for the chosen configuration. See [fbt docs](./fbt.md#nb) for details.
 The debugging support script looks up debugging information in the latest firmware build directory (`build/latest`). That directory is symlinked by `fbt` to the latest firmware configuration (Debug or Release) build directory when you run `./fbt` for the chosen configuration. See [fbt docs](./fbt.md#nb) for details.

+ 6 - 6
documentation/UnitTests.md

@@ -17,11 +17,11 @@ To run the unit tests, follow these steps:
 
 
 1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests`.
 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).
-4. Update the [assets](assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass.
+3. Add the test code to [infrared_test.c](/applications/debug/unit_tests/infrared/infrared_test.c).
+4. Update the [assets](/assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass.
 
 
 ##### Test data format
 ##### Test data format
 
 

+ 46 - 36
documentation/file_formats/BadUsbScriptFormat.md

@@ -11,18 +11,18 @@ BadUsb app can execute only text scrips from `.txt` files, no compilation is req
 ## Comment line
 ## Comment line
 
 
 Just a single comment line. The interpreter will ignore all text after the REM command.
 Just a single comment line. The interpreter will ignore all text after the REM command.
-|Command|Parameters|Notes|
-|-|-|-|
-|REM|Comment text||
+| Command | Parameters   | Notes |
+| ------- | ------------ | ----- |
+| REM     | Comment text |       |
 
 
 ## Delay
 ## Delay
 
 
 Pause script execution by a defined time.
 Pause script execution by a defined time.
-|Command|Parameters|Notes|
-|-|-|-|
-|DELAY|Delay value in ms|Single delay|
-|DEFAULT_DELAY|Delay value in ms|Add delay before every next command|
-|DEFAULTDELAY|Delay value in ms|Same as DEFAULT_DELAY|
+| Command       | Parameters        | Notes                               |
+| ------------- | ----------------- | ----------------------------------- |
+| DELAY         | Delay value in ms | Single delay                        |
+| DEFAULT_DELAY | Delay value in ms | Add delay before every next command |
+| DEFAULTDELAY  | Delay value in ms | Same as DEFAULT_DELAY               |
 
 
 ## Special keys
 ## Special keys
 
 
@@ -56,32 +56,42 @@ Pause script execution by a defined time.
 ## Modifier keys
 ## Modifier keys
 
 
 Can be combined with a special key command or a single character.
 Can be combined with a special key command or a single character.
-|Command|Notes|
-|-|-|
-|CONTROL / CTRL||
-|SHIFT||
-|ALT||
-|WINDOWS / GUI||
-|CTRL-ALT|CTRL+ALT|
-|CTRL-SHIFT|CTRL+SHIFT|
-|ALT-SHIFT|ALT+SHIFT|
-|ALT-GUI|ALT+WIN|
-|GUI-SHIFT|WIN+SHIFT|
-|GUI-CTRL|WIN+CTRL|
+| Command        | Notes      |
+| -------------- | ---------- |
+| CONTROL / CTRL |            |
+| SHIFT          |            |
+| ALT            |            |
+| WINDOWS / GUI  |            |
+| CTRL-ALT       | CTRL+ALT   |
+| CTRL-SHIFT     | CTRL+SHIFT |
+| ALT-SHIFT      | ALT+SHIFT  |
+| ALT-GUI        | ALT+WIN    |
+| GUI-SHIFT      | WIN+SHIFT  |
+| GUI-CTRL       | WIN+CTRL   |
+
+## Key hold and release
+
+Up to 5 keys can be hold simultaneously.
+| Command | Parameters                      | Notes                                     |
+| ------- | ------------------------------- | ----------------------------------------- |
+| HOLD    | Special key or single character | Press and hold key untill RELEASE command |
+| RELEASE | Special key or single character | Release key                               |
+
 
 
 ## String
 ## String
 
 
-| Command | Parameters  | Notes             |
-| ------- | ----------- | ----------------- |
-| STRING  | Text string | Print text string |
+| Command  | Parameters  | Notes                                      |
+| -------  | ----------- | -----------------                          |
+| STRING   | Text string | Print text string                          |
+| STRINGLN | Text string | Print text string and press enter after it |
 
 
 ## String delay
 ## String delay
 
 
 Delay between keypresses.
 Delay between keypresses.
-|Command|Parameters|Notes|
-|-|-|-|
-|STRING_DELAY|Delay value in ms|Applied once to next appearing string|
-|STRINGDELAY|Delay value in ms|Same as STRING_DELAY|
+| Command      | Parameters        | Notes                                         |
+| ------------ | ----------------- | --------------------------------------------- |
+| STRING_DELAY | Delay value in ms | Applied once to next appearing STRING command |
+| STRINGDELAY  | Delay value in ms | Same as STRING_DELAY                          |
 
 
 ## Repeat
 ## Repeat
 
 
@@ -91,19 +101,19 @@ Delay between keypresses.
 
 
 ## ALT+Numpad input
 ## ALT+Numpad input
 
 
-On Windows and some Linux systems, you can print characters by pressing `ALT` key and entering its code on Numpad.
-|Command|Parameters|Notes|
-|-|-|-|
-|ALTCHAR|Character code|Print single character|
-|ALTSTRING|Text string|Print text string using ALT+Numpad method|
-|ALTCODE|Text string|Same as ALTSTRING, presents in some Duckyscript implementations|
+On Windows and some Linux systems, you can print characters by holding `ALT` key and entering its code on Numpad.
+| Command   | Parameters     | Notes                                                           |
+| --------- | -------------- | --------------------------------------------------------------- |
+| ALTCHAR   | Character code | Print single character                                          |
+| ALTSTRING | Text string    | Print text string using ALT+Numpad method                       |
+| ALTCODE   | Text string    | Same as ALTSTRING, presents in some Duckyscript implementations |
 
 
 ## SysRq
 ## SysRq
 
 
 Send [SysRq command](https://en.wikipedia.org/wiki/Magic_SysRq_key)
 Send [SysRq command](https://en.wikipedia.org/wiki/Magic_SysRq_key)
-|Command|Parameters|Notes|
-|-|-|-|
-|SYSRQ|Single character||
+| Command | Parameters       | Notes |
+| ------- | ---------------- | ----- |
+| SYSRQ   | Single character |       |
 
 
 ## USB device ID
 ## USB device ID
 
 

+ 7 - 6
documentation/file_formats/iButtonFileFormat.md

@@ -24,12 +24,13 @@ Changelog:
 
 
 #### Format fields
 #### Format fields
 
 
-| Name      | Type   | Description                                  |
-| --------- | ------ | -------------------------------------------- |
-| Protocol  | string | Currently supported: DS1990, DS1992, DS1996, DSGeneric*, Cyfral, Metakom |
-| Rom Data  | hex    | Read-only memory data (Dallas protocols only) |
-| Sram Data | hex    | Static RAM data (DS1992 and DS1996 only)
-| Data      | hex    | Key data (Cyfral & Metakom only)              |
+| Name        | Type   | Description                                  |
+| ----------- | ------ | -------------------------------------------- |
+| Protocol    | string | Currently supported: DS1990, DS1992, DS1996, DS1971, DSGeneric*, Cyfral, Metakom |
+| Rom Data    | hex    | Read-only memory data (Dallas protocols only) |
+| Sram Data   | hex    | Static RAM data (DS1992 and DS1996 only)
+| Eeprom Data | hex    | EEPROM data (DS1971 only)
+| Data        | hex    | Key data (Cyfral & Metakom only)              |
 
 
 NOTE 1: DSGeneric is a catch-all protocol for all unknown 1-Wire devices. It reads only the ROM and does not perform any checks on the read data. 
 NOTE 1: DSGeneric is a catch-all protocol for all unknown 1-Wire devices. It reads only the ROM and does not perform any checks on the read data. 
 It can also be used if a key with a deliberately invalid family code or checksum is required.
 It can also be used if a key with a deliberately invalid family code or checksum is required.

+ 39 - 12
firmware/targets/f18/api_symbols.csv

@@ -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,,
@@ -151,12 +150,14 @@ Header,+,lib/mlib/m-list.h,,
 Header,+,lib/mlib/m-rbtree.h,,
 Header,+,lib/mlib/m-rbtree.h,,
 Header,+,lib/mlib/m-tuple.h,,
 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/one_wire_host.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,,
@@ -165,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,,
@@ -873,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,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
-Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
-Function,-,furi_hal_compress_free,void,FuriHalCompress*
-Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**"
-Function,-,furi_hal_compress_icon_init,void,
+Function,-,compress_alloc,Compress*,uint16_t
+Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
+Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
+Function,-,compress_free,void,Compress*
+Function,-,compress_icon_decode,void,"const uint8_t*, uint8_t**"
+Function,-,compress_icon_init,void,
 Function,+,furi_hal_console_disable,void,
 Function,+,furi_hal_console_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,
@@ -1312,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*
@@ -1394,6 +1393,7 @@ Function,+,manchester_advance,_Bool,"ManchesterState, ManchesterEvent, Mancheste
 Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*"
 Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*"
 Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState*
 Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState*
 Function,+,manchester_encoder_reset,void,ManchesterEncoderState*
 Function,+,manchester_encoder_reset,void,ManchesterEncoderState*
+Function,+,maxim_crc8,uint8_t,"const uint8_t*, const uint8_t, const uint8_t"
 Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*"
 Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*"
 Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]"
 Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]"
 Function,-,mbedtls_des3_free,void,mbedtls_des3_context*
 Function,-,mbedtls_des3_free,void,mbedtls_des3_context*
@@ -1472,6 +1472,33 @@ Function,+,notification_message,void,"NotificationApp*, const NotificationSequen
 Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*"
 Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*"
 Function,-,nrand48,long,unsigned short[3]
 Function,-,nrand48,long,unsigned short[3]
 Function,-,on_exit,int,"void (*)(int, void*), void*"
 Function,-,on_exit,int,"void (*)(int, void*), void*"
+Function,+,onewire_host_alloc,OneWireHost*,const GpioPin*
+Function,+,onewire_host_free,void,OneWireHost*
+Function,+,onewire_host_read,uint8_t,OneWireHost*
+Function,+,onewire_host_read_bit,_Bool,OneWireHost*
+Function,+,onewire_host_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t"
+Function,+,onewire_host_reset,_Bool,OneWireHost*
+Function,+,onewire_host_reset_search,void,OneWireHost*
+Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
+Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool"
+Function,+,onewire_host_start,void,OneWireHost*
+Function,+,onewire_host_stop,void,OneWireHost*
+Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t"
+Function,+,onewire_host_write,void,"OneWireHost*, uint8_t"
+Function,+,onewire_host_write_bit,void,"OneWireHost*, _Bool"
+Function,+,onewire_host_write_bytes,void,"OneWireHost*, const uint8_t*, uint16_t"
+Function,+,onewire_slave_alloc,OneWireSlave*,const GpioPin*
+Function,+,onewire_slave_free,void,OneWireSlave*
+Function,+,onewire_slave_receive,_Bool,"OneWireSlave*, uint8_t*, size_t"
+Function,+,onewire_slave_receive_bit,_Bool,OneWireSlave*
+Function,+,onewire_slave_send,_Bool,"OneWireSlave*, const uint8_t*, size_t"
+Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool"
+Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*"
+Function,+,onewire_slave_set_overdrive,void,"OneWireSlave*, _Bool"
+Function,+,onewire_slave_set_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*"
+Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*"
+Function,+,onewire_slave_start,void,OneWireSlave*
+Function,+,onewire_slave_stop,void,OneWireSlave*
 Function,-,open_memstream,FILE*,"char**, size_t*"
 Function,-,open_memstream,FILE*,"char**, size_t*"
 Function,+,path_append,void,"FuriString*, const char*"
 Function,+,path_append,void,"FuriString*, const char*"
 Function,+,path_concat,void,"const char*, const char*, FuriString*"
 Function,+,path_concat,void,"const char*, const char*, FuriString*"

+ 5 - 35
firmware/targets/f18/furi_hal/furi_hal.c

@@ -1,37 +1,27 @@
 #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>
 
 
-#include <fatfs.h>
-
 #define TAG "FuriHal"
 #define TAG "FuriHal"
 
 
 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();
 }
 }
 
 
@@ -40,44 +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();
-
-    // FatFS driver initialization
-    MX_FATFS_Init();
-    FURI_LOG_I(TAG, "FATFS OK");
 }
 }
 
 
 void furi_hal_switch(void* address) {
 void furi_hal_switch(void* address) {

+ 4 - 0
firmware/targets/f18/furi_hal/furi_hal_resources.c

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

+ 3 - 2
firmware/targets/f18/target.json

@@ -24,6 +24,7 @@
         "usb_stm32",
         "usb_stm32",
         "appframe",
         "appframe",
         "assets",
         "assets",
+        "one_wire",
         "misc",
         "misc",
         "flipper_application",
         "flipper_application",
         "flipperformat",
         "flipperformat",
@@ -45,11 +46,11 @@
         "furi_hal_subghz_configs.h"
         "furi_hal_subghz_configs.h"
     ],
     ],
     "excluded_modules": [
     "excluded_modules": [
-        "one_wire",
         "nfc",
         "nfc",
         "lfrfid",
         "lfrfid",
         "subghz",
         "subghz",
+        "ibutton",
         "infrared",
         "infrared",
         "st25rfal002"
         "st25rfal002"
     ]
     ]
-}
+}

+ 8 - 18
firmware/targets/f7/api_symbols.csv

@@ -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,,
@@ -62,7 +62,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,,
@@ -118,6 +117,9 @@ Header,+,lib/flipper_application/plugins/composite_resolver.h,,
 Header,+,lib/flipper_application/plugins/plugin_manager.h,,
 Header,+,lib/flipper_application/plugins/plugin_manager.h,,
 Header,+,lib/flipper_format/flipper_format.h,,
 Header,+,lib/flipper_format/flipper_format.h,,
 Header,+,lib/flipper_format/flipper_format_i.h,,
 Header,+,lib/flipper_format/flipper_format_i.h,,
+Header,+,lib/ibutton/ibutton_key.h,,
+Header,+,lib/ibutton/ibutton_protocols.h,,
+Header,+,lib/ibutton/ibutton_worker.h,,
 Header,+,lib/infrared/encoder_decoder/infrared.h,,
 Header,+,lib/infrared/encoder_decoder/infrared.h,,
 Header,+,lib/infrared/worker/infrared_transmit.h,,
 Header,+,lib/infrared/worker/infrared_transmit.h,,
 Header,+,lib/infrared/worker/infrared_worker.h,,
 Header,+,lib/infrared/worker/infrared_worker.h,,
@@ -167,12 +169,8 @@ Header,+,lib/mlib/m-rbtree.h,,
 Header,+,lib/mlib/m-tuple.h,,
 Header,+,lib/mlib/m-tuple.h,,
 Header,+,lib/mlib/m-variant.h,,
 Header,+,lib/mlib/m-variant.h,,
 Header,+,lib/nfc/nfc_device.h,,
 Header,+,lib/nfc/nfc_device.h,,
-Header,+,lib/one_wire/ibutton/ibutton_key.h,,
-Header,+,lib/one_wire/ibutton/ibutton_protocols.h,,
-Header,+,lib/one_wire/ibutton/ibutton_worker.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,,
@@ -192,7 +190,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,,
@@ -201,6 +198,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,,
@@ -1058,12 +1056,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,
@@ -1604,9 +1596,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"
@@ -2065,8 +2054,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_skip,void,OneWireHost*
+Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
+Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool"
 Function,+,onewire_host_start,void,OneWireHost*
 Function,+,onewire_host_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"
@@ -2080,6 +2069,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*

+ 6 - 41
firmware/targets/f7/fatfs/fatfs.c

@@ -1,39 +1,12 @@
-/**
-  ******************************************************************************
-  * @file   fatfs.c
-  * @brief  Code for fatfs applications
-  ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
-  * All rights reserved.</center></h2>
-  *
-  * This software component is licensed by ST under Ultimate Liberty license
-  * SLA0044, the "License"; You may not use this file except in compliance with
-  * the License. You may obtain a copy of the License at:
-  *                             www.st.com/SLA0044
-  *
-  ******************************************************************************
-  */
-
 #include "fatfs.h"
 #include "fatfs.h"
 
 
-uint8_t retUSER; /* Return value for USER */
-char USERPath[4]; /* USER logical drive path */
-FATFS USERFatFS; /* File system object for USER logical drive */
-FIL USERFile; /* File object for USER */
-
-/* USER CODE BEGIN Variables */
-
-/* USER CODE END Variables */
+/** logical drive path */
+char fatfs_path[4];
+/** File system object */
+FATFS fatfs_object;
 
 
-void MX_FATFS_Init(void) {
-    /*## FatFS: Link the USER driver ###########################*/
-    retUSER = FATFS_LinkDriver(&USER_Driver, USERPath);
-
-    /* USER CODE BEGIN Init */
-    /* additional user code for init */
-    /* USER CODE END Init */
+void fatfs_init(void) {
+    FATFS_LinkDriver(&sd_fatfs_driver, fatfs_path);
 }
 }
 
 
 /**
 /**
@@ -42,13 +15,5 @@ void MX_FATFS_Init(void) {
   * @retval Time in DWORD
   * @retval Time in DWORD
   */
   */
 DWORD get_fattime(void) {
 DWORD get_fattime(void) {
-    /* USER CODE BEGIN get_fattime */
     return 0;
     return 0;
-    /* USER CODE END get_fattime */
 }
 }
-
-/* USER CODE BEGIN Application */
-
-/* USER CODE END Application */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 10 - 40
firmware/targets/f7/fatfs/fatfs.h

@@ -1,49 +1,19 @@
-/**
-  ******************************************************************************
-  * @file   fatfs.h
-  * @brief  Header for fatfs applications
-  ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
-  * All rights reserved.</center></h2>
-  *
-  * This software component is licensed by ST under Ultimate Liberty license
-  * SLA0044, the "License"; You may not use this file except in compliance with
-  * the License. You may obtain a copy of the License at:
-  *                             www.st.com/SLA0044
-  *
-  ******************************************************************************
-  */
-
-/* Define to prevent recursive inclusion -------------------------------------*/
-#ifndef __fatfs_H
-#define __fatfs_H
-#ifdef __cplusplus
-extern "C" {
-#endif
+#pragma once
 
 
 #include "fatfs/ff.h"
 #include "fatfs/ff.h"
 #include "fatfs/ff_gen_drv.h"
 #include "fatfs/ff_gen_drv.h"
-#include "user_diskio.h" /* defines USER_Driver as external */
-
-/* USER CODE BEGIN Includes */
-
-/* USER CODE END Includes */
+#include "user_diskio.h"
 
 
-extern uint8_t retUSER; /* Return value for USER */
-extern char USERPath[4]; /* USER logical drive path */
-extern FATFS USERFatFS; /* File system object for USER logical drive */
-extern FIL USERFile; /* File object for USER */
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 
-void MX_FATFS_Init(void);
+/** File system object */
+extern FATFS fatfs_object;
 
 
-/* USER CODE BEGIN Prototypes */
+/** Init file system driver */
+void fatfs_init(void);
 
 
-/* USER CODE END Prototypes */
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
-#endif
-#endif /*__fatfs_H */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+#endif

+ 1 - 1
firmware/targets/f7/fatfs/ffconf.h

@@ -164,7 +164,7 @@
 
 
 /* USER CODE BEGIN Volumes */
 /* USER CODE BEGIN Volumes */
 #define _STR_VOLUME_ID 0 /* 0:Use only 0-9 for drive ID, 1:Use strings for drive ID */
 #define _STR_VOLUME_ID 0 /* 0:Use only 0-9 for drive ID, 1:Use strings for drive ID */
-#define _VOLUME_STRS "RAM", "NAND", "CF", "SD1", "SD2", "USB1", "USB2", "USB3"
+#define _VOLUME_STRS "SD"
 /* _STR_VOLUME_ID switches string support of volume ID.
 /* _STR_VOLUME_ID switches string support of volume ID.
 /  When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
 /  When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
 /  number in the path name. _VOLUME_STRS defines the drive ID strings for each
 /  number in the path name. _VOLUME_STRS defines the drive ID strings for each

+ 0 - 116
firmware/targets/f7/fatfs/syscall.c

@@ -1,116 +0,0 @@
-/*------------------------------------------------------------------------*/
-/* Sample code of OS dependent controls for FatFs                         */
-/* (C)ChaN, 2014                                                          */
-/*   Portions COPYRIGHT 2017 STMicroelectronics                           */
-/*   Portions Copyright (C) 2014, ChaN, all right reserved                */
-/*------------------------------------------------------------------------*/
-
-/**
-  ******************************************************************************
-  * @attention
-  *
-  * Copyright (c) 2017 STMicroelectronics. All rights reserved.
-  *
-  * This software component is licensed by ST under BSD 3-Clause license,
-  * the "License"; You may not use this file except in compliance with the
-  * License. You may obtain a copy of the License at:
-  *                       opensource.org/licenses/BSD-3-Clause
-  *
-  ******************************************************************************
-**/
-
-#include "fatfs/ff.h"
-
-#if _FS_REENTRANT
-/*------------------------------------------------------------------------*/
-/* Create a Synchronization Object                                        */
-/*------------------------------------------------------------------------*/
-/* This function is called in f_mount() function to create a new
-/  synchronization object, such as semaphore and mutex. When a 0 is returned,
-/  the f_mount() function fails with FR_INT_ERR.
-*/
-
-int ff_cre_syncobj(/* 1:Function succeeded, 0:Could not create the sync object */
-                   BYTE vol, /* Corresponding volume (logical drive number) */
-                   _SYNC_t* sobj /* Pointer to return the created sync object */
-) {
-    int ret;
-
-    //osSemaphoreDef(SEM);
-    //*sobj = osSemaphoreCreate(osSemaphore(SEM), 1);
-    *sobj = furi_mutex_alloc(FuriMutexTypeNormal);
-    ret = (*sobj != NULL);
-
-    return ret;
-}
-
-/*------------------------------------------------------------------------*/
-/* Delete a Synchronization Object                                        */
-/*------------------------------------------------------------------------*/
-/* This function is called in f_mount() function to delete a synchronization
-/  object that created with ff_cre_syncobj() function. When a 0 is returned,
-/  the f_mount() function fails with FR_INT_ERR.
-*/
-
-int ff_del_syncobj(/* 1:Function succeeded, 0:Could not delete due to any error */
-                   _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
-) {
-    furi_mutex_free(sobj);
-    return 1;
-}
-
-/*------------------------------------------------------------------------*/
-/* Request Grant to Access the Volume                                     */
-/*------------------------------------------------------------------------*/
-/* This function is called on entering file functions to lock the volume.
-/  When a 0 is returned, the file function fails with FR_TIMEOUT.
-*/
-
-int ff_req_grant(/* 1:Got a grant to access the volume, 0:Could not get a grant */
-                 _SYNC_t sobj /* Sync object to wait */
-) {
-    int ret = 0;
-
-    if(furi_mutex_acquire(sobj, _FS_TIMEOUT) == FuriStatusOk) {
-        ret = 1;
-    }
-
-    return ret;
-}
-
-/*------------------------------------------------------------------------*/
-/* Release Grant to Access the Volume                                     */
-/*------------------------------------------------------------------------*/
-/* This function is called on leaving file functions to unlock the volume.
-*/
-
-void ff_rel_grant(_SYNC_t sobj /* Sync object to be signaled */
-) {
-    furi_mutex_release(sobj);
-}
-
-#endif
-
-#if _USE_LFN == 3 /* LFN with a working buffer on the heap */
-/*------------------------------------------------------------------------*/
-/* Allocate a memory block                                                */
-/*------------------------------------------------------------------------*/
-/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE.
-*/
-
-void* ff_memalloc(/* Returns pointer to the allocated memory block */
-                  UINT msize /* Number of bytes to allocate */
-) {
-    return ff_malloc(msize); /* Allocate a new memory block with POSIX API */
-}
-
-/*------------------------------------------------------------------------*/
-/* Free a memory block                                                    */
-/*------------------------------------------------------------------------*/
-
-void ff_memfree(void* mblock /* Pointer to the memory block to free */
-) {
-    ff_free(mblock); /* Discard the memory block with POSIX API */
-}
-
-#endif

+ 107 - 123
firmware/targets/f7/fatfs/user_diskio.c

@@ -1,50 +1,10 @@
-/* USER CODE BEGIN Header */
-/**
- ******************************************************************************
- * @file    user_diskio.c
- * @brief   This file includes a diskio driver skeleton to be completed by the user.
- ******************************************************************************
- * @attention
- *
- * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
- * All rights reserved.</center></h2>
- *
- * This software component is licensed by ST under Ultimate Liberty license
- * SLA0044, the "License"; You may not use this file except in compliance with
- * the License. You may obtain a copy of the License at:
- *                             www.st.com/SLA0044
- *
- ******************************************************************************
- */
-/* USER CODE END Header */
-
-#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
-/* 
- * Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
- * To be suppressed in the future. 
- * Kept to ensure backward compatibility with previous CubeMx versions when 
- * migrating projects. 
- * User code previously added there should be copied in the new user sections before 
- * the section contents can be deleted.
- */
-/* USER CODE BEGIN 0 */
-/* USER CODE END 0 */
-#endif
-
-/* USER CODE BEGIN DECL */
-
-/* Includes ------------------------------------------------------------------*/
 #include "user_diskio.h"
 #include "user_diskio.h"
 #include <furi_hal.h>
 #include <furi_hal.h>
 #include "sector_cache.h"
 #include "sector_cache.h"
-/* Private typedef -----------------------------------------------------------*/
-/* Private define ------------------------------------------------------------*/
 
 
-/* Private variables ---------------------------------------------------------*/
-/* Disk status */
 static volatile DSTATUS Stat = STA_NOINIT;
 static volatile DSTATUS Stat = STA_NOINIT;
 
 
-static DSTATUS User_CheckStatus(BYTE lun) {
+static DSTATUS driver_check_status(BYTE lun) {
     UNUSED(lun);
     UNUSED(lun);
     Stat = STA_NOINIT;
     Stat = STA_NOINIT;
     if(sd_get_card_state() == SdSpiStatusOK) {
     if(sd_get_card_state() == SdSpiStatusOK) {
@@ -54,32 +14,20 @@ static DSTATUS User_CheckStatus(BYTE lun) {
     return Stat;
     return Stat;
 }
 }
 
 
-/* USER CODE END DECL */
-
-/* Private function prototypes -----------------------------------------------*/
-DSTATUS USER_initialize(BYTE pdrv);
-DSTATUS USER_status(BYTE pdrv);
-DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
-#if _USE_WRITE == 1
-DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
-#endif /* _USE_WRITE == 1 */
-#if _USE_IOCTL == 1
-DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff);
-#endif /* _USE_IOCTL == 1 */
-
-Diskio_drvTypeDef USER_Driver = {
-    USER_initialize,
-    USER_status,
-    USER_read,
-#if _USE_WRITE
-    USER_write,
-#endif /* _USE_WRITE == 1 */
-#if _USE_IOCTL == 1
-    USER_ioctl,
-#endif /* _USE_IOCTL == 1 */
+static DSTATUS driver_initialize(BYTE pdrv);
+static DSTATUS driver_status(BYTE pdrv);
+static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
+static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
+static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff);
+
+Diskio_drvTypeDef sd_fatfs_driver = {
+    driver_initialize,
+    driver_status,
+    driver_read,
+    driver_write,
+    driver_ioctl,
 };
 };
 
 
-/* Private functions ---------------------------------------------------------*/
 static inline bool sd_cache_get(uint32_t address, uint32_t* data) {
 static inline bool sd_cache_get(uint32_t address, uint32_t* data) {
     uint8_t* cached_data = sector_cache_get(address);
     uint8_t* cached_data = sector_cache_get(address);
     if(cached_data) {
     if(cached_data) {
@@ -101,24 +49,73 @@ static inline void sd_cache_invalidate_all() {
     sector_cache_init();
     sector_cache_init();
 }
 }
 
 
+static bool sd_device_read(uint32_t* buff, uint32_t sector, uint32_t count) {
+    bool result = false;
+
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
+    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
+
+    if(sd_read_blocks(buff, sector, count, SD_TIMEOUT_MS) == SdSpiStatusOK) {
+        FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
+
+        /* wait until the read operation is finished */
+        result = true;
+        while(sd_get_card_state() != SdSpiStatusOK) {
+            if(furi_hal_cortex_timer_is_expired(timer)) {
+                result = false;
+                break;
+            }
+        }
+    }
+
+    furi_hal_sd_spi_handle = NULL;
+    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
+
+    return result;
+}
+
+static bool sd_device_write(uint32_t* buff, uint32_t sector, uint32_t count) {
+    bool result = false;
+
+    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
+    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
+
+    if(sd_write_blocks(buff, sector, count, SD_TIMEOUT_MS) == SdSpiStatusOK) {
+        FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
+
+        /* wait until the Write operation is finished */
+        result = true;
+        while(sd_get_card_state() != SdSpiStatusOK) {
+            if(furi_hal_cortex_timer_is_expired(timer)) {
+                sd_cache_invalidate_all();
+
+                result = false;
+                break;
+            }
+        }
+    }
+
+    furi_hal_sd_spi_handle = NULL;
+    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
+
+    return result;
+}
+
 /**
 /**
   * @brief  Initializes a Drive
   * @brief  Initializes a Drive
   * @param  pdrv: Physical drive number (0..)
   * @param  pdrv: Physical drive number (0..)
   * @retval DSTATUS: Operation status
   * @retval DSTATUS: Operation status
   */
   */
-DSTATUS USER_initialize(BYTE pdrv) {
-    /* USER CODE BEGIN INIT */
-
+static DSTATUS driver_initialize(BYTE pdrv) {
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
 
 
-    DSTATUS status = User_CheckStatus(pdrv);
+    DSTATUS status = driver_check_status(pdrv);
 
 
     furi_hal_sd_spi_handle = NULL;
     furi_hal_sd_spi_handle = NULL;
     furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
     furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
 
 
     return status;
     return status;
-    /* USER CODE END INIT */
 }
 }
 
 
 /**
 /**
@@ -126,11 +123,9 @@ DSTATUS USER_initialize(BYTE pdrv) {
   * @param  pdrv: Physical drive number (0..)
   * @param  pdrv: Physical drive number (0..)
   * @retval DSTATUS: Operation status
   * @retval DSTATUS: Operation status
   */
   */
-DSTATUS USER_status(BYTE pdrv) {
-    /* USER CODE BEGIN STATUS */
+static DSTATUS driver_status(BYTE pdrv) {
     UNUSED(pdrv);
     UNUSED(pdrv);
     return Stat;
     return Stat;
-    /* USER CODE END STATUS */
 }
 }
 
 
 /**
 /**
@@ -141,11 +136,10 @@ DSTATUS USER_status(BYTE pdrv) {
   * @param  count: Number of sectors to read (1..128)
   * @param  count: Number of sectors to read (1..128)
   * @retval DRESULT: Operation result
   * @retval DRESULT: Operation result
   */
   */
-DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
-    /* USER CODE BEGIN READ */
+static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
     UNUSED(pdrv);
     UNUSED(pdrv);
-    DRESULT res = RES_ERROR;
 
 
+    bool result;
     bool single_sector = count == 1;
     bool single_sector = count == 1;
 
 
     if(single_sector) {
     if(single_sector) {
@@ -154,32 +148,33 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
         }
         }
     }
     }
 
 
-    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
-    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
+    result = sd_device_read((uint32_t*)buff, (uint32_t)(sector), count);
 
 
-    if(sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) ==
-       SdSpiStatusOK) {
-        FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
+    if(!result) {
+        uint8_t counter = sd_max_mount_retry_count();
 
 
-        /* wait until the read operation is finished */
-        res = RES_OK;
-        while(sd_get_card_state() != SdSpiStatusOK) {
-            if(furi_hal_cortex_timer_is_expired(timer)) {
-                res = RES_ERROR;
-                break;
+        while(result == false && counter > 0 && hal_sd_detect()) {
+            SdSpiStatus status;
+
+            if((counter % 2) == 0) {
+                // power reset sd card
+                status = sd_init(true);
+            } else {
+                status = sd_init(false);
             }
             }
+
+            if(status == SdSpiStatusOK) {
+                result = sd_device_read((uint32_t*)buff, (uint32_t)(sector), count);
+            }
+            counter--;
         }
         }
     }
     }
 
 
-    furi_hal_sd_spi_handle = NULL;
-    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
-
-    if(single_sector && res == RES_OK) {
+    if(single_sector && result == true) {
         sd_cache_put(sector, (uint32_t*)buff);
         sd_cache_put(sector, (uint32_t*)buff);
     }
     }
 
 
-    return res;
-    /* USER CODE END READ */
+    return result ? RES_OK : RES_ERROR;
 }
 }
 
 
 /**
 /**
@@ -190,41 +185,36 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
   * @param  count: Number of sectors to write (1..128)
   * @param  count: Number of sectors to write (1..128)
   * @retval DRESULT: Operation result
   * @retval DRESULT: Operation result
   */
   */
-#if _USE_WRITE == 1
-DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
-    /* USER CODE BEGIN WRITE */
-    /* USER CODE HERE */
+static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
     UNUSED(pdrv);
     UNUSED(pdrv);
-    DRESULT res = RES_ERROR;
+    bool result;
 
 
     sd_cache_invalidate_range(sector, sector + count);
     sd_cache_invalidate_range(sector, sector + count);
 
 
-    furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
-    furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
+    result = sd_device_write((uint32_t*)buff, (uint32_t)(sector), count);
 
 
-    if(sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) ==
-       SdSpiStatusOK) {
-        FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
+    if(!result) {
+        uint8_t counter = sd_max_mount_retry_count();
 
 
-        /* wait until the Write operation is finished */
-        res = RES_OK;
-        while(sd_get_card_state() != SdSpiStatusOK) {
-            if(furi_hal_cortex_timer_is_expired(timer)) {
-                sd_cache_invalidate_all();
+        while(result == false && counter > 0 && hal_sd_detect()) {
+            SdSpiStatus status;
 
 
-                res = RES_ERROR;
-                break;
+            if((counter % 2) == 0) {
+                // power reset sd card
+                status = sd_init(true);
+            } else {
+                status = sd_init(false);
             }
             }
+
+            if(status == SdSpiStatusOK) {
+                result = sd_device_write((uint32_t*)buff, (uint32_t)(sector), count);
+            }
+            counter--;
         }
         }
     }
     }
 
 
-    furi_hal_sd_spi_handle = NULL;
-    furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
-
-    return res;
-    /* USER CODE END WRITE */
+    return result ? RES_OK : RES_ERROR;
 }
 }
-#endif /* _USE_WRITE == 1 */
 
 
 /**
 /**
   * @brief  I/O control operation  
   * @brief  I/O control operation  
@@ -233,9 +223,7 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
   * @param  *buff: Buffer to send/receive control data
   * @param  *buff: Buffer to send/receive control data
   * @retval DRESULT: Operation result
   * @retval DRESULT: Operation result
   */
   */
-#if _USE_IOCTL == 1
-DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
-    /* USER CODE BEGIN IOCTL */
+static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
     UNUSED(pdrv);
     UNUSED(pdrv);
     DRESULT res = RES_ERROR;
     DRESULT res = RES_ERROR;
     SD_CardInfo CardInfo;
     SD_CardInfo CardInfo;
@@ -280,8 +268,4 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
     furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
     furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
 
 
     return res;
     return res;
-    /* USER CODE END IOCTL */
 }
 }
-#endif /* _USE_IOCTL == 1 */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 3 - 37
firmware/targets/f7/fatfs/user_diskio.h

@@ -1,48 +1,14 @@
-/* USER CODE BEGIN Header */
-/**
- ******************************************************************************
-  * @file    user_diskio.h
-  * @brief   This file contains the common defines and functions prototypes for  
-  *          the user_diskio driver.
-  ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
-  * All rights reserved.</center></h2>
-  *
-  * This software component is licensed by ST under Ultimate Liberty license
-  * SLA0044, the "License"; You may not use this file except in compliance with
-  * the License. You may obtain a copy of the License at:
-  *                             www.st.com/SLA0044
-  *
-  ******************************************************************************
-  */
-/* USER CODE END Header */
-
-/* Define to prevent recursive inclusion -------------------------------------*/
-#ifndef __USER_DISKIO_H
-#define __USER_DISKIO_H
+#pragma once
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
-/* USER CODE BEGIN 0 */
-
-/* Includes ------------------------------------------------------------------*/
 #include "sd_spi_io.h"
 #include "sd_spi_io.h"
 #include "fatfs/ff_gen_drv.h"
 #include "fatfs/ff_gen_drv.h"
-/* Exported types ------------------------------------------------------------*/
-/* Exported constants --------------------------------------------------------*/
-/* Exported functions ------------------------------------------------------- */
-extern Diskio_drvTypeDef USER_Driver;
 
 
-/* USER CODE END 0 */
+extern Diskio_drvTypeDef sd_fatfs_driver;
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
-#endif
-
-#endif /* __USER_DISKIO_H */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+#endif

+ 0 - 32
firmware/targets/f7/furi_hal/furi_hal.c

@@ -4,35 +4,24 @@
 
 
 #include <stm32wbxx_ll_cortex.h>
 #include <stm32wbxx_ll_cortex.h>
 
 
-#include <fatfs.h>
-
 #define TAG "FuriHal"
 #define TAG "FuriHal"
 
 
 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();
 }
 }
 
 
@@ -41,50 +30,29 @@ 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_region_init();
     furi_hal_region_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();
     furi_hal_rfid_init();
     furi_hal_rfid_init();
 #endif
 #endif
-
-    // FatFS driver initialization
-    MX_FATFS_Init();
-    FURI_LOG_I(TAG, "FATFS OK");
 }
 }
 
 
 void furi_hal_switch(void* address) {
 void furi_hal_switch(void* address) {

+ 3 - 0
firmware/targets/f7/furi_hal/furi_hal_ibutton.c

@@ -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 - 0
firmware/targets/f7/furi_hal/furi_hal_resources.c

@@ -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};
 
 
@@ -190,6 +192,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) {

+ 13 - 6
firmware/targets/f7/furi_hal/furi_hal_uart.c

@@ -44,7 +44,8 @@ static void furi_hal_usart_init(uint32_t baud) {
     while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1))
     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);
+        }
     }
     }
 }
 }
 
 

+ 133 - 117
firmware/targets/f7/furi_hal/furi_hal_usb_hid.c

@@ -8,28 +8,22 @@
 #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_KB_MAX_KEYS 6
-#define HID_CONSUMER_MAX_KEYS 2
-
 #define HID_INTERVAL 2
 #define HID_INTERVAL 2
 
 
 #define HID_VID_DEFAULT 0x046D
 #define HID_VID_DEFAULT 0x046D
 #define HID_PID_DEFAULT 0xC529
 #define HID_PID_DEFAULT 0xC529
 
 
-struct HidIadDescriptor {
-    struct usb_iad_descriptor hid_iad;
+struct HidIntfDescriptor {
     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 {
@@ -38,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_USAGE_PAGE(HID_DESKTOP_KEYPAD),
-    HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
-    HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
-    HID_LOGICAL_MINIMUM(0),
-    HID_LOGICAL_MAXIMUM(1),
-    HID_REPORT_SIZE(1),
-    HID_REPORT_COUNT(8),
-    HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-    HID_REPORT_COUNT(1),
-    HID_REPORT_SIZE(8),
-    HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-    HID_USAGE_PAGE(HID_PAGE_LED),
-    HID_REPORT_COUNT(8),
-    HID_REPORT_SIZE(1),
-    HID_USAGE_MINIMUM(1),
-    HID_USAGE_MAXIMUM(8),
-    HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-    HID_REPORT_COUNT(HID_KB_MAX_KEYS),
-    HID_REPORT_SIZE(8),
-    HID_LOGICAL_MINIMUM(0),
-    HID_LOGICAL_MAXIMUM(101),
-    HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
-    HID_USAGE_MINIMUM(0),
-    HID_USAGE_MAXIMUM(101),
-    HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
+        HID_REPORT_ID(ReportIdKeyboard), 
+        // Keyboard report
+        HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
+        HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL),
+        HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI),
+        HID_LOGICAL_MINIMUM(0),
+        HID_LOGICAL_MAXIMUM(1),
+        HID_REPORT_SIZE(1),
+        HID_REPORT_COUNT(8),
+        // Input - Modifier keys byte
+        HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+        
+        HID_REPORT_COUNT(1),
+        HID_REPORT_SIZE(8),
+        // Input - Reserved byte
+        HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+
+        HID_USAGE_PAGE(HID_PAGE_LED),
+        HID_REPORT_COUNT(8),
+        HID_REPORT_SIZE(1),
+        HID_USAGE_MINIMUM(1),
+        HID_USAGE_MAXIMUM(8),
+        // Output - LEDs
+        HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+
+        HID_REPORT_COUNT(HID_KB_MAX_KEYS),
+        HID_REPORT_SIZE(8),
+        HID_LOGICAL_MINIMUM(0),
+        HID_LOGICAL_MAXIMUM(101),
+        HID_USAGE_PAGE(HID_DESKTOP_KEYPAD),
+        HID_USAGE_MINIMUM(0),
+        HID_USAGE_MAXIMUM(101),
+        // Input - Key codes
+        HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
     HID_END_COLLECTION,
     HID_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_COLLECTION(HID_PHYSICAL_COLLECTION),
-    HID_REPORT_ID(ReportIdMouse),
-    HID_USAGE_PAGE(HID_PAGE_BUTTON),
-    HID_USAGE_MINIMUM(1),
-    HID_USAGE_MAXIMUM(3),
-    HID_LOGICAL_MINIMUM(0),
-    HID_LOGICAL_MAXIMUM(1),
-    HID_REPORT_COUNT(3),
-    HID_REPORT_SIZE(1),
-    HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-    HID_REPORT_SIZE(1),
-    HID_REPORT_COUNT(5),
-    HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
-    HID_USAGE_PAGE(HID_PAGE_DESKTOP),
-    HID_USAGE(HID_DESKTOP_X),
-    HID_USAGE(HID_DESKTOP_Y),
-    HID_USAGE(HID_DESKTOP_WHEEL),
-    HID_LOGICAL_MINIMUM(-127),
-    HID_LOGICAL_MAXIMUM(127),
-    HID_REPORT_SIZE(8),
-    HID_REPORT_COUNT(3),
-    HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
-    HID_END_COLLECTION,
+        HID_USAGE(HID_DESKTOP_POINTER),
+        HID_COLLECTION(HID_PHYSICAL_COLLECTION),
+            HID_REPORT_ID(ReportIdMouse),
+            // Mouse report
+            HID_USAGE_PAGE(HID_PAGE_BUTTON),
+            HID_USAGE_MINIMUM(1),
+            HID_USAGE_MAXIMUM(3),
+            HID_LOGICAL_MINIMUM(0),
+            HID_LOGICAL_MAXIMUM(1),
+            HID_REPORT_COUNT(3),
+            HID_REPORT_SIZE(1),
+            // Input - Mouse keys
+            HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+
+            HID_REPORT_SIZE(1),
+            HID_REPORT_COUNT(5),
+            // Input - Mouse keys padding
+            HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+            
+            HID_USAGE_PAGE(HID_PAGE_DESKTOP),
+            HID_USAGE(HID_DESKTOP_X),
+            HID_USAGE(HID_DESKTOP_Y),
+            HID_USAGE(HID_DESKTOP_WHEEL),
+            HID_LOGICAL_MINIMUM(-127),
+            HID_LOGICAL_MAXIMUM(127),
+            HID_REPORT_SIZE(8),
+            HID_REPORT_COUNT(3),
+            // Input - Mouse movement data (x, y, scroll)
+            HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
+        HID_END_COLLECTION,
     HID_END_COLLECTION,
     HID_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_LOGICAL_MINIMUM(0),
-    HID_RI_LOGICAL_MAXIMUM(16, 0x3FF),
-    HID_USAGE_MINIMUM(0),
-    HID_RI_USAGE_MAXIMUM(16, 0x3FF),
-    HID_REPORT_COUNT(HID_CONSUMER_MAX_KEYS),
-    HID_REPORT_SIZE(16),
-    HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
+        HID_REPORT_ID(ReportIdConsumer),
+        // Consumer report
+        HID_LOGICAL_MINIMUM(0),
+        HID_RI_LOGICAL_MAXIMUM(16, 0x3FF),
+        HID_USAGE_MINIMUM(0),
+        HID_RI_USAGE_MAXIMUM(16, 0x3FF),
+        HID_REPORT_COUNT(HID_CONSUMER_MAX_KEYS),
+        HID_REPORT_SIZE(16),
+        // Input - Consumer control keys
+        HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),
     HID_END_COLLECTION,
     HID_END_COLLECTION,
+    // clang-format on
 };
 };
 
 
 /* Device descriptor */
 /* Device descriptor */
@@ -117,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,
-    .bDeviceSubClass = USB_SUBCLASS_IAD,
-    .bDeviceProtocol = USB_PROTO_IAD,
+    .bDeviceClass = USB_CLASS_PER_INTERFACE,
+    .bDeviceSubClass = USB_SUBCLASS_NONE,
+    .bDeviceProtocol = USB_PROTO_NONE,
     .bMaxPacketSize0 = USB_EP0_SIZE,
     .bMaxPacketSize0 = USB_EP0_SIZE,
     .idVendor = HID_VID_DEFAULT,
     .idVendor = HID_VID_DEFAULT,
     .idProduct = HID_PID_DEFAULT,
     .idProduct = HID_PID_DEFAULT,
@@ -143,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,
-                    .bInterfaceProtocol = USB_HID_PROTO_NONBOOT,
+                    .bInterfaceSubClass = USB_HID_SUBCLASS_BOOT,
+                    .bInterfaceProtocol = USB_HID_PROTO_KEYBOARD,
                     .iInterface = NO_DESCRIPTOR,
                     .iInterface = NO_DESCRIPTOR,
                 },
                 },
             .hid_desc =
             .hid_desc =
@@ -187,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,
-                },
         },
         },
 };
 };
 
 
@@ -209,9 +203,11 @@ struct HidReportMouse {
 
 
 struct HidReportKB {
 struct HidReportKB {
     uint8_t report_id;
     uint8_t report_id;
-    uint8_t mods;
-    uint8_t reserved;
-    uint8_t btn[HID_KB_MAX_KEYS];
+    struct {
+        uint8_t mods;
+        uint8_t reserved;
+        uint8_t btn[HID_KB_MAX_KEYS];
+    } boot;
 } __attribute__((packed));
 } __attribute__((packed));
 
 
 struct HidReportConsumer {
 struct HidReportConsumer {
@@ -259,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;
@@ -283,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) {
-            hid_report.keyboard.btn[key_nb] = button & 0xFF;
+        if(hid_report.keyboard.boot.btn[key_nb] == 0) {
+            hid_report.keyboard.boot.btn[key_nb] = button & 0xFF;
             break;
             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)) {
-            hid_report.keyboard.btn[key_nb] = 0;
+        if(hid_report.keyboard.boot.btn[key_nb] == (button & 0xFF)) {
+            hid_report.keyboard.boot.btn[key_nb] = 0;
             break;
             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);
 }
 }
 
 
@@ -437,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;
     }
     }
 }
 }
@@ -467,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;
@@ -496,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;
-            dev->status.data_count = sizeof(hid_report);
+            if(boot_protocol == true) {
+                dev->status.data_ptr = &hid_report.keyboard.boot;
+                dev->status.data_count = sizeof(hid_report.keyboard.boot);
+            } else {
+                dev->status.data_ptr = &hid_report;
+                dev->status.data_count = sizeof(hid_report);
+            }
+            return usbd_ack;
+        case USB_HID_SETPROTOCOL:
+            if(req->wValue == 0)
+                boot_protocol = true;
+            else if(req->wValue == 1)
+                boot_protocol = false;
+            else
+                return usbd_fail;
             return usbd_ack;
             return usbd_ack;
         default:
         default:
             return usbd_fail;
             return usbd_fail;
@@ -508,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_count = sizeof(hid_cfg_desc.iad_0.hid_desc);
+            dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.intf_0.hid_desc);
+            dev->status.data_count = sizeof(hid_cfg_desc.intf_0.hid_desc);
             return usbd_ack;
             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;

+ 5 - 2
firmware/targets/f7/src/dfu.c

@@ -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 - 2
firmware/targets/f7/src/recovery.c

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

+ 1 - 1
firmware/targets/f7/src/update.c

@@ -44,7 +44,7 @@ static bool flipper_update_init() {
 
 
     furi_hal_spi_config_init();
     furi_hal_spi_config_init();
 
 
-    MX_FATFS_Init();
+    fatfs_init();
     if(!hal_sd_detect()) {
     if(!hal_sd_detect()) {
         return false;
         return false;
     }
     }

+ 2 - 1
firmware/targets/f7/target.json

@@ -35,6 +35,7 @@
         "appframe",
         "appframe",
         "assets",
         "assets",
         "one_wire",
         "one_wire",
+        "ibutton",
         "misc",
         "misc",
         "mbedtls",
         "mbedtls",
         "lfrfid",
         "lfrfid",
@@ -42,4 +43,4 @@
         "flipperformat",
         "flipperformat",
         "toolbox"
         "toolbox"
     ]
     ]
-}
+}

+ 0 - 1
firmware/targets/furi_hal_include/furi_hal.h

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

+ 0 - 87
firmware/targets/furi_hal_include/furi_hal_compress.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

+ 5 - 0
firmware/targets/furi_hal_include/furi_hal_usb_hid.h

@@ -9,6 +9,11 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
+/** Max number of simultaneously pressed keys (keyboard) */
+#define HID_KB_MAX_KEYS 6
+/** Max number of simultaneously pressed keys (consumer control) */
+#define HID_CONSUMER_MAX_KEYS 2
+
 #define HID_KEYBOARD_NONE 0x00
 #define HID_KEYBOARD_NONE 0x00
 
 
 /** HID keyboard modifier keys */
 /** HID keyboard modifier keys */

+ 1 - 0
lib/SConscript

@@ -87,6 +87,7 @@ libs = env.BuildModules(
         "fatfs",
         "fatfs",
         "flipper_format",
         "flipper_format",
         "one_wire",
         "one_wire",
+        "ibutton",
         "infrared",
         "infrared",
         "littlefs",
         "littlefs",
         "mbedtls",
         "mbedtls",

+ 4 - 0
lib/err.h

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

+ 3 - 2
lib/flipper_application/elf/elf_file.c

@@ -830,8 +830,9 @@ void elf_file_init_debug_info(ELFFile* elf, ELFDebugInfo* debug_info) {
 
 
         const void* data_ptr = itref->value.data;
         const void* data_ptr = itref->value.data;
         if(data_ptr) {
         if(data_ptr) {
-            debug_info->mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr;
-            debug_info->mmap_entries[mmap_entry_idx].name = itref->key;
+            ELFMemoryMapEntry* entry = &debug_info->mmap_entries[mmap_entry_idx];
+            entry->address = (uint32_t)data_ptr;
+            entry->name = itref->key;
             mmap_entry_idx++;
             mmap_entry_idx++;
         }
         }
     }
     }

+ 39 - 8
lib/flipper_application/flipper_application.c

@@ -3,7 +3,9 @@
 #include <notification/notification_messages.h>
 #include <notification/notification_messages.h>
 #include "application_assets.h"
 #include "application_assets.h"
 
 
-#define TAG "fapp"
+#include <m-list.h>
+
+#define TAG "Fap"
 
 
 struct FlipperApplication {
 struct FlipperApplication {
     ELFDebugInfo state;
     ELFDebugInfo state;
@@ -13,8 +15,39 @@ struct FlipperApplication {
     void* ep_thread_args;
     void* ep_thread_args;
 };
 };
 
 
-/* For debugger access to app state */
-FlipperApplication* last_loaded_app = NULL;
+/********************** Debugger access to loader state **********************/
+
+LIST_DEF(FlipperApplicationList, const FlipperApplication*, M_POD_OPLIST);
+
+FlipperApplicationList_t flipper_application_loaded_app_list = {0};
+static bool flipper_application_loaded_app_list_initialized = false;
+
+static void flipper_application_list_add_app(const FlipperApplication* app) {
+    furi_assert(app);
+
+    if(!flipper_application_loaded_app_list_initialized) {
+        FlipperApplicationList_init(flipper_application_loaded_app_list);
+        flipper_application_loaded_app_list_initialized = true;
+    }
+    FlipperApplicationList_push_back(flipper_application_loaded_app_list, app);
+}
+
+static void flipper_application_list_remove_app(const FlipperApplication* app) {
+    furi_assert(flipper_application_loaded_app_list_initialized);
+    furi_assert(app);
+
+    FlipperApplicationList_it_t it;
+    for(FlipperApplicationList_it(it, flipper_application_loaded_app_list);
+        !FlipperApplicationList_end_p(it);
+        FlipperApplicationList_next(it)) {
+        if(*FlipperApplicationList_ref(it) == app) {
+            FlipperApplicationList_remove(flipper_application_loaded_app_list, it);
+            break;
+        }
+    }
+}
+
+/*****************************************************************************/
 
 
 FlipperApplication*
 FlipperApplication*
     flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
     flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
@@ -37,8 +70,8 @@ void flipper_application_free(FlipperApplication* app) {
         furi_thread_free(app->thread);
         furi_thread_free(app->thread);
     }
     }
 
 
-    if(!flipper_application_is_plugin(app)) {
-        last_loaded_app = NULL;
+    if(app->state.entry) {
+        flipper_application_list_remove_app(app);
     }
     }
 
 
     elf_file_clear_debug_info(&app->state);
     elf_file_clear_debug_info(&app->state);
@@ -153,14 +186,12 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic
 }
 }
 
 
 FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
 FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
-    if(!flipper_application_is_plugin(app)) {
-        last_loaded_app = app;
-    }
     ELFFileLoadStatus status = elf_file_load_sections(app->elf);
     ELFFileLoadStatus status = elf_file_load_sections(app->elf);
 
 
     switch(status) {
     switch(status) {
     case ELFFileLoadStatusSuccess:
     case ELFFileLoadStatusSuccess:
         elf_file_init_debug_info(app->elf, &app->state);
         elf_file_init_debug_info(app->elf, &app->state);
+        flipper_application_list_add_app(app);
         return FlipperApplicationLoadStatusSuccess;
         return FlipperApplicationLoadStatusSuccess;
     case ELFFileLoadStatusNoFreeMemory:
     case ELFFileLoadStatusNoFreeMemory:
         return FlipperApplicationLoadStatusNoFreeMemory;
         return FlipperApplicationLoadStatusNoFreeMemory;

+ 1 - 0
lib/heatshrink

@@ -0,0 +1 @@
+Subproject commit 7398ccc91652a33483245200cfa1a83b073bc206

+ 0 - 20
lib/heatshrink/heatshrink_common.h

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

+ 0 - 28
lib/heatshrink/heatshrink_config.h

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

+ 0 - 364
lib/heatshrink/heatshrink_decoder.c

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

+ 0 - 100
lib/heatshrink/heatshrink_decoder.h

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

+ 0 - 602
lib/heatshrink/heatshrink_encoder.c

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

+ 0 - 109
lib/heatshrink/heatshrink_encoder.h

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

+ 24 - 0
lib/ibutton/SConscript

@@ -0,0 +1,24 @@
+Import("env")
+
+env.Append(
+    LINT_SOURCES=[
+        Dir("."),
+    ],
+    CPPPATH=[
+        "#/lib/ibutton",
+    ],
+    SDK_HEADERS=[
+        File("ibutton_key.h"),
+        File("ibutton_worker.h"),
+        File("ibutton_protocols.h"),
+    ],
+)
+
+libenv = env.Clone(FW_LIB_NAME="ibutton")
+libenv.ApplyLibFlags()
+
+sources = libenv.GlobRecursive("*.c*")
+
+lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
+libenv.Install("${LIB_DIST_DIR}", lib)
+Return("lib")

+ 0 - 0
lib/one_wire/ibutton/ibutton_key.c → lib/ibutton/ibutton_key.c


+ 0 - 0
lib/one_wire/ibutton/ibutton_key.h → lib/ibutton/ibutton_key.h


+ 0 - 0
lib/one_wire/ibutton/ibutton_key_i.h → lib/ibutton/ibutton_key_i.h


+ 0 - 0
lib/one_wire/ibutton/ibutton_protocols.c → lib/ibutton/ibutton_protocols.c


+ 0 - 0
lib/one_wire/ibutton/ibutton_protocols.h → lib/ibutton/ibutton_protocols.h


+ 0 - 0
lib/one_wire/ibutton/ibutton_worker.c → lib/ibutton/ibutton_worker.c


+ 0 - 0
lib/one_wire/ibutton/ibutton_worker.h → lib/ibutton/ibutton_worker.h


+ 0 - 0
lib/one_wire/ibutton/ibutton_worker_i.h → lib/ibutton/ibutton_worker_i.h


+ 0 - 0
lib/one_wire/ibutton/ibutton_worker_modes.c → lib/ibutton/ibutton_worker_modes.c


+ 0 - 0
lib/one_wire/ibutton/protocols/blanks/rw1990.c → lib/ibutton/protocols/blanks/rw1990.c


+ 0 - 0
lib/one_wire/ibutton/protocols/blanks/rw1990.h → lib/ibutton/protocols/blanks/rw1990.h


+ 0 - 0
lib/one_wire/ibutton/protocols/blanks/tm2004.c → lib/ibutton/protocols/blanks/tm2004.c


+ 0 - 0
lib/one_wire/ibutton/protocols/blanks/tm2004.h → lib/ibutton/protocols/blanks/tm2004.h


Некоторые файлы не были показаны из-за большого количества измененных файлов