From 05e53cac2647236da455952c643917609c56a1a6 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Tue, 14 Mar 2023 20:25:33 +0300
Subject: [PATCH] Fix merge issues
---
.ci_files/anims_ofw.txt | 4 +-
ReadMe.md | 4 +-
applications/debug/usb_mouse/application.fam | 2 +-
applications/external/dap_link/README.md | 105 ++++
applications/external/dap_link/dap_config.h | 234 ++++++++
applications/external/dap_link/dap_link.c | 527 ++++++++++++++++++
applications/external/dap_link/dap_link.h | 55 ++
applications/external/dap_link/gui/dap_gui.c | 92 +++
applications/external/dap_link/gui/dap_gui.h | 4 +
.../dap_link/gui/dap_gui_custom_event.h | 7 +
.../external/dap_link/gui/dap_gui_i.h | 34 ++
.../gui/scenes/config/dap_scene_config.h | 4 +
.../dap_link/gui/scenes/dap_scene_config.c | 107 ++++
.../dap_link/gui/scenes/dap_scene_help.c | 102 ++++
.../dap_link/gui/scenes/dap_scene_main.c | 154 +++++
.../dap_link/gui/views/dap_main_view.c | 189 +++++++
.../dap_link/gui/views/dap_main_view.h | 45 ++
.../dap_link/icons/ActiveConnection_50x64.png | Bin 0 -> 3842 bytes
.../dap_link/icons/ArrowUpFilled_12x18.png | Bin 0 -> 173 bytes
applications/external/hid_app/application.fam | 8 +-
.../external/music_player/application.fam | 2 +-
.../external/signal_generator/application.fam | 2 +-
22 files changed, 1670 insertions(+), 11 deletions(-)
create mode 100644 applications/external/dap_link/README.md
create mode 100644 applications/external/dap_link/dap_config.h
create mode 100644 applications/external/dap_link/dap_link.c
create mode 100644 applications/external/dap_link/dap_link.h
create mode 100644 applications/external/dap_link/gui/dap_gui.c
create mode 100644 applications/external/dap_link/gui/dap_gui.h
create mode 100644 applications/external/dap_link/gui/dap_gui_custom_event.h
create mode 100644 applications/external/dap_link/gui/dap_gui_i.h
create mode 100644 applications/external/dap_link/gui/scenes/config/dap_scene_config.h
create mode 100644 applications/external/dap_link/gui/scenes/dap_scene_config.c
create mode 100644 applications/external/dap_link/gui/scenes/dap_scene_help.c
create mode 100644 applications/external/dap_link/gui/scenes/dap_scene_main.c
create mode 100644 applications/external/dap_link/gui/views/dap_main_view.c
create mode 100644 applications/external/dap_link/gui/views/dap_main_view.h
create mode 100644 applications/external/dap_link/icons/ActiveConnection_50x64.png
create mode 100644 applications/external/dap_link/icons/ArrowUpFilled_12x18.png
diff --git a/.ci_files/anims_ofw.txt b/.ci_files/anims_ofw.txt
index 94c61e0ff..a9a6fd241 100644
--- a/.ci_files/anims_ofw.txt
+++ b/.ci_files/anims_ofw.txt
@@ -134,9 +134,9 @@ Min level: 3
Max level: 3
Weight: 3
-Name: L1_Sleigh_ride_128x64
+Name: L1_Senpai_128x64
Min butthurt: 0
-Max butthurt: 14
+Max butthurt: 5
Min level: 1
Max level: 3
Weight: 4
diff --git a/ReadMe.md b/ReadMe.md
index ed31961fa..84b80a1e8 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -211,9 +211,9 @@ Games:
## [- How to use: Unitemp - Temperature sensors reader](https://github.com/quen0n/unitemp-flipperzero#readme)
-## [- How to use: [NMEA] GPS](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/gps_nmea_uart/README.md)
+## [- How to use: [NMEA] GPS](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/external/gps_nmea_uart/README.md)
-## [- How to use: i2c Tools](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/flipper_i2ctools/README.md)
+## [- How to use: i2c Tools](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/external/flipper_i2ctools/README.md)
## [- How to use: [NRF24] plugins](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/NRF24.md)
diff --git a/applications/debug/usb_mouse/application.fam b/applications/debug/usb_mouse/application.fam
index bf555c671..38ba55425 100644
--- a/applications/debug/usb_mouse/application.fam
+++ b/applications/debug/usb_mouse/application.fam
@@ -7,6 +7,6 @@ App(
requires=["gui"],
stack_size=1 * 1024,
order=60,
- fap_icon="../../plugins/mousejacker/mouse_10px.png",
+ fap_icon="../../external/mousejacker/mouse_10px.png",
fap_category="Debug",
)
diff --git a/applications/external/dap_link/README.md b/applications/external/dap_link/README.md
new file mode 100644
index 000000000..aead0a60a
--- /dev/null
+++ b/applications/external/dap_link/README.md
@@ -0,0 +1,105 @@
+# Flipper Zero as CMSIS DAP/DAP Link
+Flipper Zero as a [Free-DAP](https://github.com/ataradov/free-dap) based SWD\JTAG debugger. Free-DAP is a free and open source firmware implementation of the [CMSIS-DAP](https://www.keil.com/pack/doc/CMSIS_Dev/DAP/html/index.html) debugger.
+
+## Protocols
+SWD, JTAG , CMSIS-DAP v1 (18 KiB/s), CMSIS-DAP v2 (46 KiB/s), VCP (USB-UART).
+
+WinUSB for driverless installation for Windows 8 and above.
+
+## Usage
+
+### VSCode + Cortex-Debug
+ Set `"device": "cmsis-dap"`
+
+
+ BluePill configuration example
+
+ ```json
+{
+ "name": "Attach (DAP)",
+ "cwd": "${workspaceFolder}",
+ "executable": "./build/firmware.elf",
+ "request": "attach",
+ "type": "cortex-debug",
+ "servertype": "openocd",
+ "device": "cmsis-dap",
+ "configFiles": [
+ "interface/cmsis-dap.cfg",
+ "target/stm32f1x.cfg",
+ ],
+},
+ ```
+
+
+
+ Flipper Zero configuration example
+
+ ```json
+{
+ "name": "Attach (DAP)",
+ "cwd": "${workspaceFolder}",
+ "executable": "./build/latest/firmware.elf",
+ "request": "attach",
+ "type": "cortex-debug",
+ "servertype": "openocd",
+ "device": "cmsis-dap",
+ "svdFile": "./debug/STM32WB55_CM4.svd",
+ "rtos": "FreeRTOS",
+ "configFiles": [
+ "interface/cmsis-dap.cfg",
+ "./debug/stm32wbx.cfg",
+ ],
+ "postAttachCommands": [
+ "source debug/flipperapps.py",
+ ],
+},
+ ```
+
+
+### OpenOCD
+Use `interface/cmsis-dap.cfg`. You will need OpenOCD v0.11.0.
+
+Additional commands:
+* `cmsis_dap_backend hid` for CMSIS-DAP v1 protocol.
+* `cmsis_dap_backend usb_bulk` for CMSIS-DAP v2 protocol.
+* `cmsis_dap_serial DAP_Oyevoxo` use DAP-Link running on Flipper named `Oyevoxo`.
+* `cmsis-dap cmd 81` - reboot connected DAP-Link.
+
+
+ Flash BluePill
+
+ ```
+openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c init -c "program build/firmware.bin reset exit 0x8000000"
+ ```
+
+
+
+ Flash Flipper Zero using DAP v2 protocol
+
+ ```
+openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_backend usb_bulk" -f debug/stm32wbx.cfg -c init -c "program build/latest/firmware.bin reset exit 0x8000000"
+ ```
+
+
+
+ Reboot connected DAP-Link on Flipper named Oyevoxo
+
+ ```
+openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_serial DAP_Oyevoxo" -c "transport select swd" -c "adapter speed 4000000" -c init -c "cmsis-dap cmd 81" -c "exit"
+ ```
+
+
+### PlatformIO
+Use `debug_tool = cmsis-dap` and `upload_protocol = cmsis-dap`. [Documentation](https://docs.platformio.org/en/latest/plus/debug-tools/cmsis-dap.html#debugging-tool-cmsis-dap). Remember that Windows 8 and above do not require drivers.
+
+
+ BluePill platformio.ini example
+
+ ```
+[env:bluepill_f103c8]
+platform = ststm32
+board = bluepill_f103c8
+debug_tool = cmsis-dap
+upload_protocol = cmsis-dap
+ ```
+
diff --git a/applications/external/dap_link/dap_config.h b/applications/external/dap_link/dap_config.h
new file mode 100644
index 000000000..88b90bd34
--- /dev/null
+++ b/applications/external/dap_link/dap_config.h
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2022, Alex Taradov . All rights reserved.
+
+#ifndef _DAP_CONFIG_H_
+#define _DAP_CONFIG_H_
+
+/*- Includes ----------------------------------------------------------------*/
+#include
+
+/*- Definitions -------------------------------------------------------------*/
+#define DAP_CONFIG_ENABLE_JTAG
+
+#define DAP_CONFIG_DEFAULT_PORT DAP_PORT_SWD
+#define DAP_CONFIG_DEFAULT_CLOCK 4200000 // Hz
+
+#define DAP_CONFIG_PACKET_SIZE 64
+#define DAP_CONFIG_PACKET_COUNT 1
+
+#define DAP_CONFIG_JTAG_DEV_COUNT 8
+
+// DAP_CONFIG_PRODUCT_STR must contain "CMSIS-DAP" to be compatible with the standard
+#define DAP_CONFIG_VENDOR_STR "Flipper Zero"
+#define DAP_CONFIG_PRODUCT_STR "Generic CMSIS-DAP Adapter"
+#define DAP_CONFIG_SER_NUM_STR usb_serial_number
+#define DAP_CONFIG_CMSIS_DAP_VER_STR "2.0.0"
+
+#define DAP_CONFIG_RESET_TARGET_FN dap_app_target_reset
+#define DAP_CONFIG_VENDOR_FN dap_app_vendor_cmd
+
+// Attribute to use for performance-critical functions
+#define DAP_CONFIG_PERFORMANCE_ATTR
+
+// A value at which dap_clock_test() produces 1 kHz output on the SWCLK pin
+// #define DAP_CONFIG_DELAY_CONSTANT 19000
+#define DAP_CONFIG_DELAY_CONSTANT 6290
+
+// A threshold for switching to fast clock (no added delays)
+// This is the frequency produced by dap_clock_test(1) on the SWCLK pin
+#define DAP_CONFIG_FAST_CLOCK 2400000 // Hz
+
+/*- Prototypes --------------------------------------------------------------*/
+extern char usb_serial_number[16];
+
+/*- Implementations ---------------------------------------------------------*/
+extern GpioPin flipper_dap_swclk_pin;
+extern GpioPin flipper_dap_swdio_pin;
+extern GpioPin flipper_dap_reset_pin;
+extern GpioPin flipper_dap_tdo_pin;
+extern GpioPin flipper_dap_tdi_pin;
+
+extern void dap_app_vendor_cmd(uint8_t cmd);
+extern void dap_app_target_reset();
+extern void dap_app_disconnect();
+extern void dap_app_connect_swd();
+extern void dap_app_connect_jtag();
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_SWCLK_TCK_write(int value) {
+ furi_hal_gpio_write(&flipper_dap_swclk_pin, value);
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_SWDIO_TMS_write(int value) {
+ furi_hal_gpio_write(&flipper_dap_swdio_pin, value);
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_TDI_write(int value) {
+#ifdef DAP_CONFIG_ENABLE_JTAG
+ furi_hal_gpio_write(&flipper_dap_tdi_pin, value);
+#else
+ (void)value;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_TDO_write(int value) {
+#ifdef DAP_CONFIG_ENABLE_JTAG
+ furi_hal_gpio_write(&flipper_dap_tdo_pin, value);
+#else
+ (void)value;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_nTRST_write(int value) {
+ (void)value;
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_nRESET_write(int value) {
+ furi_hal_gpio_write(&flipper_dap_reset_pin, value);
+}
+
+//-----------------------------------------------------------------------------
+static inline int DAP_CONFIG_SWCLK_TCK_read(void) {
+ return furi_hal_gpio_read(&flipper_dap_swclk_pin);
+}
+
+//-----------------------------------------------------------------------------
+static inline int DAP_CONFIG_SWDIO_TMS_read(void) {
+ return furi_hal_gpio_read(&flipper_dap_swdio_pin);
+}
+
+//-----------------------------------------------------------------------------
+static inline int DAP_CONFIG_TDO_read(void) {
+#ifdef DAP_CONFIG_ENABLE_JTAG
+ return furi_hal_gpio_read(&flipper_dap_tdo_pin);
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+static inline int DAP_CONFIG_TDI_read(void) {
+#ifdef DAP_CONFIG_ENABLE_JTAG
+ return furi_hal_gpio_read(&flipper_dap_tdi_pin);
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+static inline int DAP_CONFIG_nTRST_read(void) {
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+static inline int DAP_CONFIG_nRESET_read(void) {
+ return furi_hal_gpio_read(&flipper_dap_reset_pin);
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_SWCLK_TCK_set(void) {
+ LL_GPIO_SetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin);
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_SWCLK_TCK_clr(void) {
+ LL_GPIO_ResetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin);
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_SWDIO_TMS_in(void) {
+ LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_INPUT);
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_SWDIO_TMS_out(void) {
+ LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_OUTPUT);
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_SETUP(void) {
+ furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+#ifdef DAP_CONFIG_ENABLE_JTAG
+ furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_DISCONNECT(void) {
+ furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+#ifdef DAP_CONFIG_ENABLE_JTAG
+ furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+#endif
+ dap_app_disconnect();
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_CONNECT_SWD(void) {
+ furi_hal_gpio_init(
+ &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_write(&flipper_dap_swdio_pin, true);
+
+ furi_hal_gpio_init(
+ &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_write(&flipper_dap_swclk_pin, true);
+
+ furi_hal_gpio_init(
+ &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_write(&flipper_dap_reset_pin, true);
+
+#ifdef DAP_CONFIG_ENABLE_JTAG
+ furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+#endif
+ dap_app_connect_swd();
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_CONNECT_JTAG(void) {
+ furi_hal_gpio_init(
+ &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_write(&flipper_dap_swdio_pin, true);
+
+ furi_hal_gpio_init(
+ &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_write(&flipper_dap_swclk_pin, true);
+
+ furi_hal_gpio_init(
+ &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_write(&flipper_dap_reset_pin, true);
+
+#ifdef DAP_CONFIG_ENABLE_JTAG
+ furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+
+ furi_hal_gpio_init(
+ &flipper_dap_tdi_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+ furi_hal_gpio_write(&flipper_dap_tdi_pin, true);
+#endif
+ dap_app_connect_jtag();
+}
+
+//-----------------------------------------------------------------------------
+static inline void DAP_CONFIG_LED(int index, int state) {
+ (void)index;
+ (void)state;
+}
+
+//-----------------------------------------------------------------------------
+__attribute__((always_inline)) static inline void DAP_CONFIG_DELAY(uint32_t cycles) {
+ asm volatile("1: subs %[cycles], %[cycles], #1 \n"
+ " bne 1b \n"
+ : [cycles] "+l"(cycles));
+}
+
+#endif // _DAP_CONFIG_H_
diff --git a/applications/external/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c
new file mode 100644
index 000000000..eafb435e7
--- /dev/null
+++ b/applications/external/dap_link/dap_link.c
@@ -0,0 +1,527 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "dap_link.h"
+#include "dap_config.h"
+#include "gui/dap_gui.h"
+#include "usb/dap_v2_usb.h"
+#include
+#include "dap_link_icons.h"
+
+/***************************************************************************/
+/****************************** DAP COMMON *********************************/
+/***************************************************************************/
+
+struct DapApp {
+ FuriThread* dap_thread;
+ FuriThread* cdc_thread;
+ FuriThread* gui_thread;
+
+ DapState state;
+ DapConfig config;
+};
+
+void dap_app_get_state(DapApp* app, DapState* state) {
+ *state = app->state;
+}
+
+#define DAP_PROCESS_THREAD_TICK 500
+
+typedef enum {
+ DapThreadEventStop = (1 << 0),
+} DapThreadEvent;
+
+void dap_thread_send_stop(FuriThread* thread) {
+ furi_thread_flags_set(furi_thread_get_id(thread), DapThreadEventStop);
+}
+
+GpioPin flipper_dap_swclk_pin;
+GpioPin flipper_dap_swdio_pin;
+GpioPin flipper_dap_reset_pin;
+GpioPin flipper_dap_tdo_pin;
+GpioPin flipper_dap_tdi_pin;
+
+/***************************************************************************/
+/****************************** DAP PROCESS ********************************/
+/***************************************************************************/
+
+typedef struct {
+ uint8_t data[DAP_CONFIG_PACKET_SIZE];
+ uint8_t size;
+} DapPacket;
+
+typedef enum {
+ DAPThreadEventStop = DapThreadEventStop,
+ DAPThreadEventRxV1 = (1 << 1),
+ DAPThreadEventRxV2 = (1 << 2),
+ DAPThreadEventUSBConnect = (1 << 3),
+ DAPThreadEventUSBDisconnect = (1 << 4),
+ DAPThreadEventApplyConfig = (1 << 5),
+ DAPThreadEventAll = DAPThreadEventStop | DAPThreadEventRxV1 | DAPThreadEventRxV2 |
+ DAPThreadEventUSBConnect | DAPThreadEventUSBDisconnect |
+ DAPThreadEventApplyConfig,
+} DAPThreadEvent;
+
+#define USB_SERIAL_NUMBER_LEN 16
+char usb_serial_number[USB_SERIAL_NUMBER_LEN] = {0};
+
+const char* dap_app_get_serial(DapApp* app) {
+ UNUSED(app);
+ return usb_serial_number;
+}
+
+static void dap_app_rx1_callback(void* context) {
+ furi_assert(context);
+ FuriThreadId thread_id = (FuriThreadId)context;
+ furi_thread_flags_set(thread_id, DAPThreadEventRxV1);
+}
+
+static void dap_app_rx2_callback(void* context) {
+ furi_assert(context);
+ FuriThreadId thread_id = (FuriThreadId)context;
+ furi_thread_flags_set(thread_id, DAPThreadEventRxV2);
+}
+
+static void dap_app_usb_state_callback(bool state, void* context) {
+ furi_assert(context);
+ FuriThreadId thread_id = (FuriThreadId)context;
+ if(state) {
+ furi_thread_flags_set(thread_id, DAPThreadEventUSBConnect);
+ } else {
+ furi_thread_flags_set(thread_id, DAPThreadEventUSBDisconnect);
+ }
+}
+
+static void dap_app_process_v1() {
+ DapPacket tx_packet;
+ DapPacket rx_packet;
+ memset(&tx_packet, 0, sizeof(DapPacket));
+ rx_packet.size = dap_v1_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE);
+ dap_process_request(rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE);
+ dap_v1_usb_tx(tx_packet.data, DAP_CONFIG_PACKET_SIZE);
+}
+
+static void dap_app_process_v2() {
+ DapPacket tx_packet;
+ DapPacket rx_packet;
+ memset(&tx_packet, 0, sizeof(DapPacket));
+ rx_packet.size = dap_v2_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE);
+ size_t len = dap_process_request(
+ rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE);
+ dap_v2_usb_tx(tx_packet.data, len);
+}
+
+void dap_app_vendor_cmd(uint8_t cmd) {
+ // openocd -c "cmsis-dap cmd 81"
+ if(cmd == 0x01) {
+ furi_hal_power_reset();
+ }
+}
+
+void dap_app_target_reset() {
+ FURI_LOG_I("DAP", "Target reset");
+}
+
+static void dap_init_gpio(DapSwdPins swd_pins) {
+ switch(swd_pins) {
+ case DapSwdPinsPA7PA6:
+ flipper_dap_swclk_pin = gpio_ext_pa7;
+ flipper_dap_swdio_pin = gpio_ext_pa6;
+ break;
+ case DapSwdPinsPA14PA13:
+ flipper_dap_swclk_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_14};
+ flipper_dap_swdio_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_13};
+ break;
+ }
+
+ flipper_dap_reset_pin = gpio_ext_pa4;
+ flipper_dap_tdo_pin = gpio_ext_pb3;
+ flipper_dap_tdi_pin = gpio_ext_pb2;
+}
+
+static void dap_deinit_gpio(DapSwdPins swd_pins) {
+ // setup gpio pins to default state
+ furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+
+ if(DapSwdPinsPA14PA13 == swd_pins) {
+ // PA14 and PA13 are used by SWD
+ furi_hal_gpio_init_ex(
+ &flipper_dap_swclk_pin,
+ GpioModeAltFunctionPushPull,
+ GpioPullDown,
+ GpioSpeedLow,
+ GpioAltFn0JTCK_SWCLK);
+ furi_hal_gpio_init_ex(
+ &flipper_dap_swdio_pin,
+ GpioModeAltFunctionPushPull,
+ GpioPullUp,
+ GpioSpeedVeryHigh,
+ GpioAltFn0JTMS_SWDIO);
+ } else {
+ furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+ }
+}
+
+static int32_t dap_process(void* p) {
+ DapApp* app = p;
+ DapState* dap_state = &(app->state);
+
+ // allocate resources
+ FuriHalUsbInterface* usb_config_prev;
+ app->config.swd_pins = DapSwdPinsPA7PA6;
+ DapSwdPins swd_pins_prev = app->config.swd_pins;
+
+ // init pins
+ dap_init_gpio(swd_pins_prev);
+
+ // init dap
+ dap_init();
+
+ // get name
+ const char* name = furi_hal_version_get_name_ptr();
+ if(!name) {
+ name = "Flipper";
+ }
+ snprintf(usb_serial_number, USB_SERIAL_NUMBER_LEN, "DAP_%s", name);
+
+ // init usb
+ usb_config_prev = furi_hal_usb_get_config();
+ dap_common_usb_alloc_name(usb_serial_number);
+ dap_common_usb_set_context(furi_thread_get_id(furi_thread_get_current()));
+ dap_v1_usb_set_rx_callback(dap_app_rx1_callback);
+ dap_v2_usb_set_rx_callback(dap_app_rx2_callback);
+ dap_common_usb_set_state_callback(dap_app_usb_state_callback);
+ furi_hal_usb_set_config(&dap_v2_usb_hid, NULL);
+
+ // work
+ uint32_t events;
+ while(1) {
+ events = furi_thread_flags_wait(DAPThreadEventAll, FuriFlagWaitAny, FuriWaitForever);
+
+ if(!(events & FuriFlagError)) {
+ if(events & DAPThreadEventRxV1) {
+ dap_app_process_v1();
+ dap_state->dap_counter++;
+ dap_state->dap_version = DapVersionV1;
+ }
+
+ if(events & DAPThreadEventRxV2) {
+ dap_app_process_v2();
+ dap_state->dap_counter++;
+ dap_state->dap_version = DapVersionV2;
+ }
+
+ if(events & DAPThreadEventUSBConnect) {
+ dap_state->usb_connected = true;
+ }
+
+ if(events & DAPThreadEventUSBDisconnect) {
+ dap_state->usb_connected = false;
+ dap_state->dap_version = DapVersionUnknown;
+ }
+
+ if(events & DAPThreadEventApplyConfig) {
+ if(swd_pins_prev != app->config.swd_pins) {
+ dap_deinit_gpio(swd_pins_prev);
+ swd_pins_prev = app->config.swd_pins;
+ dap_init_gpio(swd_pins_prev);
+ }
+ }
+
+ if(events & DAPThreadEventStop) {
+ break;
+ }
+ }
+ }
+
+ // deinit usb
+ furi_hal_usb_set_config(usb_config_prev, NULL);
+ dap_common_usb_free_name();
+ dap_deinit_gpio(swd_pins_prev);
+ return 0;
+}
+
+/***************************************************************************/
+/****************************** CDC PROCESS ********************************/
+/***************************************************************************/
+
+typedef enum {
+ CDCThreadEventStop = DapThreadEventStop,
+ CDCThreadEventUARTRx = (1 << 1),
+ CDCThreadEventCDCRx = (1 << 2),
+ CDCThreadEventCDCConfig = (1 << 3),
+ CDCThreadEventApplyConfig = (1 << 4),
+ CDCThreadEventAll = CDCThreadEventStop | CDCThreadEventUARTRx | CDCThreadEventCDCRx |
+ CDCThreadEventCDCConfig | CDCThreadEventApplyConfig,
+} CDCThreadEvent;
+
+typedef struct {
+ FuriStreamBuffer* rx_stream;
+ FuriThreadId thread_id;
+ FuriHalUartId uart_id;
+ struct usb_cdc_line_coding line_coding;
+} CDCProcess;
+
+static void cdc_uart_irq_cb(UartIrqEvent ev, uint8_t data, void* ctx) {
+ CDCProcess* app = ctx;
+
+ if(ev == UartIrqEventRXNE) {
+ furi_stream_buffer_send(app->rx_stream, &data, 1, 0);
+ furi_thread_flags_set(app->thread_id, CDCThreadEventUARTRx);
+ }
+}
+
+static void cdc_usb_rx_callback(void* context) {
+ CDCProcess* app = context;
+ furi_thread_flags_set(app->thread_id, CDCThreadEventCDCRx);
+}
+
+static void cdc_usb_control_line_callback(uint8_t state, void* context) {
+ UNUSED(context);
+ UNUSED(state);
+}
+
+static void cdc_usb_config_callback(struct usb_cdc_line_coding* config, void* context) {
+ CDCProcess* app = context;
+ app->line_coding = *config;
+ furi_thread_flags_set(app->thread_id, CDCThreadEventCDCConfig);
+}
+
+static FuriHalUartId cdc_init_uart(
+ DapUartType type,
+ DapUartTXRX swap,
+ uint32_t baudrate,
+ void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx),
+ void* ctx) {
+ FuriHalUartId uart_id = FuriHalUartIdUSART1;
+ if(baudrate == 0) baudrate = 115200;
+
+ switch(type) {
+ case DapUartTypeUSART1:
+ uart_id = FuriHalUartIdUSART1;
+ furi_hal_console_disable();
+ furi_hal_uart_deinit(uart_id);
+ if(swap == DapUartTXRXSwap) {
+ LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_SWAPPED);
+ } else {
+ LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD);
+ }
+ furi_hal_uart_init(uart_id, baudrate);
+ furi_hal_uart_set_irq_cb(uart_id, cb, ctx);
+ break;
+ case DapUartTypeLPUART1:
+ uart_id = FuriHalUartIdLPUART1;
+ furi_hal_uart_deinit(uart_id);
+ if(swap == DapUartTXRXSwap) {
+ LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_SWAPPED);
+ } else {
+ LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD);
+ }
+ furi_hal_uart_init(uart_id, baudrate);
+ furi_hal_uart_set_irq_cb(uart_id, cb, ctx);
+ break;
+ }
+
+ return uart_id;
+}
+
+static void cdc_deinit_uart(DapUartType type) {
+ switch(type) {
+ case DapUartTypeUSART1:
+ furi_hal_uart_deinit(FuriHalUartIdUSART1);
+ LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD);
+ furi_hal_console_init();
+ break;
+ case DapUartTypeLPUART1:
+ furi_hal_uart_deinit(FuriHalUartIdLPUART1);
+ LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD);
+ break;
+ }
+}
+
+static int32_t cdc_process(void* p) {
+ DapApp* dap_app = p;
+ DapState* dap_state = &(dap_app->state);
+
+ dap_app->config.uart_pins = DapUartTypeLPUART1;
+ dap_app->config.uart_swap = DapUartTXRXNormal;
+
+ DapUartType uart_pins_prev = dap_app->config.uart_pins;
+ DapUartTXRX uart_swap_prev = dap_app->config.uart_swap;
+
+ CDCProcess* app = malloc(sizeof(CDCProcess));
+ app->thread_id = furi_thread_get_id(furi_thread_get_current());
+ app->rx_stream = furi_stream_buffer_alloc(512, 1);
+
+ const uint8_t rx_buffer_size = 64;
+ uint8_t* rx_buffer = malloc(rx_buffer_size);
+
+ app->uart_id = cdc_init_uart(
+ uart_pins_prev, uart_swap_prev, dap_state->cdc_baudrate, cdc_uart_irq_cb, app);
+
+ dap_cdc_usb_set_context(app);
+ dap_cdc_usb_set_rx_callback(cdc_usb_rx_callback);
+ dap_cdc_usb_set_control_line_callback(cdc_usb_control_line_callback);
+ dap_cdc_usb_set_config_callback(cdc_usb_config_callback);
+
+ uint32_t events;
+ while(1) {
+ events = furi_thread_flags_wait(CDCThreadEventAll, FuriFlagWaitAny, FuriWaitForever);
+
+ if(!(events & FuriFlagError)) {
+ if(events & CDCThreadEventCDCConfig) {
+ if(dap_state->cdc_baudrate != app->line_coding.dwDTERate) {
+ dap_state->cdc_baudrate = app->line_coding.dwDTERate;
+ if(dap_state->cdc_baudrate > 0) {
+ furi_hal_uart_set_br(app->uart_id, dap_state->cdc_baudrate);
+ }
+ }
+ }
+
+ if(events & CDCThreadEventUARTRx) {
+ size_t len =
+ furi_stream_buffer_receive(app->rx_stream, rx_buffer, rx_buffer_size, 0);
+
+ if(len > 0) {
+ dap_cdc_usb_tx(rx_buffer, len);
+ }
+ dap_state->cdc_rx_counter += len;
+ }
+
+ if(events & CDCThreadEventCDCRx) {
+ size_t len = dap_cdc_usb_rx(rx_buffer, rx_buffer_size);
+ if(len > 0) {
+ furi_hal_uart_tx(app->uart_id, rx_buffer, len);
+ }
+ dap_state->cdc_tx_counter += len;
+ }
+
+ if(events & CDCThreadEventApplyConfig) {
+ if(uart_pins_prev != dap_app->config.uart_pins ||
+ uart_swap_prev != dap_app->config.uart_swap) {
+ cdc_deinit_uart(uart_pins_prev);
+ uart_pins_prev = dap_app->config.uart_pins;
+ uart_swap_prev = dap_app->config.uart_swap;
+ app->uart_id = cdc_init_uart(
+ uart_pins_prev,
+ uart_swap_prev,
+ dap_state->cdc_baudrate,
+ cdc_uart_irq_cb,
+ app);
+ }
+ }
+
+ if(events & CDCThreadEventStop) {
+ break;
+ }
+ }
+ }
+
+ cdc_deinit_uart(uart_pins_prev);
+ free(rx_buffer);
+ furi_stream_buffer_free(app->rx_stream);
+ free(app);
+
+ return 0;
+}
+
+/***************************************************************************/
+/******************************* MAIN APP **********************************/
+/***************************************************************************/
+
+static DapApp* dap_app_alloc() {
+ DapApp* dap_app = malloc(sizeof(DapApp));
+ dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app);
+ dap_app->cdc_thread = furi_thread_alloc_ex("DAP CDC", 1024, cdc_process, dap_app);
+ dap_app->gui_thread = furi_thread_alloc_ex("DAP GUI", 1024, dap_gui_thread, dap_app);
+ return dap_app;
+}
+
+static void dap_app_free(DapApp* dap_app) {
+ furi_assert(dap_app);
+ furi_thread_free(dap_app->dap_thread);
+ furi_thread_free(dap_app->cdc_thread);
+ furi_thread_free(dap_app->gui_thread);
+ free(dap_app);
+}
+
+static DapApp* app_handle = NULL;
+
+void dap_app_disconnect() {
+ app_handle->state.dap_mode = DapModeDisconnected;
+}
+
+void dap_app_connect_swd() {
+ app_handle->state.dap_mode = DapModeSWD;
+}
+
+void dap_app_connect_jtag() {
+ app_handle->state.dap_mode = DapModeJTAG;
+}
+
+void dap_app_set_config(DapApp* app, DapConfig* config) {
+ app->config = *config;
+ furi_thread_flags_set(furi_thread_get_id(app->dap_thread), DAPThreadEventApplyConfig);
+ furi_thread_flags_set(furi_thread_get_id(app->cdc_thread), CDCThreadEventApplyConfig);
+}
+
+DapConfig* dap_app_get_config(DapApp* app) {
+ return &app->config;
+}
+
+int32_t dap_link_app(void* p) {
+ UNUSED(p);
+
+ if(furi_hal_usb_is_locked()) {
+ DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
+ DialogMessage* message = dialog_message_alloc();
+ dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
+ dialog_message_set_text(
+ message,
+ "Disconnect from\nPC or phone to\nuse this function.",
+ 3,
+ 30,
+ AlignLeft,
+ AlignTop);
+ dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0);
+ dialog_message_show(dialogs, message);
+ dialog_message_free(message);
+ furi_record_close(RECORD_DIALOGS);
+ return -1;
+ }
+
+ // alloc app
+ DapApp* app = dap_app_alloc();
+ app_handle = app;
+
+ furi_thread_start(app->dap_thread);
+ furi_thread_start(app->cdc_thread);
+ furi_thread_start(app->gui_thread);
+
+ // wait until gui thread is finished
+ furi_thread_join(app->gui_thread);
+
+ // send stop event to threads
+ dap_thread_send_stop(app->dap_thread);
+ dap_thread_send_stop(app->cdc_thread);
+
+ // wait for threads to stop
+ furi_thread_join(app->dap_thread);
+ furi_thread_join(app->cdc_thread);
+
+ // free app
+ dap_app_free(app);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/applications/external/dap_link/dap_link.h b/applications/external/dap_link/dap_link.h
new file mode 100644
index 000000000..d51726c45
--- /dev/null
+++ b/applications/external/dap_link/dap_link.h
@@ -0,0 +1,55 @@
+#pragma once
+#include
+
+typedef enum {
+ DapModeDisconnected,
+ DapModeSWD,
+ DapModeJTAG,
+} DapMode;
+
+typedef enum {
+ DapVersionUnknown,
+ DapVersionV1,
+ DapVersionV2,
+} DapVersion;
+
+typedef struct {
+ bool usb_connected;
+ DapMode dap_mode;
+ DapVersion dap_version;
+ uint32_t dap_counter;
+ uint32_t cdc_baudrate;
+ uint32_t cdc_tx_counter;
+ uint32_t cdc_rx_counter;
+} DapState;
+
+typedef enum {
+ DapSwdPinsPA7PA6, // Pins 2, 3
+ DapSwdPinsPA14PA13, // Pins 10, 12
+} DapSwdPins;
+
+typedef enum {
+ DapUartTypeUSART1, // Pins 13, 14
+ DapUartTypeLPUART1, // Pins 15, 16
+} DapUartType;
+
+typedef enum {
+ DapUartTXRXNormal,
+ DapUartTXRXSwap,
+} DapUartTXRX;
+
+typedef struct {
+ DapSwdPins swd_pins;
+ DapUartType uart_pins;
+ DapUartTXRX uart_swap;
+} DapConfig;
+
+typedef struct DapApp DapApp;
+
+void dap_app_get_state(DapApp* app, DapState* state);
+
+const char* dap_app_get_serial(DapApp* app);
+
+void dap_app_set_config(DapApp* app, DapConfig* config);
+
+DapConfig* dap_app_get_config(DapApp* app);
\ No newline at end of file
diff --git a/applications/external/dap_link/gui/dap_gui.c b/applications/external/dap_link/gui/dap_gui.c
new file mode 100644
index 000000000..4dd986153
--- /dev/null
+++ b/applications/external/dap_link/gui/dap_gui.c
@@ -0,0 +1,92 @@
+#include "dap_gui.h"
+#include "dap_gui_i.h"
+
+#define DAP_GUI_TICK 250
+
+static bool dap_gui_custom_event_callback(void* context, uint32_t event) {
+ furi_assert(context);
+ DapGuiApp* app = context;
+ return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+static bool dap_gui_back_event_callback(void* context) {
+ furi_assert(context);
+ DapGuiApp* app = context;
+ return scene_manager_handle_back_event(app->scene_manager);
+}
+
+static void dap_gui_tick_event_callback(void* context) {
+ furi_assert(context);
+ DapGuiApp* app = context;
+ scene_manager_handle_tick_event(app->scene_manager);
+}
+
+DapGuiApp* dap_gui_alloc() {
+ DapGuiApp* app = malloc(sizeof(DapGuiApp));
+ app->gui = furi_record_open(RECORD_GUI);
+ app->view_dispatcher = view_dispatcher_alloc();
+ app->scene_manager = scene_manager_alloc(&dap_scene_handlers, app);
+ view_dispatcher_enable_queue(app->view_dispatcher);
+ view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+
+ view_dispatcher_set_custom_event_callback(app->view_dispatcher, dap_gui_custom_event_callback);
+ view_dispatcher_set_navigation_event_callback(
+ app->view_dispatcher, dap_gui_back_event_callback);
+ view_dispatcher_set_tick_event_callback(
+ app->view_dispatcher, dap_gui_tick_event_callback, DAP_GUI_TICK);
+
+ view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+ app->notifications = furi_record_open(RECORD_NOTIFICATION);
+
+ app->var_item_list = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ DapGuiAppViewVarItemList,
+ variable_item_list_get_view(app->var_item_list));
+
+ app->main_view = dap_main_view_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, DapGuiAppViewMainView, dap_main_view_get_view(app->main_view));
+
+ app->widget = widget_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, DapGuiAppViewWidget, widget_get_view(app->widget));
+
+ scene_manager_next_scene(app->scene_manager, DapSceneMain);
+
+ return app;
+}
+
+void dap_gui_free(DapGuiApp* app) {
+ view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewVarItemList);
+ variable_item_list_free(app->var_item_list);
+
+ view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewMainView);
+ dap_main_view_free(app->main_view);
+
+ view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewWidget);
+ widget_free(app->widget);
+
+ // View dispatcher
+ view_dispatcher_free(app->view_dispatcher);
+ scene_manager_free(app->scene_manager);
+
+ // Close records
+ furi_record_close(RECORD_GUI);
+ furi_record_close(RECORD_NOTIFICATION);
+
+ free(app);
+}
+
+int32_t dap_gui_thread(void* arg) {
+ DapGuiApp* app = dap_gui_alloc();
+ app->dap_app = arg;
+
+ notification_message_block(app->notifications, &sequence_display_backlight_enforce_on);
+ view_dispatcher_run(app->view_dispatcher);
+ notification_message_block(app->notifications, &sequence_display_backlight_enforce_auto);
+
+ dap_gui_free(app);
+ return 0;
+}
\ No newline at end of file
diff --git a/applications/external/dap_link/gui/dap_gui.h b/applications/external/dap_link/gui/dap_gui.h
new file mode 100644
index 000000000..3d8e6bdf9
--- /dev/null
+++ b/applications/external/dap_link/gui/dap_gui.h
@@ -0,0 +1,4 @@
+#pragma once
+#include
+
+int32_t dap_gui_thread(void* arg);
\ No newline at end of file
diff --git a/applications/external/dap_link/gui/dap_gui_custom_event.h b/applications/external/dap_link/gui/dap_gui_custom_event.h
new file mode 100644
index 000000000..8b127c9d4
--- /dev/null
+++ b/applications/external/dap_link/gui/dap_gui_custom_event.h
@@ -0,0 +1,7 @@
+#pragma once
+
+typedef enum {
+ DapAppCustomEventConfig,
+ DapAppCustomEventHelp,
+ DapAppCustomEventAbout,
+} DapAppCustomEvent;
diff --git a/applications/external/dap_link/gui/dap_gui_i.h b/applications/external/dap_link/gui/dap_gui_i.h
new file mode 100644
index 000000000..59411e78c
--- /dev/null
+++ b/applications/external/dap_link/gui/dap_gui_i.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "dap_gui.h"
+#include "../dap_link.h"
+#include "scenes/config/dap_scene.h"
+#include "dap_gui_custom_event.h"
+#include "views/dap_main_view.h"
+
+typedef struct {
+ DapApp* dap_app;
+
+ Gui* gui;
+ NotificationApp* notifications;
+ ViewDispatcher* view_dispatcher;
+ SceneManager* scene_manager;
+
+ VariableItemList* var_item_list;
+ DapMainView* main_view;
+ Widget* widget;
+} DapGuiApp;
+
+typedef enum {
+ DapGuiAppViewVarItemList,
+ DapGuiAppViewMainView,
+ DapGuiAppViewWidget,
+} DapGuiAppView;
diff --git a/applications/external/dap_link/gui/scenes/config/dap_scene_config.h b/applications/external/dap_link/gui/scenes/config/dap_scene_config.h
new file mode 100644
index 000000000..8957aca06
--- /dev/null
+++ b/applications/external/dap_link/gui/scenes/config/dap_scene_config.h
@@ -0,0 +1,4 @@
+ADD_SCENE(dap, main, Main)
+ADD_SCENE(dap, config, Config)
+ADD_SCENE(dap, help, Help)
+ADD_SCENE(dap, about, About)
\ No newline at end of file
diff --git a/applications/external/dap_link/gui/scenes/dap_scene_config.c b/applications/external/dap_link/gui/scenes/dap_scene_config.c
new file mode 100644
index 000000000..48d5fedcd
--- /dev/null
+++ b/applications/external/dap_link/gui/scenes/dap_scene_config.c
@@ -0,0 +1,107 @@
+#include "../dap_gui_i.h"
+
+static const char* swd_pins[] = {[DapSwdPinsPA7PA6] = "2,3", [DapSwdPinsPA14PA13] = "10,12"};
+static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"};
+static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"};
+
+static void swd_pins_cb(VariableItem* item) {
+ DapGuiApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+
+ variable_item_set_current_value_text(item, swd_pins[index]);
+
+ DapConfig* config = dap_app_get_config(app->dap_app);
+ config->swd_pins = index;
+ dap_app_set_config(app->dap_app, config);
+}
+
+static void uart_pins_cb(VariableItem* item) {
+ DapGuiApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+
+ variable_item_set_current_value_text(item, uart_pins[index]);
+
+ DapConfig* config = dap_app_get_config(app->dap_app);
+ config->uart_pins = index;
+ dap_app_set_config(app->dap_app, config);
+}
+
+static void uart_swap_cb(VariableItem* item) {
+ DapGuiApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+
+ variable_item_set_current_value_text(item, uart_swap[index]);
+
+ DapConfig* config = dap_app_get_config(app->dap_app);
+ config->uart_swap = index;
+ dap_app_set_config(app->dap_app, config);
+}
+
+static void ok_cb(void* context, uint32_t index) {
+ DapGuiApp* app = context;
+ switch(index) {
+ case 3:
+ view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventHelp);
+ break;
+ case 4:
+ view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventAbout);
+ break;
+ default:
+ break;
+ }
+}
+
+void dap_scene_config_on_enter(void* context) {
+ DapGuiApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+ DapConfig* config = dap_app_get_config(app->dap_app);
+
+ item = variable_item_list_add(
+ var_item_list, "SWC SWD Pins", COUNT_OF(swd_pins), swd_pins_cb, app);
+ variable_item_set_current_value_index(item, config->swd_pins);
+ variable_item_set_current_value_text(item, swd_pins[config->swd_pins]);
+
+ item =
+ variable_item_list_add(var_item_list, "UART Pins", COUNT_OF(uart_pins), uart_pins_cb, app);
+ variable_item_set_current_value_index(item, config->uart_pins);
+ variable_item_set_current_value_text(item, uart_pins[config->uart_pins]);
+
+ item = variable_item_list_add(
+ var_item_list, "Swap TX RX", COUNT_OF(uart_swap), uart_swap_cb, app);
+ variable_item_set_current_value_index(item, config->uart_swap);
+ variable_item_set_current_value_text(item, uart_swap[config->uart_swap]);
+
+ variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL);
+ variable_item_list_add(var_item_list, "About", 0, NULL, NULL);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig));
+
+ variable_item_list_set_enter_callback(var_item_list, ok_cb, app);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewVarItemList);
+}
+
+bool dap_scene_config_on_event(void* context, SceneManagerEvent event) {
+ DapGuiApp* app = context;
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == DapAppCustomEventHelp) {
+ scene_manager_next_scene(app->scene_manager, DapSceneHelp);
+ return true;
+ } else if(event.event == DapAppCustomEventAbout) {
+ scene_manager_next_scene(app->scene_manager, DapSceneAbout);
+ return true;
+ }
+ }
+ return false;
+}
+
+void dap_scene_config_on_exit(void* context) {
+ DapGuiApp* app = context;
+ scene_manager_set_scene_state(
+ app->scene_manager,
+ DapSceneConfig,
+ variable_item_list_get_selected_item_index(app->var_item_list));
+ variable_item_list_reset(app->var_item_list);
+}
\ No newline at end of file
diff --git a/applications/external/dap_link/gui/scenes/dap_scene_help.c b/applications/external/dap_link/gui/scenes/dap_scene_help.c
new file mode 100644
index 000000000..d8d70e7ff
--- /dev/null
+++ b/applications/external/dap_link/gui/scenes/dap_scene_help.c
@@ -0,0 +1,102 @@
+#include "../dap_gui_i.h"
+
+void dap_scene_help_on_enter(void* context) {
+ DapGuiApp* app = context;
+ DapConfig* config = dap_app_get_config(app->dap_app);
+ FuriString* string = furi_string_alloc();
+
+ furi_string_cat(string, "CMSIS DAP/DAP Link v2\r\n");
+ furi_string_cat_printf(string, "Serial: %s\r\n", dap_app_get_serial(app->dap_app));
+ furi_string_cat(
+ string,
+ "Pinout:\r\n"
+ "\e#SWD:\r\n");
+
+ switch(config->swd_pins) {
+ case DapSwdPinsPA7PA6:
+ furi_string_cat(
+ string,
+ " SWC: 2 [A7]\r\n"
+ " SWD: 3 [A6]\r\n");
+ break;
+ case DapSwdPinsPA14PA13:
+ furi_string_cat(
+ string,
+ " SWC: 10 [SWC]\r\n"
+ " SWD: 12 [SIO]\r\n");
+ break;
+ default:
+ break;
+ }
+
+ furi_string_cat(string, "\e#JTAG:\r\n");
+ switch(config->swd_pins) {
+ case DapSwdPinsPA7PA6:
+ furi_string_cat(
+ string,
+ " TCK: 2 [A7]\r\n"
+ " TMS: 3 [A6]\r\n"
+ " RST: 4 [A4]\r\n"
+ " TDO: 5 [B3]\r\n"
+ " TDI: 6 [B2]\r\n");
+ break;
+ case DapSwdPinsPA14PA13:
+ furi_string_cat(
+ string,
+ " RST: 4 [A4]\r\n"
+ " TDO: 5 [B3]\r\n"
+ " TDI: 6 [B2]\r\n"
+ " TCK: 10 [SWC]\r\n"
+ " TMS: 12 [SIO]\r\n");
+ break;
+ default:
+ break;
+ }
+
+ furi_string_cat(string, "\e#UART:\r\n");
+ switch(config->uart_pins) {
+ case DapUartTypeUSART1:
+ if(config->uart_swap == DapUartTXRXNormal) {
+ furi_string_cat(
+ string,
+ " TX: 13 [TX]\r\n"
+ " RX: 14 [RX]\r\n");
+ } else {
+ furi_string_cat(
+ string,
+ " RX: 13 [TX]\r\n"
+ " TX: 14 [RX]\r\n");
+ }
+ break;
+ case DapUartTypeLPUART1:
+ if(config->uart_swap == DapUartTXRXNormal) {
+ furi_string_cat(
+ string,
+ " TX: 15 [C1]\r\n"
+ " RX: 16 [C0]\r\n");
+ } else {
+ furi_string_cat(
+ string,
+ " RX: 15 [C1]\r\n"
+ " TX: 16 [C0]\r\n");
+ }
+ break;
+ default:
+ break;
+ }
+
+ widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(string));
+ furi_string_free(string);
+ view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget);
+}
+
+bool dap_scene_help_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void dap_scene_help_on_exit(void* context) {
+ DapGuiApp* app = context;
+ widget_reset(app->widget);
+}
\ No newline at end of file
diff --git a/applications/external/dap_link/gui/scenes/dap_scene_main.c b/applications/external/dap_link/gui/scenes/dap_scene_main.c
new file mode 100644
index 000000000..8c19bd6a5
--- /dev/null
+++ b/applications/external/dap_link/gui/scenes/dap_scene_main.c
@@ -0,0 +1,154 @@
+#include "../dap_gui_i.h"
+#include "../../dap_link.h"
+
+typedef struct {
+ DapState dap_state;
+ bool dap_active;
+ bool tx_active;
+ bool rx_active;
+} DapSceneMainState;
+
+static bool process_dap_state(DapGuiApp* app) {
+ DapSceneMainState* state =
+ (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain);
+ if(state == NULL) return true;
+
+ DapState* prev_state = &state->dap_state;
+ DapState next_state;
+ dap_app_get_state(app->dap_app, &next_state);
+ bool need_to_update = false;
+
+ if(prev_state->dap_mode != next_state.dap_mode) {
+ switch(next_state.dap_mode) {
+ case DapModeDisconnected:
+ dap_main_view_set_mode(app->main_view, DapMainViewModeDisconnected);
+ notification_message(app->notifications, &sequence_blink_stop);
+ break;
+ case DapModeSWD:
+ dap_main_view_set_mode(app->main_view, DapMainViewModeSWD);
+ notification_message(app->notifications, &sequence_blink_start_blue);
+ break;
+ case DapModeJTAG:
+ dap_main_view_set_mode(app->main_view, DapMainViewModeJTAG);
+ notification_message(app->notifications, &sequence_blink_start_magenta);
+ break;
+ }
+ need_to_update = true;
+ }
+
+ if(prev_state->dap_version != next_state.dap_version) {
+ switch(next_state.dap_version) {
+ case DapVersionUnknown:
+ dap_main_view_set_version(app->main_view, DapMainViewVersionUnknown);
+ break;
+ case DapVersionV1:
+ dap_main_view_set_version(app->main_view, DapMainViewVersionV1);
+ break;
+ case DapVersionV2:
+ dap_main_view_set_version(app->main_view, DapMainViewVersionV2);
+ break;
+ }
+ need_to_update = true;
+ }
+
+ if(prev_state->usb_connected != next_state.usb_connected) {
+ dap_main_view_set_usb_connected(app->main_view, next_state.usb_connected);
+ need_to_update = true;
+ }
+
+ if(prev_state->dap_counter != next_state.dap_counter) {
+ if(!state->dap_active) {
+ state->dap_active = true;
+ dap_main_view_set_dap(app->main_view, state->dap_active);
+ need_to_update = true;
+ }
+ } else {
+ if(state->dap_active) {
+ state->dap_active = false;
+ dap_main_view_set_dap(app->main_view, state->dap_active);
+ need_to_update = true;
+ }
+ }
+
+ if(prev_state->cdc_baudrate != next_state.cdc_baudrate) {
+ dap_main_view_set_baudrate(app->main_view, next_state.cdc_baudrate);
+ need_to_update = true;
+ }
+
+ if(prev_state->cdc_tx_counter != next_state.cdc_tx_counter) {
+ if(!state->tx_active) {
+ state->tx_active = true;
+ dap_main_view_set_tx(app->main_view, state->tx_active);
+ need_to_update = true;
+ notification_message(app->notifications, &sequence_blink_start_red);
+ }
+ } else {
+ if(state->tx_active) {
+ state->tx_active = false;
+ dap_main_view_set_tx(app->main_view, state->tx_active);
+ need_to_update = true;
+ notification_message(app->notifications, &sequence_blink_stop);
+ }
+ }
+
+ if(prev_state->cdc_rx_counter != next_state.cdc_rx_counter) {
+ if(!state->rx_active) {
+ state->rx_active = true;
+ dap_main_view_set_rx(app->main_view, state->rx_active);
+ need_to_update = true;
+ notification_message(app->notifications, &sequence_blink_start_green);
+ }
+ } else {
+ if(state->rx_active) {
+ state->rx_active = false;
+ dap_main_view_set_rx(app->main_view, state->rx_active);
+ need_to_update = true;
+ notification_message(app->notifications, &sequence_blink_stop);
+ }
+ }
+
+ if(need_to_update) {
+ dap_main_view_update(app->main_view);
+ }
+
+ *prev_state = next_state;
+ return true;
+}
+
+static void dap_scene_main_on_left(void* context) {
+ DapGuiApp* app = (DapGuiApp*)context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventConfig);
+}
+
+void dap_scene_main_on_enter(void* context) {
+ DapGuiApp* app = context;
+ DapSceneMainState* state = malloc(sizeof(DapSceneMainState));
+ dap_main_view_set_left_callback(app->main_view, dap_scene_main_on_left, app);
+ view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewMainView);
+ scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)state);
+}
+
+bool dap_scene_main_on_event(void* context, SceneManagerEvent event) {
+ DapGuiApp* app = context;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == DapAppCustomEventConfig) {
+ scene_manager_next_scene(app->scene_manager, DapSceneConfig);
+ return true;
+ }
+ } else if(event.type == SceneManagerEventTypeTick) {
+ return process_dap_state(app);
+ }
+
+ return false;
+}
+
+void dap_scene_main_on_exit(void* context) {
+ DapGuiApp* app = context;
+ DapSceneMainState* state =
+ (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain);
+ scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)NULL);
+ FURI_SW_MEMBARRIER();
+ free(state);
+ notification_message(app->notifications, &sequence_blink_stop);
+}
\ No newline at end of file
diff --git a/applications/external/dap_link/gui/views/dap_main_view.c b/applications/external/dap_link/gui/views/dap_main_view.c
new file mode 100644
index 000000000..c5c8f9dff
--- /dev/null
+++ b/applications/external/dap_link/gui/views/dap_main_view.c
@@ -0,0 +1,189 @@
+#include "dap_main_view.h"
+#include "dap_link_icons.h"
+#include
+
+// extern const Icon I_ArrowDownEmpty_12x18;
+// extern const Icon I_ArrowDownFilled_12x18;
+// extern const Icon I_ArrowUpEmpty_12x18;
+// extern const Icon I_ArrowUpFilled_12x18;
+
+struct DapMainView {
+ View* view;
+ DapMainViewButtonCallback cb_left;
+ void* cb_context;
+};
+
+typedef struct {
+ DapMainViewMode mode;
+ DapMainViewVersion version;
+ bool usb_connected;
+ uint32_t baudrate;
+ bool dap_active;
+ bool tx_active;
+ bool rx_active;
+} DapMainViewModel;
+
+static void dap_main_view_draw_callback(Canvas* canvas, void* _model) {
+ DapMainViewModel* model = _model;
+ UNUSED(model);
+ canvas_clear(canvas);
+ elements_button_left(canvas, "Config");
+
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_box(canvas, 0, 0, 127, 11);
+ canvas_set_color(canvas, ColorWhite);
+
+ const char* header_string;
+ if(model->usb_connected) {
+ if(model->version == DapMainViewVersionV1) {
+ header_string = "DAP Link V1 Connected";
+ } else if(model->version == DapMainViewVersionV2) {
+ header_string = "DAP Link V2 Connected";
+ } else {
+ header_string = "DAP Link Connected";
+ }
+ } else {
+ header_string = "DAP Link";
+ }
+
+ canvas_draw_str_aligned(canvas, 64, 9, AlignCenter, AlignBottom, header_string);
+
+ canvas_set_color(canvas, ColorBlack);
+ if(model->dap_active) {
+ canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18);
+ canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18);
+ } else {
+ canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18);
+ canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18);
+ }
+
+ switch(model->mode) {
+ case DapMainViewModeDisconnected:
+ canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "----");
+ break;
+ case DapMainViewModeSWD:
+ canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "SWD");
+ break;
+ case DapMainViewModeJTAG:
+ canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "JTAG");
+ break;
+ }
+
+ if(model->tx_active) {
+ canvas_draw_icon(canvas, 87, 16, &I_ArrowUpFilled_12x18);
+ } else {
+ canvas_draw_icon(canvas, 87, 16, &I_ArrowUpEmpty_12x18);
+ }
+
+ if(model->rx_active) {
+ canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18);
+ } else {
+ canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18);
+ }
+
+ canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART");
+
+ canvas_draw_line(canvas, 44, 52, 123, 52);
+ if(model->baudrate == 0) {
+ canvas_draw_str(canvas, 45, 62, "Baud: ????");
+ } else {
+ char baudrate_str[18];
+ snprintf(baudrate_str, 18, "Baud: %lu", model->baudrate);
+ canvas_draw_str(canvas, 45, 62, baudrate_str);
+ }
+}
+
+static bool dap_main_view_input_callback(InputEvent* event, void* context) {
+ furi_assert(context);
+ DapMainView* dap_main_view = context;
+ bool consumed = false;
+
+ if(event->type == InputTypeShort) {
+ if(event->key == InputKeyLeft) {
+ if(dap_main_view->cb_left) {
+ dap_main_view->cb_left(dap_main_view->cb_context);
+ }
+ consumed = true;
+ }
+ }
+
+ return consumed;
+}
+
+DapMainView* dap_main_view_alloc() {
+ DapMainView* dap_main_view = malloc(sizeof(DapMainView));
+
+ dap_main_view->view = view_alloc();
+ view_allocate_model(dap_main_view->view, ViewModelTypeLocking, sizeof(DapMainViewModel));
+ view_set_context(dap_main_view->view, dap_main_view);
+ view_set_draw_callback(dap_main_view->view, dap_main_view_draw_callback);
+ view_set_input_callback(dap_main_view->view, dap_main_view_input_callback);
+ return dap_main_view;
+}
+
+void dap_main_view_free(DapMainView* dap_main_view) {
+ view_free(dap_main_view->view);
+ free(dap_main_view);
+}
+
+View* dap_main_view_get_view(DapMainView* dap_main_view) {
+ return dap_main_view->view;
+}
+
+void dap_main_view_set_left_callback(
+ DapMainView* dap_main_view,
+ DapMainViewButtonCallback callback,
+ void* context) {
+ with_view_model(
+ dap_main_view->view,
+ DapMainViewModel * model,
+ {
+ UNUSED(model);
+ dap_main_view->cb_left = callback;
+ dap_main_view->cb_context = context;
+ },
+ true);
+}
+
+void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode) {
+ with_view_model(
+ dap_main_view->view, DapMainViewModel * model, { model->mode = mode; }, false);
+}
+
+void dap_main_view_set_dap(DapMainView* dap_main_view, bool active) {
+ with_view_model(
+ dap_main_view->view, DapMainViewModel * model, { model->dap_active = active; }, false);
+}
+
+void dap_main_view_set_tx(DapMainView* dap_main_view, bool active) {
+ with_view_model(
+ dap_main_view->view, DapMainViewModel * model, { model->tx_active = active; }, false);
+}
+
+void dap_main_view_set_rx(DapMainView* dap_main_view, bool active) {
+ with_view_model(
+ dap_main_view->view, DapMainViewModel * model, { model->rx_active = active; }, false);
+}
+
+void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate) {
+ with_view_model(
+ dap_main_view->view, DapMainViewModel * model, { model->baudrate = baudrate; }, false);
+}
+
+void dap_main_view_update(DapMainView* dap_main_view) {
+ with_view_model(
+ dap_main_view->view, DapMainViewModel * model, { UNUSED(model); }, true);
+}
+
+void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version) {
+ with_view_model(
+ dap_main_view->view, DapMainViewModel * model, { model->version = version; }, false);
+}
+
+void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected) {
+ with_view_model(
+ dap_main_view->view,
+ DapMainViewModel * model,
+ { model->usb_connected = connected; },
+ false);
+}
\ No newline at end of file
diff --git a/applications/external/dap_link/gui/views/dap_main_view.h b/applications/external/dap_link/gui/views/dap_main_view.h
new file mode 100644
index 000000000..1fd900452
--- /dev/null
+++ b/applications/external/dap_link/gui/views/dap_main_view.h
@@ -0,0 +1,45 @@
+#pragma once
+#include
+
+typedef struct DapMainView DapMainView;
+
+typedef void (*DapMainViewButtonCallback)(void* context);
+
+typedef enum {
+ DapMainViewVersionUnknown,
+ DapMainViewVersionV1,
+ DapMainViewVersionV2,
+} DapMainViewVersion;
+
+typedef enum {
+ DapMainViewModeDisconnected,
+ DapMainViewModeSWD,
+ DapMainViewModeJTAG,
+} DapMainViewMode;
+
+DapMainView* dap_main_view_alloc();
+
+void dap_main_view_free(DapMainView* dap_main_view);
+
+View* dap_main_view_get_view(DapMainView* dap_main_view);
+
+void dap_main_view_set_left_callback(
+ DapMainView* dap_main_view,
+ DapMainViewButtonCallback callback,
+ void* context);
+
+void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode);
+
+void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version);
+
+void dap_main_view_set_dap(DapMainView* dap_main_view, bool active);
+
+void dap_main_view_set_tx(DapMainView* dap_main_view, bool active);
+
+void dap_main_view_set_rx(DapMainView* dap_main_view, bool active);
+
+void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected);
+
+void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate);
+
+void dap_main_view_update(DapMainView* dap_main_view);
\ No newline at end of file
diff --git a/applications/external/dap_link/icons/ActiveConnection_50x64.png b/applications/external/dap_link/icons/ActiveConnection_50x64.png
new file mode 100644
index 0000000000000000000000000000000000000000..1d7686dddf8a33b724c7528ed36435514b7518b2
GIT binary patch
literal 3842
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
WwRe~lwXYaxC4;A{pUXO@geCw-ib4GV
literal 0
HcmV?d00001
diff --git a/applications/external/hid_app/application.fam b/applications/external/hid_app/application.fam
index a9d8305dd..e96e956d8 100644
--- a/applications/external/hid_app/application.fam
+++ b/applications/external/hid_app/application.fam
@@ -1,10 +1,10 @@
App(
appid="hid_usb",
- name="Remote",
+ name="USB Keyboard & Mouse",
apptype=FlipperAppType.EXTERNAL,
entry_point="hid_usb_app",
stack_size=1 * 1024,
- fap_category="USB",
+ fap_category="Misc",
fap_icon="hid_usb_10px.png",
fap_icon_assets="assets",
fap_icon_assets_symbol="hid",
@@ -13,11 +13,11 @@ App(
App(
appid="hid_ble",
- name="Remote",
+ name="Bluetooth Remote",
apptype=FlipperAppType.EXTERNAL,
entry_point="hid_ble_app",
stack_size=1 * 1024,
- fap_category="Bluetooth",
+ fap_category="Misc",
fap_icon="hid_ble_10px.png",
fap_icon_assets="assets",
fap_icon_assets_symbol="hid",
diff --git a/applications/external/music_player/application.fam b/applications/external/music_player/application.fam
index 3414c0a48..e0254250e 100644
--- a/applications/external/music_player/application.fam
+++ b/applications/external/music_player/application.fam
@@ -1,5 +1,5 @@
App(
- appid="music_player",
+ appid="Music_Player",
name="Music Player",
apptype=FlipperAppType.EXTERNAL,
entry_point="music_player_app",
diff --git a/applications/external/signal_generator/application.fam b/applications/external/signal_generator/application.fam
index 094e784cc..bcf7d20b6 100644
--- a/applications/external/signal_generator/application.fam
+++ b/applications/external/signal_generator/application.fam
@@ -1,5 +1,5 @@
App(
- appid="signal_generator",
+ appid="Signal_Generator",
name="Signal Generator",
apptype=FlipperAppType.EXTERNAL,
entry_point="signal_gen_app",