Merge remote-tracking branch 'upstream/dev' into shutdown_idle

# Conflicts:
#	firmware/targets/f7/api_symbols.csv
This commit is contained in:
SHxKenzuto
2022-10-21 23:16:29 +02:00
332 changed files with 10688 additions and 1037 deletions

View File

@@ -62,6 +62,8 @@ jobs:
- name: 'Download build artifacts' - name: 'Download build artifacts'
run: | run: |
mkdir -p ~/.ssh
ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
chmod 600 ./deploy_key; chmod 600 ./deploy_key;
rsync -avzP \ rsync -avzP \
@@ -97,3 +99,4 @@ jobs:
${{ secrets.AMAP_MARIADB_PORT }} \ ${{ secrets.AMAP_MARIADB_PORT }} \
${{ secrets.AMAP_MARIADB_DATABASE }} \ ${{ secrets.AMAP_MARIADB_DATABASE }} \
artifacts/flipper-z-f7-firmware-$SUFFIX.elf.map.all artifacts/flipper-z-f7-firmware-$SUFFIX.elf.map.all

View File

@@ -56,14 +56,14 @@ jobs:
- name: 'Bundle scripts' - name: 'Bundle scripts'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
run: | run: |
tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug
- name: 'Build the firmware' - name: 'Build the firmware'
run: | run: |
set -e set -e
for TARGET in ${TARGETS}; do for TARGET in ${TARGETS}; do
FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
done done
- name: 'Move upload files' - name: 'Move upload files'
@@ -74,17 +74,6 @@ jobs:
mv dist/${TARGET}-*/* artifacts/ mv dist/${TARGET}-*/* artifacts/
done done
- name: 'Bundle self-update package'
if: ${{ !github.event.pull_request.head.repo.fork }}
run: |
set -e
for UPDATEBUNDLE in artifacts/*/; do
BUNDLE_NAME="$(echo "$UPDATEBUNDLE" | cut -d'/' -f2)"
echo Packaging "${BUNDLE_NAME}"
tar czpf "artifacts/flipper-z-${BUNDLE_NAME}.tgz" -C artifacts "${BUNDLE_NAME}"
rm -rf "artifacts/${BUNDLE_NAME}"
done
- name: "Check for uncommitted changes" - name: "Check for uncommitted changes"
run: | run: |
git diff --exit-code git diff --exit-code
@@ -97,8 +86,7 @@ jobs:
- name: 'Bundle core2 firmware' - name: 'Bundle core2 firmware'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
run: | run: |
FBT_TOOLCHAIN_PATH=/opt ./fbt copro_dist cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz"
tar czpf "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" -C assets core2_firmware
- name: 'Copy .map file' - name: 'Copy .map file'
run: | run: |
@@ -107,6 +95,8 @@ jobs:
- name: 'Upload artifacts to update server' - name: 'Upload artifacts to update server'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
run: | run: |
mkdir -p ~/.ssh
ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
chmod 600 ./deploy_key; chmod 600 ./deploy_key;
rsync -avzP --delete --mkpath \ rsync -avzP --delete --mkpath \
@@ -137,7 +127,7 @@ jobs:
**Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:**
- [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz) - [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz)
- [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-full-${{steps.names.outputs.suffix}}.dfu) - [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-full-${{steps.names.outputs.suffix}}.dfu)
- [☁️ Web updater](https://my.flipp.dev/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}}) - [☁️ Web/App updater](https://my.flipp.dev/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}})
edit-mode: replace edit-mode: replace
compact: compact:
@@ -174,6 +164,6 @@ jobs:
run: | run: |
set -e set -e
for TARGET in ${TARGETS}; do for TARGET in ${TARGETS}; do
FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
updater_package DEBUG=0 COMPACT=1 updater_package DEBUG=0 COMPACT=1
done done

View File

@@ -27,6 +27,7 @@ jobs:
- name: 'Check protobuf branch' - name: 'Check protobuf branch'
run: | run: |
git submodule update --init
SUB_PATH="assets/protobuf"; SUB_PATH="assets/protobuf";
SUB_BRANCH="dev"; SUB_BRANCH="dev";
SUB_COMMITS_MIN=40; SUB_COMMITS_MIN=40;

View File

@@ -30,7 +30,7 @@ jobs:
- name: 'Check code formatting' - name: 'Check code formatting'
id: syntax_check id: syntax_check
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/runner/_work ./fbt lint
- name: Report code formatting errors - name: Report code formatting errors
if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request

View File

@@ -26,4 +26,4 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: 'Check code formatting' - name: 'Check code formatting'
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint_py run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/runner/_work ./fbt lint_py

View File

@@ -57,11 +57,11 @@ jobs:
- name: 'Generate compile_comands.json' - name: 'Generate compile_comands.json'
run: | run: |
FBT_TOOLCHAIN_PATH=/opt ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking
- name: 'Static code analysis' - name: 'Static code analysis'
run: | run: |
FBT_TOOLCHAIN_PATH=/opt source scripts/toolchain/fbtenv.sh FBT_TOOLCHAIN_PATH=/runner/_work source scripts/toolchain/fbtenv.sh
pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }}
pvs-studio-analyzer analyze \ pvs-studio-analyzer analyze \
@.pvsoptions \ @.pvsoptions \
@@ -76,6 +76,8 @@ jobs:
- name: 'Upload artifacts to update server' - name: 'Upload artifacts to update server'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
run: | run: |
mkdir -p ~/.ssh
ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
chmod 600 ./deploy_key; chmod 600 ./deploy_key;
rsync -avrzP --mkpath \ rsync -avrzP --mkpath \

3
.gitmodules vendored
View File

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

View File

@@ -7,7 +7,6 @@
# construction of certain targets behind command-line options. # construction of certain targets behind command-line options.
import os import os
import subprocess
DefaultEnvironment(tools=[]) DefaultEnvironment(tools=[])
@@ -15,17 +14,22 @@ EnsurePythonVersion(3, 8)
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15) # Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
# This environment is created only for loading options & validating file/dir existence # This environment is created only for loading options & validating file/dir existence
fbt_variables = SConscript("site_scons/commandline.scons") fbt_variables = SConscript("site_scons/commandline.scons")
cmd_environment = Environment(tools=[], variables=fbt_variables) cmd_environment = Environment(
Help(fbt_variables.GenerateHelpText(cmd_environment)) toolpath=["#/scripts/fbt_tools"],
tools=[
("fbt_help", {"vars": fbt_variables}),
],
variables=fbt_variables,
)
# Building basic environment - tools, utility methods, cross-compilation # Building basic environment - tools, utility methods, cross-compilation
# settings, gcc flags for Cortex-M4, basic builders and more # settings, gcc flags for Cortex-M4, basic builders and more
coreenv = SConscript( coreenv = SConscript(
"site_scons/environ.scons", "site_scons/environ.scons",
exports={"VAR_ENV": cmd_environment}, exports={"VAR_ENV": cmd_environment},
toolpath=["#/scripts/fbt_tools"],
) )
SConscript("site_scons/cc.scons", exports={"ENV": coreenv}) SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
@@ -35,41 +39,13 @@ coreenv["ROOT_DIR"] = Dir(".")
# Create a separate "dist" environment and add construction envs to it # Create a separate "dist" environment and add construction envs to it
distenv = coreenv.Clone( distenv = coreenv.Clone(
tools=["fbt_dist", "openocd", "blackmagic", "jflash"], tools=[
OPENOCD_GDB_PIPE=[ "fbt_dist",
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" "fbt_debugopts",
"openocd",
"blackmagic",
"jflash",
], ],
GDBOPTS_BASE=[
"-ex",
"target extended-remote ${GDBREMOTE}",
"-ex",
"set confirm off",
"-ex",
"set pagination off",
],
GDBOPTS_BLACKMAGIC=[
"-ex",
"monitor swdp_scan",
"-ex",
"monitor debug_bmp enable",
"-ex",
"attach 1",
"-ex",
"set mem inaccessible-by-default off",
],
GDBPYOPTS=[
"-ex",
"source debug/FreeRTOS/FreeRTOS.py",
"-ex",
"source debug/flipperapps.py",
"-ex",
"source debug/PyCortexMDebug/PyCortexMDebug.py",
"-ex",
"svd_load ${SVD_FILE}",
"-ex",
"compare-sections",
],
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
ENV=os.environ, ENV=os.environ,
) )
@@ -166,7 +142,7 @@ basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
distenv.Default(basic_dist) distenv.Default(basic_dist)
dist_dir = distenv.GetProjetDirName() dist_dir = distenv.GetProjetDirName()
plugin_dist = [ fap_dist = [
distenv.Install( distenv.Install(
f"#/dist/{dist_dir}/apps/debug_elf", f"#/dist/{dist_dir}/apps/debug_elf",
firmware_env["FW_EXTAPPS"]["debug"].values(), firmware_env["FW_EXTAPPS"]["debug"].values(),
@@ -176,9 +152,9 @@ plugin_dist = [
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
), ),
] ]
Depends(plugin_dist, firmware_env["FW_EXTAPPS"]["validators"].values()) Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
Alias("plugin_dist", plugin_dist) Alias("fap_dist", fap_dist)
# distenv.Default(plugin_dist) # distenv.Default(fap_dist)
plugin_resources_dist = list( plugin_resources_dist = list(
distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1]) distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
@@ -189,9 +165,10 @@ distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist)
# Target for bundling core2 package for qFlipper # Target for bundling core2 package for qFlipper
copro_dist = distenv.CoproBuilder( copro_dist = distenv.CoproBuilder(
distenv.Dir("assets/core2_firmware"), "#/build/core2_firmware.tgz",
[], [],
) )
distenv.AlwaysBuild(copro_dist)
distenv.Alias("copro_dist", copro_dist) distenv.Alias("copro_dist", copro_dist)
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)

View File

@@ -16,6 +16,7 @@
#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/") #define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/")
#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" #define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc"
#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" #define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc"
#define NFC_TEST_DICT_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc")
static const char* nfc_test_file_type = "Flipper NFC test"; static const char* nfc_test_file_type = "Flipper NFC test";
static const uint32_t nfc_test_file_version = 1; static const uint32_t nfc_test_file_version = 1;
@@ -220,11 +221,78 @@ MU_TEST(mf_classic_dict_test) {
furi_string_free(temp_str); furi_string_free(temp_str);
} }
MU_TEST(mf_classic_dict_load_test) {
Storage* storage = furi_record_open(RECORD_STORAGE);
mu_assert(storage != NULL, "storage != NULL assert failed\r\n");
// Delete unit test dict file if exists
if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) {
mu_assert(
storage_simply_remove(storage, NFC_TEST_DICT_PATH),
"remove == true assert failed\r\n");
}
// Create unit test dict file
Stream* file_stream = file_stream_alloc(storage);
mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n");
mu_assert(
file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS),
"file_stream_open == true assert failed\r\n");
// Write unit test dict file
char key_str[] = "a0a1a2a3a4a5";
mu_assert(
stream_write_cstring(file_stream, key_str) == strlen(key_str),
"write == true assert failed\r\n");
// Close unit test dict file
mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n");
// Load unit test dict file
MfClassicDict* instance = NULL;
instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest);
mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n");
uint32_t total_keys = mf_classic_dict_get_total_keys(instance);
mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n");
// Read key
uint64_t key_ref = 0xa0a1a2a3a4a5;
uint64_t key_dut = 0;
FuriString* temp_str = furi_string_alloc();
mu_assert(
mf_classic_dict_get_next_key_str(instance, temp_str),
"get_next_key_str == true assert failed\r\n");
mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n");
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
mu_assert(
mf_classic_dict_get_next_key(instance, &key_dut),
"get_next_key == true assert failed\r\n");
mu_assert(key_dut == key_ref, "invalid key loaded\r\n");
furi_string_free(temp_str);
mf_classic_dict_free(instance);
// Check that MfClassicDict added new line to the end of the file
mu_assert(
file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING),
"file_stream_open == true assert failed\r\n");
mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n");
uint8_t last_char = 0;
mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n");
mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n");
mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n");
// Delete unit test dict file
mu_assert(
storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n");
stream_free(file_stream);
furi_record_close(RECORD_STORAGE);
}
MU_TEST_SUITE(nfc) { MU_TEST_SUITE(nfc) {
nfc_test_alloc(); nfc_test_alloc();
MU_RUN_TEST(nfc_digital_signal_test); MU_RUN_TEST(nfc_digital_signal_test);
MU_RUN_TEST(mf_classic_dict_test); MU_RUN_TEST(mf_classic_dict_test);
MU_RUN_TEST(mf_classic_dict_load_test);
nfc_test_free(); nfc_test_free();
} }

View File

@@ -5,7 +5,7 @@
#include <lib/subghz/transmitter.h> #include <lib/subghz/transmitter.h>
#include <lib/subghz/subghz_keystore.h> #include <lib/subghz/subghz_keystore.h>
#include <lib/subghz/subghz_file_encoder_worker.h> #include <lib/subghz/subghz_file_encoder_worker.h>
#include <lib/subghz/protocols/registry.h> #include <lib/subghz/protocols/protocol_items.h>
#include <flipper_format/flipper_format_i.h> #include <flipper_format/flipper_format_i.h>
#define TAG "SubGhz TEST" #define TAG "SubGhz TEST"
@@ -43,6 +43,8 @@ static void subghz_test_init(void) {
environment_handler, CAME_ATOMO_DIR_NAME); environment_handler, CAME_ATOMO_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment_handler, NICE_FLOR_S_DIR_NAME); environment_handler, NICE_FLOR_S_DIR_NAME);
subghz_environment_set_protocol_registry(
environment_handler, (void*)&subghz_protocol_registry);
receiver_handler = subghz_receiver_alloc_init(environment_handler); receiver_handler = subghz_receiver_alloc_init(environment_handler);
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
@@ -413,11 +415,11 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) {
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_magellen_test) { MU_TEST(subghz_decoder_magellan_test) {
mu_assert( mu_assert(
subghz_decoder_test( subghz_decoder_test(
EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME), EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME),
"Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_intertechno_v3_test) { MU_TEST(subghz_decoder_intertechno_v3_test) {
@@ -545,10 +547,10 @@ MU_TEST(subghz_encoder_honeywell_wdb_test) {
"Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
} }
MU_TEST(subghz_encoder_magellen_test) { MU_TEST(subghz_encoder_magellan_test) {
mu_assert( mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")), subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")),
"Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
} }
MU_TEST(subghz_encoder_intertechno_v3_test) { MU_TEST(subghz_encoder_intertechno_v3_test) {
@@ -600,7 +602,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_doitrand_test); MU_RUN_TEST(subghz_decoder_doitrand_test);
MU_RUN_TEST(subghz_decoder_phoenix_v2_test); MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
MU_RUN_TEST(subghz_decoder_magellen_test); MU_RUN_TEST(subghz_decoder_magellan_test);
MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_decoder_oregon2_test); MU_RUN_TEST(subghz_decoder_oregon2_test);
@@ -622,7 +624,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_doitrand_test); MU_RUN_TEST(subghz_encoder_doitrand_test);
MU_RUN_TEST(subghz_encoder_phoenix_v2_test); MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
MU_RUN_TEST(subghz_encoder_magellen_test); MU_RUN_TEST(subghz_encoder_magellan_test);
MU_RUN_TEST(subghz_encoder_intertechno_v3_test); MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
MU_RUN_TEST(subghz_encoder_clemsa_test); MU_RUN_TEST(subghz_encoder_clemsa_test);

View File

@@ -4,6 +4,8 @@
#include <gui/gui.h> #include <gui/gui.h>
#include <input/input.h> #include <input/input.h>
/* Magic happens here -- this file is generated by fbt.
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
#include "example_images_icons.h" #include "example_images_icons.h"
typedef struct { typedef struct {

View File

@@ -231,7 +231,8 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) {
return 0; return 0;
} }
static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) { static int32_t
ducky_parse_line(BadUsbScript* bad_usb, FuriString* line, char* error, size_t error_len) {
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);
bool state = false; bool state = false;
@@ -261,6 +262,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
if((state) && (delay_val > 0)) { if((state) && (delay_val > 0)) {
return (int32_t)delay_val; return (int32_t)delay_val;
} }
if(error != NULL) {
snprintf(error, error_len, "Invalid number %s", line_tmp);
}
return SCRIPT_STATE_ERROR; return SCRIPT_STATE_ERROR;
} else if( } else if(
(strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) || (strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
@@ -268,17 +272,26 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
// DEFAULT_DELAY // DEFAULT_DELAY
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
state = ducky_get_number(line_tmp, &bad_usb->defdelay); 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; return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
// STRING // STRING
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
state = ducky_string(line_tmp); state = ducky_string(line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid string %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR; return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) { } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
// ALTCHAR // ALTCHAR
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
ducky_numlock_on(); ducky_numlock_on();
state = ducky_altchar(line_tmp); state = ducky_altchar(line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid altchar %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR; return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if( } else if(
(strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) || (strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
@@ -287,11 +300,17 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
ducky_numlock_on(); ducky_numlock_on();
state = ducky_altstring(line_tmp); state = ducky_altstring(line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid altstring %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR; return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) { } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
// REPEAT // REPEAT
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt); 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; return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) { } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
// SYSRQ // SYSRQ
@@ -304,7 +323,12 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
} else { } else {
// Special keys + modifiers // Special keys + modifiers
uint16_t key = ducky_get_keycode(line_tmp, false); uint16_t key = ducky_get_keycode(line_tmp, false);
if(key == HID_KEYBOARD_NONE) return SCRIPT_STATE_ERROR; if(key == HID_KEYBOARD_NONE) {
if(error != NULL) {
snprintf(error, error_len, "No keycode defined for %s", line_tmp);
}
return SCRIPT_STATE_ERROR;
}
if((key & 0xFF00) != 0) { if((key & 0xFF00) != 0) {
// It's a modifier key // It's a modifier key
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
@@ -314,6 +338,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
furi_hal_hid_kb_release(key); furi_hal_hid_kb_release(key);
return (0); return (0);
} }
if(error != NULL) {
strncpy(error, "Unknown error", error_len);
}
return SCRIPT_STATE_ERROR; return SCRIPT_STATE_ERROR;
} }
@@ -392,7 +419,8 @@ 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); delay_val = ducky_parse_line(
bad_usb, bad_usb->line_prev, bad_usb->st.error, sizeof(bad_usb->st.error));
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
@@ -426,7 +454,9 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
bad_usb->st.line_cur++; bad_usb->st.line_cur++;
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;
delay_val = ducky_parse_line(bad_usb, bad_usb->line); delay_val = ducky_parse_line(
bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error));
if(delay_val < 0) { 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);
@@ -602,6 +632,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
furi_string_set(bad_usb->file_path, file_path); furi_string_set(bad_usb->file_path, file_path);
bad_usb->st.state = BadUsbStateInit; bad_usb->st.state = BadUsbStateInit;
bad_usb->st.error[0] = '\0';
bad_usb->thread = furi_thread_alloc(); bad_usb->thread = furi_thread_alloc();
furi_thread_set_name(bad_usb->thread, "BadUsbWorker"); furi_thread_set_name(bad_usb->thread, "BadUsbWorker");

View File

@@ -25,6 +25,7 @@ typedef struct {
uint16_t line_nb; uint16_t line_nb;
uint32_t delay_remain; uint32_t delay_remain;
uint16_t error_line; uint16_t error_line;
char error[64];
} BadUsbState; } BadUsbState;
BadUsbScript* bad_usb_script_open(FuriString* file_path); BadUsbScript* bad_usb_script_open(FuriString* file_path);

View File

@@ -53,6 +53,7 @@ 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);
} else if(model->state.state == BadUsbStateIdle) { } else if(model->state.state == BadUsbStateIdle) {
canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18); canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18);
canvas_set_font(canvas, FontBigNumbers); canvas_set_font(canvas, FontBigNumbers);

View File

@@ -3,6 +3,7 @@ App(
name="Applications", name="Applications",
apptype=FlipperAppType.APP, apptype=FlipperAppType.APP,
entry_point="fap_loader_app", entry_point="fap_loader_app",
cdefines=["APP_FAP_LOADER"],
requires=[ requires=[
"gui", "gui",
"storage", "storage",

View File

@@ -101,7 +101,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
} }
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
FURI_LOG_I(TAG, "FAP Loader is staring app"); FURI_LOG_I(TAG, "FAP Loader is starting app");
FuriThread* thread = flipper_application_spawn(loader->app, NULL); FuriThread* thread = flipper_application_spawn(loader->app, NULL);
furi_thread_start(thread); furi_thread_start(thread);
@@ -182,6 +182,7 @@ int32_t fap_loader_app(void* p) {
FapLoader* loader; FapLoader* loader;
if(p) { if(p) {
loader = fap_loader_alloc((const char*)p); loader = fap_loader_alloc((const char*)p);
view_dispatcher_switch_to_view(loader->view_dispatcher, 0);
fap_loader_run_selected_app(loader); fap_loader_run_selected_app(loader);
} else { } else {
loader = fap_loader_alloc(EXT_PATH("apps")); loader = fap_loader_alloc(EXT_PATH("apps"));

View File

@@ -24,6 +24,7 @@ struct GpioApp {
Widget* widget; Widget* widget;
VariableItemList* var_item_list; VariableItemList* var_item_list;
VariableItem* var_item_flow;
GpioTest* gpio_test; GpioTest* gpio_test;
GpioUsbUart* gpio_usb_uart; GpioUsbUart* gpio_usb_uart;
UsbUartBridge* usb_uart_bridge; UsbUartBridge* usb_uart_bridge;

View File

@@ -1,6 +1,7 @@
#include "../gpio_app_i.h" #include "../gpio_app_i.h"
#include "furi_hal_power.h" #include "furi_hal_power.h"
#include "furi_hal_usb.h" #include "furi_hal_usb.h"
#include <dolphin/dolphin.h>
enum GpioItem { enum GpioItem {
GpioItemUsbUart, GpioItemUsbUart,
@@ -88,6 +89,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == GpioStartEventUsbUart) { } else if(event.event == GpioStartEventUsbUart) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
if(!furi_hal_usb_is_locked()) { if(!furi_hal_usb_is_locked()) {
DOLPHIN_DEED(DolphinDeedGpioUartBridge);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
} else { } else {
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);

View File

@@ -13,7 +13,7 @@ static UsbUartConfig* cfg_set;
static const char* vcp_ch[] = {"0 (CLI)", "1"}; static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"13,14", "15,16"}; static const char* uart_ch[] = {"13,14", "15,16"};
static const char* flow_pins[] = {"None", "2,3", "6,7"}; static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
static const char* baudrate_mode[] = {"Host"}; static const char* baudrate_mode[] = {"Host"};
static const uint32_t baudrate_list[] = { static const uint32_t baudrate_list[] = {
2400, 2400,
@@ -33,6 +33,24 @@ bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
return false; return false;
} }
void line_ensure_flow_invariant(GpioApp* app) {
// GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is
// selected. This function enforces that invariant by resetting flow_pins
// to None if it is configured to 16,15 when LPUART is selected.
uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
VariableItem* item = app->var_item_flow;
variable_item_set_values_count(item, available_flow_pins);
if(cfg_set->flow_pins >= available_flow_pins) {
cfg_set->flow_pins = 0;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
variable_item_set_current_value_index(item, cfg_set->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
}
}
static void line_vcp_cb(VariableItem* item) { static void line_vcp_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
@@ -54,6 +72,7 @@ static void line_port_cb(VariableItem* item) {
else if(index == 1) else if(index == 1)
cfg_set->uart_ch = FuriHalUartIdLPUART1; cfg_set->uart_ch = FuriHalUartIdLPUART1;
usb_uart_set_config(app->usb_uart_bridge, cfg_set); usb_uart_set_config(app->usb_uart_bridge, cfg_set);
line_ensure_flow_invariant(app);
} }
static void line_flow_cb(VariableItem* item) { static void line_flow_cb(VariableItem* item) {
@@ -116,9 +135,12 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
variable_item_set_current_value_index(item, cfg_set->uart_ch); variable_item_set_current_value_index(item, cfg_set->uart_ch);
variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]); variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]);
item = variable_item_list_add(var_item_list, "RTS/DTR Pins", 3, line_flow_cb, app); item = variable_item_list_add(
var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
variable_item_set_current_value_index(item, cfg_set->flow_pins); variable_item_set_current_value_index(item, cfg_set->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
app->var_item_flow = item;
line_ensure_flow_invariant(app);
variable_item_list_set_selected_item( variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg));

View File

@@ -14,6 +14,7 @@
static const GpioPin* flow_pins[][2] = { static const GpioPin* flow_pins[][2] = {
{&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3
{&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7 {&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7
{&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15
}; };
typedef enum { typedef enum {

View File

@@ -1,4 +1,5 @@
#include <cli/cli.h> #include <cli/cli.h>
#include <cli/cli_i.h>
#include <infrared.h> #include <infrared.h>
#include <infrared_worker.h> #include <infrared_worker.h>
#include <furi_hal_infrared.h> #include <furi_hal_infrared.h>
@@ -6,12 +7,23 @@
#include <toolbox/args.h> #include <toolbox/args.h>
#include "infrared_signal.h" #include "infrared_signal.h"
#include "infrared_brute_force.h"
#include <m-dict.h>
#define INFRARED_CLI_BUF_SIZE 10 #define INFRARED_CLI_BUF_SIZE 10
DICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST)
enum RemoteTypes { TV = 0, AC = 1 };
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args); static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args);
static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args); static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args);
static void infrared_cli_process_decode(Cli* cli, FuriString* args); static void infrared_cli_process_decode(Cli* cli, FuriString* args);
static void infrared_cli_process_universal(Cli* cli, FuriString* args);
static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type);
static void
infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal);
static const struct { static const struct {
const char* cmd; const char* cmd;
@@ -20,6 +32,7 @@ static const struct {
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, {.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, {.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
{.cmd = "decode", .process_function = infrared_cli_process_decode}, {.cmd = "decode", .process_function = infrared_cli_process_decode},
{.cmd = "universal", .process_function = infrared_cli_process_universal},
}; };
static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
@@ -57,25 +70,9 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv
} }
} }
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
UNUSED(cli);
UNUSED(args);
InfraredWorker* worker = infrared_worker_alloc();
infrared_worker_rx_start(worker);
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n");
while(!cli_cmd_interrupt_received(cli)) {
furi_delay_ms(50);
}
infrared_worker_rx_stop(worker);
infrared_worker_free(worker);
}
static void infrared_cli_print_usage(void) { static void infrared_cli_print_usage(void) {
printf("Usage:\r\n"); printf("Usage:\r\n");
printf("\tir rx\r\n"); printf("\tir rx [raw]\r\n");
printf("\tir tx <protocol> <address> <command>\r\n"); printf("\tir tx <protocol> <address> <command>\r\n");
printf("\t<command> and <address> are hex-formatted\r\n"); printf("\t<command> and <address> are hex-formatted\r\n");
printf("\tAvailable protocols:"); printf("\tAvailable protocols:");
@@ -90,6 +87,37 @@ static void infrared_cli_print_usage(void) {
INFRARED_MIN_FREQUENCY, INFRARED_MIN_FREQUENCY,
INFRARED_MAX_FREQUENCY); INFRARED_MAX_FREQUENCY);
printf("\tir decode <input_file> [<output_file>]\r\n"); printf("\tir decode <input_file> [<output_file>]\r\n");
printf("\tir universal <tv, ac> <signal name>\r\n");
printf("\tir universal list <tv, ac>\r\n");
}
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
UNUSED(cli);
bool enable_decoding = true;
if(!furi_string_empty(args)) {
if(!furi_string_cmp_str(args, "raw")) {
enable_decoding = false;
} else {
printf("Wrong arguments.\r\n");
infrared_cli_print_usage();
return;
}
}
InfraredWorker* worker = infrared_worker_alloc();
infrared_worker_rx_enable_signal_decoding(worker, enable_decoding);
infrared_worker_rx_start(worker);
infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
printf("Receiving %s INFRARED...\r\nPress Ctrl+C to abort\r\n", enable_decoding ? "" : "RAW");
while(!cli_cmd_interrupt_received(cli)) {
furi_delay_ms(50);
}
infrared_worker_rx_stop(worker);
infrared_worker_free(worker);
} }
static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
@@ -328,6 +356,168 @@ static void infrared_cli_process_decode(Cli* cli, FuriString* args) {
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
} }
static void infrared_cli_process_universal(Cli* cli, FuriString* args) {
enum RemoteTypes Remote;
FuriString* command;
FuriString* remote;
FuriString* signal;
command = furi_string_alloc();
remote = furi_string_alloc();
signal = furi_string_alloc();
do {
if(!args_read_string_and_trim(args, command)) {
infrared_cli_print_usage();
break;
}
if(furi_string_cmp_str(command, "list") == 0) {
args_read_string_and_trim(args, remote);
if(furi_string_cmp_str(remote, "tv") == 0) {
Remote = TV;
} else if(furi_string_cmp_str(remote, "ac") == 0) {
Remote = AC;
} else {
printf("Invalid remote type.\r\n");
break;
}
infrared_cli_list_remote_signals(Remote);
break;
}
if(furi_string_cmp_str(command, "tv") == 0) {
Remote = TV;
} else if(furi_string_cmp_str(command, "ac") == 0) {
Remote = AC;
} else {
printf("Invalid remote type.\r\n");
break;
}
args_read_string_and_trim(args, signal);
if(furi_string_empty(signal)) {
printf("Must supply a valid signal for type of remote selected.\r\n");
break;
}
infrared_cli_brute_force_signals(cli, Remote, signal);
break;
} while(false);
furi_string_free(command);
furi_string_free(remote);
furi_string_free(signal);
}
static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
dict_signals_t signals_dict;
FuriString* key;
const char* remote_file = NULL;
bool success = false;
int max = 1;
switch(remote_type) {
case TV:
remote_file = EXT_PATH("infrared/assets/tv.ir");
break;
case AC:
remote_file = EXT_PATH("infrared/assets/ac.ir");
break;
default:
break;
}
dict_signals_init(signals_dict);
key = furi_string_alloc();
success = flipper_format_buffered_file_open_existing(ff, remote_file);
if(success) {
FuriString* signal_name;
signal_name = furi_string_alloc();
printf("Valid signals:\r\n");
while(flipper_format_read_string(ff, "name", signal_name)) {
furi_string_set_str(key, furi_string_get_cstr(signal_name));
int* v = dict_signals_get(signals_dict, key);
if(v != NULL) {
(*v)++;
max = M_MAX(*v, max);
} else {
dict_signals_set_at(signals_dict, key, 1);
}
}
dict_signals_it_t it;
for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) {
const struct dict_signals_pair_s* pair = dict_signals_cref(it);
printf("\t%s\r\n", furi_string_get_cstr(pair->key));
}
furi_string_free(signal_name);
}
furi_string_free(key);
dict_signals_clear(signals_dict);
flipper_format_free(ff);
furi_record_close(RECORD_STORAGE);
}
static void
infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal) {
InfraredBruteForce* brute_force = infrared_brute_force_alloc();
const char* remote_file = NULL;
uint32_t i = 0;
bool success = false;
switch(remote_type) {
case TV:
remote_file = EXT_PATH("infrared/assets/tv.ir");
break;
case AC:
remote_file = EXT_PATH("infrared/assets/ac.ir");
break;
default:
break;
}
infrared_brute_force_set_db_filename(brute_force, remote_file);
infrared_brute_force_add_record(brute_force, i++, furi_string_get_cstr(signal));
success = infrared_brute_force_calculate_messages(brute_force);
if(success) {
uint32_t record_count;
uint32_t index = 0;
int records_sent = 0;
bool running = false;
running = infrared_brute_force_start(brute_force, index, &record_count);
if(record_count <= 0) {
printf("Invalid signal.\n");
infrared_brute_force_reset(brute_force);
return;
}
printf("Sending %ld codes to the tv.\r\n", record_count);
printf("Press Ctrl-C to stop.\r\n");
while(running) {
running = infrared_brute_force_send_next(brute_force);
if(cli_cmd_interrupt_received(cli)) break;
printf("\r%d%% complete.", (int)((float)records_sent++ / (float)record_count * 100));
fflush(stdout);
}
infrared_brute_force_stop(brute_force);
} else {
printf("Invalid signal.\r\n");
}
infrared_brute_force_reset(brute_force);
infrared_brute_force_free(brute_force);
}
static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) {
UNUSED(context); UNUSED(context);
if(furi_hal_infrared_is_busy()) { if(furi_hal_infrared_is_busy()) {

View File

@@ -70,7 +70,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e
uint32_t record_count; uint32_t record_count;
if(infrared_brute_force_start( if(infrared_brute_force_start(
brute_force, infrared_custom_event_get_value(event.event), &record_count)) { brute_force, infrared_custom_event_get_value(event.event), &record_count)) {
DOLPHIN_DEED(DolphinDeedIrBruteForce); DOLPHIN_DEED(DolphinDeedIrSend);
infrared_scene_universal_common_show_popup(infrared, record_count); infrared_scene_universal_common_show_popup(infrared, record_count);
} else { } else {
scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases);

View File

@@ -34,7 +34,7 @@ void lfrfid_scene_raw_info_on_enter(void* context) {
} }
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
//string_clear(tmp_string); //furi_string_free(tmp_string);
} }
bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) { bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) {

View File

@@ -26,7 +26,7 @@ void nfc_scene_detect_reader_callback(void* context) {
void nfc_scene_detect_reader_on_enter(void* context) { void nfc_scene_detect_reader_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate); DOLPHIN_DEED(DolphinDeedNfcDetectReader);
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);

View File

@@ -1,7 +1,6 @@
#include "../nfc_i.h" #include "../nfc_i.h"
enum SubmenuIndex { enum SubmenuIndex {
SubmenuIndexSave,
SubmenuIndexInfo, SubmenuIndexInfo,
}; };
@@ -15,7 +14,6 @@ void nfc_scene_emv_menu_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc);
submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc); submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
submenu_set_selected_item( submenu_set_selected_item(
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu)); nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
@@ -28,13 +26,7 @@ bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) {
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) { if(event.event == SubmenuIndexInfo) {
nfc->dev->format = NfcDeviceSaveFormatBankCard;
// Clear device name
nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
consumed = true; consumed = true;
} }

View File

@@ -24,12 +24,33 @@ void nfc_scene_emv_read_success_on_enter(void* context) {
nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc); nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc);
FuriString* temp_str; FuriString* temp_str;
temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name); if(emv_data->name[0] != '\0') {
for(uint8_t i = 0; i < emv_data->number_len; i += 2) { temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
furi_string_cat_printf( } else {
temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]); temp_str = furi_string_alloc_printf("\e#Unknown Bank Card\n");
}
if(emv_data->number_len) {
for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
furi_string_cat_printf(
temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
}
furi_string_trim(temp_str);
} else if(emv_data->aid_len) {
furi_string_cat_printf(temp_str, "Can't parse data from app\n");
// Parse AID name
FuriString* aid_name;
aid_name = furi_string_alloc();
if(nfc_emv_parser_get_aid_name(
nfc->dev->storage, emv_data->aid, emv_data->aid_len, aid_name)) {
furi_string_cat_printf(temp_str, "AID: %s", furi_string_get_cstr(aid_name));
} else {
furi_string_cat_printf(temp_str, "AID: ");
for(uint8_t i = 0; i < emv_data->aid_len; i++) {
furi_string_cat_printf(temp_str, "%02X", emv_data->aid[i]);
}
}
furi_string_free(aid_name);
} }
furi_string_trim(temp_str);
// Add expiration date // Add expiration date
if(emv_data->exp_mon) { if(emv_data->exp_mon) {

View File

@@ -34,8 +34,6 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
widget_add_button_element( widget_add_button_element(
nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
widget_add_button_element(
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc);
widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36); widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36);
if(user_dict_keys_total > 0) { if(user_dict_keys_total > 0) {
widget_add_button_element( widget_add_button_element(
@@ -57,9 +55,6 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event)
if(event.event == GuiButtonTypeCenter) { if(event.event == GuiButtonTypeCenter) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd);
consumed = true; consumed = true;
} else if(event.event == GuiButtonTypeLeft) {
scene_manager_previous_scene(nfc->scene_manager);
consumed = true;
} else if(event.event == GuiButtonTypeRight) { } else if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList);
consumed = true; consumed = true;

View File

@@ -1,4 +1,5 @@
#include "../nfc_i.h" #include "../nfc_i.h"
#include <dolphin/dolphin.h>
enum SubmenuIndex { enum SubmenuIndex {
SubmenuIndexSave, SubmenuIndexSave,
@@ -35,6 +36,8 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) { if(event.event == SubmenuIndexSave) {
DOLPHIN_DEED(DolphinDeedNfcMfcAdd);
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave); nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave);
nfc->dev->format = NfcDeviceSaveFormatMifareClassic; nfc->dev->format = NfcDeviceSaveFormatMifareClassic;

View File

@@ -14,7 +14,6 @@ void nfc_scene_mf_ultralight_read_auth_result_widget_callback(
void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup dialog view // Setup dialog view
FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
@@ -38,6 +37,7 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
widget_add_string_element( widget_add_string_element(
widget, 0, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); widget, 0, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
if(mf_ul_data->auth_success) { if(mf_ul_data->auth_success) {
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
furi_string_printf( furi_string_printf(
temp_str, temp_str,
"Password: %02X %02X %02X %02X", "Password: %02X %02X %02X %02X",
@@ -54,6 +54,8 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
config_pages->auth_data.pack.raw[1]); config_pages->auth_data.pack.raw[1]);
widget_add_string_element( widget_add_string_element(
widget, 0, 39, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); widget, 0, 39, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
} else {
DOLPHIN_DEED(DolphinDeedNfcMfulError);
} }
furi_string_printf( furi_string_printf(
temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4);

View File

@@ -30,7 +30,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) { if(event.event == NfcCustomEventByteInputDone) {
DOLPHIN_DEED(DolphinDeedNfcAdd); DOLPHIN_DEED(DolphinDeedNfcAddSave);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; nfc->dev->dev_data.nfc_data = nfc->dev_edit_data;
if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) { if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) {

View File

@@ -69,12 +69,3 @@ typedef enum {
SubGhzViewIdTestCarrier, SubGhzViewIdTestCarrier,
SubGhzViewIdTestPacket, SubGhzViewIdTestPacket,
} SubGhzViewId; } SubGhzViewId;
struct SubGhzPresetDefinition {
FuriString* name;
uint32_t frequency;
uint8_t* data;
size_t data_size;
};
typedef struct SubGhzPresetDefinition SubGhzPresetDefinition;

View File

@@ -28,8 +28,8 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
subghz->txrx->decoder_result, subghz->txrx->decoder_result,
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
SubGhzPresetDefinition* preset = SubGhzRadioPreset* preset =
subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen); subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
subghz_preset_init( subghz_preset_init(
subghz, subghz,
furi_string_get_cstr(preset->name), furi_string_get_cstr(preset->name),

View File

@@ -6,7 +6,7 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <flipper_format/flipper_format_i.h> #include <flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/stream.h> #include <lib/toolbox/stream/stream.h>
#include <lib/subghz/protocols/registry.h> #include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubGhzSetType" #define TAG "SubGhzSetType"

View File

@@ -3,6 +3,7 @@
#include "subghz/types.h" #include "subghz/types.h"
#include "subghz_i.h" #include "subghz_i.h"
#include <lib/toolbox/path.h> #include <lib/toolbox/path.h>
#include <lib/subghz/protocols/protocol_items.h>
bool subghz_custom_event_callback(void* context, uint32_t event) { bool subghz_custom_event_callback(void* context, uint32_t event) {
furi_assert(context); furi_assert(context);
@@ -169,7 +170,7 @@ SubGhz* subghz_alloc() {
//init Worker & Protocol & History & KeyBoard //init Worker & Protocol & History & KeyBoard
subghz->lock = SubGhzLockOff; subghz->lock = SubGhzLockOff;
subghz->txrx = malloc(sizeof(SubGhzTxRx)); subghz->txrx = malloc(sizeof(SubGhzTxRx));
subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition)); subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset));
subghz->txrx->preset->name = furi_string_alloc(); subghz->txrx->preset->name = furi_string_alloc();
subghz_preset_init( subghz_preset_init(
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
@@ -186,6 +187,8 @@ SubGhz* subghz_alloc() {
subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz_environment_set_nice_flor_s_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(
subghz->txrx->environment, (void*)&subghz_protocol_registry);
subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);

View File

@@ -9,6 +9,7 @@
#include <lib/subghz/receiver.h> #include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h> #include <lib/subghz/transmitter.h>
#include <lib/subghz/subghz_file_encoder_worker.h> #include <lib/subghz/subghz_file_encoder_worker.h>
#include <lib/subghz/protocols/protocol_items.h>
#include "helpers/subghz_chat.h" #include "helpers/subghz_chat.h"
@@ -164,6 +165,7 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string)); stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string));
SubGhzEnvironment* environment = subghz_environment_alloc(); SubGhzEnvironment* environment = subghz_environment_alloc();
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton");
subghz_transmitter_deserialize(transmitter, flipper_format); subghz_transmitter_deserialize(transmitter, flipper_format);
@@ -257,6 +259,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
environment, EXT_PATH("subghz/assets/came_atomo")); environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, EXT_PATH("subghz/assets/nice_flor_s")); environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
@@ -376,6 +379,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
environment, EXT_PATH("subghz/assets/came_atomo")); environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, EXT_PATH("subghz/assets/nice_flor_s")); environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);

View File

@@ -11,7 +11,7 @@ typedef struct {
FuriString* item_str; FuriString* item_str;
FlipperFormat* flipper_string; FlipperFormat* flipper_string;
uint8_t type; uint8_t type;
SubGhzPresetDefinition* preset; SubGhzRadioPreset* preset;
} SubGhzHistoryItem; } SubGhzHistoryItem;
ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST)
@@ -60,7 +60,7 @@ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) {
return item->preset->frequency; return item->preset->frequency;
} }
SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) { SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance); furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->preset; return item->preset;
@@ -138,7 +138,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp
bool subghz_history_add_to_history( bool subghz_history_add_to_history(
SubGhzHistory* instance, SubGhzHistory* instance,
void* context, void* context,
SubGhzPresetDefinition* preset) { SubGhzRadioPreset* preset) {
furi_assert(instance); furi_assert(instance);
furi_assert(context); furi_assert(context);
@@ -158,7 +158,7 @@ bool subghz_history_add_to_history(
FuriString* text; FuriString* text;
text = furi_string_alloc(); text = furi_string_alloc();
SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data);
item->preset = malloc(sizeof(SubGhzPresetDefinition)); item->preset = malloc(sizeof(SubGhzRadioPreset));
item->type = decoder_base->protocol->type; item->type = decoder_base->protocol->type;
item->preset->frequency = preset->frequency; item->preset->frequency = preset->frequency;
item->preset->name = furi_string_alloc(); item->preset->name = furi_string_alloc();

View File

@@ -5,7 +5,7 @@
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
#include <lib/flipper_format/flipper_format.h> #include <lib/flipper_format/flipper_format.h>
#include "helpers/subghz_types.h" #include <lib/subghz/types.h>
typedef struct SubGhzHistory SubGhzHistory; typedef struct SubGhzHistory SubGhzHistory;
@@ -35,7 +35,7 @@ void subghz_history_reset(SubGhzHistory* instance);
*/ */
uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx);
SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx); SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx);
/** Get preset to history[idx] /** Get preset to history[idx]
* *
@@ -88,13 +88,13 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* out
* *
* @param instance - SubGhzHistory instance * @param instance - SubGhzHistory instance
* @param context - SubGhzProtocolCommon context * @param context - SubGhzProtocolCommon context
* @param preset - SubGhzPresetDefinition preset * @param preset - SubGhzRadioPreset preset
* @return bool; * @return bool;
*/ */
bool subghz_history_add_to_history( bool subghz_history_add_to_history(
SubGhzHistory* instance, SubGhzHistory* instance,
void* context, void* context,
SubGhzPresetDefinition* preset); SubGhzRadioPreset* preset);
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
* *

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "helpers/subghz_types.h" #include "helpers/subghz_types.h"
#include <lib/subghz/types.h>
#include "subghz.h" #include "subghz.h"
#include "views/receiver.h" #include "views/receiver.h"
#include "views/transmitter.h" #include "views/transmitter.h"
@@ -11,8 +12,6 @@
#include "views/subghz_test_carrier.h" #include "views/subghz_test_carrier.h"
#include "views/subghz_test_packet.h" #include "views/subghz_test_packet.h"
// #include <furi.h>
// #include <furi_hal.h>
#include <gui/gui.h> #include <gui/gui.h>
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include <gui/scene_manager.h> #include <gui/scene_manager.h>
@@ -24,14 +23,12 @@
#include <gui/modules/widget.h> #include <gui/modules/widget.h>
#include <subghz/scenes/subghz_scene.h> #include <subghz/scenes/subghz_scene.h>
#include <lib/subghz/subghz_worker.h> #include <lib/subghz/subghz_worker.h>
#include <lib/subghz/subghz_setting.h>
#include <lib/subghz/receiver.h> #include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h> #include <lib/subghz/transmitter.h>
#include "subghz_history.h" #include "subghz_history.h"
#include "subghz_setting.h"
#include <gui/modules/variable_item_list.h> #include <gui/modules/variable_item_list.h>
#include <lib/toolbox/path.h> #include <lib/toolbox/path.h>
@@ -49,7 +46,7 @@ struct SubGhzTxRx {
SubGhzProtocolDecoderBase* decoder_result; SubGhzProtocolDecoderBase* decoder_result;
FlipperFormat* fff_data; FlipperFormat* fff_data;
SubGhzPresetDefinition* preset; SubGhzRadioPreset* preset;
SubGhzHistory* history; SubGhzHistory* history;
uint16_t idx_menu_chosen; uint16_t idx_menu_chosen;
SubGhzTxRxState txrx_state; SubGhzTxRxState txrx_state;

View File

@@ -192,7 +192,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
} else { } else {
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
} }
canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff));
furi_string_reset(str_buff); furi_string_reset(str_buff);
} }

View File

@@ -5,6 +5,5 @@ App(
provides=[ provides=[
"music_player", "music_player",
"snake_game", "snake_game",
"bt_hid",
], ],
) )

View File

@@ -1,15 +1,10 @@
App( App(
appid="bt_hid", appid="bt_hid",
name="Bluetooth Remote", name="Bluetooth Remote",
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.EXTERNAL,
entry_point="bt_hid_app", entry_point="bt_hid_app",
stack_size=1 * 1024, stack_size=1 * 1024,
cdefines=["APP_BLE_HID"],
requires=[
"bt",
"gui",
],
order=10,
fap_icon="bt_remote_10px.png",
fap_category="Tools", fap_category="Tools",
fap_icon="bt_remote_10px.png",
fap_icon_assets="assets",
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,6 +1,7 @@
#include "bt_hid.h" #include "bt_hid.h"
#include <furi_hal_bt.h> #include <furi_hal_bt.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include <dolphin/dolphin.h>
#define TAG "BtHidApp" #define TAG "BtHidApp"
@@ -8,6 +9,7 @@ enum BtDebugSubmenuIndex {
BtHidSubmenuIndexKeynote, BtHidSubmenuIndexKeynote,
BtHidSubmenuIndexKeyboard, BtHidSubmenuIndexKeyboard,
BtHidSubmenuIndexMedia, BtHidSubmenuIndexMedia,
BtHidSubmenuIndexTikTok,
BtHidSubmenuIndexMouse, BtHidSubmenuIndexMouse,
}; };
@@ -26,6 +28,9 @@ void bt_hid_submenu_callback(void* context, uint32_t index) {
} else if(index == BtHidSubmenuIndexMouse) { } else if(index == BtHidSubmenuIndexMouse) {
app->view_id = BtHidViewMouse; app->view_id = BtHidViewMouse;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse); view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse);
} else if(index == BtHidSubmenuIndexTikTok) {
app->view_id = BtHidViewTikTok;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
} }
} }
@@ -64,6 +69,7 @@ void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected); bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected);
bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected); bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected);
bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected); bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected);
bt_hid_tiktok_set_connected_status(bt_hid->bt_hid_tiktok, connected);
} }
BtHid* bt_hid_app_alloc() { BtHid* bt_hid_app_alloc() {
@@ -90,6 +96,8 @@ BtHid* bt_hid_app_alloc() {
submenu_add_item( submenu_add_item(
app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app);
submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
submenu_add_item(
app->submenu, "TikTok Controller", BtHidSubmenuIndexTikTok, bt_hid_submenu_callback, app);
submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app); submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app);
view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit);
view_dispatcher_add_view( view_dispatcher_add_view(
@@ -126,6 +134,13 @@ BtHid* bt_hid_app_alloc() {
view_dispatcher_add_view( view_dispatcher_add_view(
app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media)); app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media));
// TikTok view
app->bt_hid_tiktok = bt_hid_tiktok_alloc();
view_set_previous_callback(
bt_hid_tiktok_get_view(app->bt_hid_tiktok), bt_hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewTikTok, bt_hid_tiktok_get_view(app->bt_hid_tiktok));
// Mouse view // Mouse view
app->bt_hid_mouse = bt_hid_mouse_alloc(); app->bt_hid_mouse = bt_hid_mouse_alloc();
view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view); view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view);
@@ -158,6 +173,8 @@ void bt_hid_app_free(BtHid* app) {
bt_hid_media_free(app->bt_hid_media); bt_hid_media_free(app->bt_hid_media);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse);
bt_hid_mouse_free(app->bt_hid_mouse); bt_hid_mouse_free(app->bt_hid_mouse);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
bt_hid_tiktok_free(app->bt_hid_tiktok);
view_dispatcher_free(app->view_dispatcher); view_dispatcher_free(app->view_dispatcher);
// Close records // Close records
@@ -185,6 +202,8 @@ int32_t bt_hid_app(void* p) {
} }
furi_hal_bt_start_advertising(); furi_hal_bt_start_advertising();
DOLPHIN_DEED(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher); view_dispatcher_run(app->view_dispatcher);
bt_set_status_changed_callback(app->bt, NULL, NULL); bt_set_status_changed_callback(app->bt, NULL, NULL);

View File

@@ -13,6 +13,7 @@
#include "views/bt_hid_keyboard.h" #include "views/bt_hid_keyboard.h"
#include "views/bt_hid_media.h" #include "views/bt_hid_media.h"
#include "views/bt_hid_mouse.h" #include "views/bt_hid_mouse.h"
#include "views/bt_hid_tiktok.h"
typedef struct { typedef struct {
Bt* bt; Bt* bt;
@@ -25,6 +26,7 @@ typedef struct {
BtHidKeyboard* bt_hid_keyboard; BtHidKeyboard* bt_hid_keyboard;
BtHidMedia* bt_hid_media; BtHidMedia* bt_hid_media;
BtHidMouse* bt_hid_mouse; BtHidMouse* bt_hid_mouse;
BtHidTikTok* bt_hid_tiktok;
uint32_t view_id; uint32_t view_id;
} BtHid; } BtHid;
@@ -34,5 +36,6 @@ typedef enum {
BtHidViewKeyboard, BtHidViewKeyboard,
BtHidViewMedia, BtHidViewMedia,
BtHidViewMouse, BtHidViewMouse,
BtHidViewTikTok,
BtHidViewExitConfirm, BtHidViewExitConfirm,
} BtHidView; } BtHidView;

View File

@@ -5,6 +5,8 @@
#include <gui/elements.h> #include <gui/elements.h>
#include <gui/icon_i.h> #include <gui/icon_i.h>
#include "bt_hid_icons.h"
struct BtHidKeyboard { struct BtHidKeyboard {
View* view; View* view;
}; };

View File

@@ -4,6 +4,8 @@
#include <furi_hal_usb_hid.h> #include <furi_hal_usb_hid.h>
#include <gui/elements.h> #include <gui/elements.h>
#include "bt_hid_icons.h"
struct BtHidKeynote { struct BtHidKeynote {
View* view; View* view;
}; };

View File

@@ -4,6 +4,8 @@
#include <furi_hal_usb_hid.h> #include <furi_hal_usb_hid.h>
#include <gui/elements.h> #include <gui/elements.h>
#include "bt_hid_icons.h"
struct BtHidMedia { struct BtHidMedia {
View* view; View* view;
}; };

View File

@@ -4,6 +4,8 @@
#include <furi_hal_usb_hid.h> #include <furi_hal_usb_hid.h>
#include <gui/elements.h> #include <gui/elements.h>
#include "bt_hid_icons.h"
struct BtHidMouse { struct BtHidMouse {
View* view; View* view;
}; };

View File

@@ -0,0 +1,207 @@
#include "bt_hid_tiktok.h"
#include <furi.h>
#include <furi_hal_bt_hid.h>
#include <furi_hal_usb_hid.h>
#include <gui/elements.h>
#include "bt_hid_icons.h"
struct BtHidTikTok {
View* view;
};
typedef struct {
bool left_pressed;
bool up_pressed;
bool right_pressed;
bool down_pressed;
bool ok_pressed;
bool connected;
} BtHidTikTokModel;
static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
BtHidTikTokModel* model = context;
// Header
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok");
canvas_set_font(canvas, FontSecondary);
// Keypad circles
canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47);
// Up
if(model->up_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 96, 11, &I_Arr_up_7x9);
canvas_set_color(canvas, ColorBlack);
// Down
if(model->down_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 96, 44, &I_Arr_dwn_7x9);
canvas_set_color(canvas, ColorBlack);
// Left
if(model->left_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6);
canvas_set_color(canvas, ColorBlack);
// Right
if(model->right_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 111, 29, &I_Volup_8x6);
canvas_set_color(canvas, ColorBlack);
// Ok
if(model->ok_pressed) {
canvas_draw_icon(canvas, 91, 23, &I_Like_pressed_17x17);
} else {
canvas_draw_icon(canvas, 94, 27, &I_Like_def_11x9);
}
// Exit
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
}
static void bt_hid_tiktok_process_press(BtHidTikTok* bt_hid_tiktok, InputEvent* event) {
with_view_model(
bt_hid_tiktok->view,
BtHidTikTokModel * model,
{
if(event->key == InputKeyUp) {
model->up_pressed = true;
} else if(event->key == InputKeyDown) {
model->down_pressed = true;
} else if(event->key == InputKeyLeft) {
model->left_pressed = true;
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT);
} else if(event->key == InputKeyRight) {
model->right_pressed = true;
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT);
} else if(event->key == InputKeyOk) {
model->ok_pressed = true;
}
},
true);
}
static void bt_hid_tiktok_process_release(BtHidTikTok* bt_hid_tiktok, InputEvent* event) {
with_view_model(
bt_hid_tiktok->view,
BtHidTikTokModel * model,
{
if(event->key == InputKeyUp) {
model->up_pressed = false;
} else if(event->key == InputKeyDown) {
model->down_pressed = false;
} else if(event->key == InputKeyLeft) {
model->left_pressed = false;
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT);
} else if(event->key == InputKeyRight) {
model->right_pressed = false;
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT);
} else if(event->key == InputKeyOk) {
model->ok_pressed = false;
}
},
true);
}
static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) {
furi_assert(context);
BtHidTikTok* bt_hid_tiktok = context;
bool consumed = false;
if(event->type == InputTypePress) {
bt_hid_tiktok_process_press(bt_hid_tiktok, event);
consumed = true;
} else if(event->type == InputTypeRelease) {
bt_hid_tiktok_process_release(bt_hid_tiktok, event);
consumed = true;
} else if(event->type == InputTypeShort) {
if(event->key == InputKeyOk) {
furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT);
furi_delay_ms(50);
furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT);
furi_delay_ms(50);
furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT);
furi_delay_ms(50);
furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT);
consumed = true;
} else if(event->key == InputKeyUp) {
// Emulate up swipe
furi_hal_bt_hid_mouse_scroll(-6);
furi_hal_bt_hid_mouse_scroll(-12);
furi_hal_bt_hid_mouse_scroll(-19);
furi_hal_bt_hid_mouse_scroll(-12);
furi_hal_bt_hid_mouse_scroll(-6);
consumed = true;
} else if(event->key == InputKeyDown) {
// Emulate down swipe
furi_hal_bt_hid_mouse_scroll(6);
furi_hal_bt_hid_mouse_scroll(12);
furi_hal_bt_hid_mouse_scroll(19);
furi_hal_bt_hid_mouse_scroll(12);
furi_hal_bt_hid_mouse_scroll(6);
consumed = true;
} else if(event->key == InputKeyBack) {
furi_hal_bt_hid_consumer_key_release_all();
consumed = true;
}
}
return consumed;
}
BtHidTikTok* bt_hid_tiktok_alloc() {
BtHidTikTok* bt_hid_tiktok = malloc(sizeof(BtHidTikTok));
bt_hid_tiktok->view = view_alloc();
view_set_context(bt_hid_tiktok->view, bt_hid_tiktok);
view_allocate_model(bt_hid_tiktok->view, ViewModelTypeLocking, sizeof(BtHidTikTokModel));
view_set_draw_callback(bt_hid_tiktok->view, bt_hid_tiktok_draw_callback);
view_set_input_callback(bt_hid_tiktok->view, bt_hid_tiktok_input_callback);
return bt_hid_tiktok;
}
void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok) {
furi_assert(bt_hid_tiktok);
view_free(bt_hid_tiktok->view);
free(bt_hid_tiktok);
}
View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok) {
furi_assert(bt_hid_tiktok);
return bt_hid_tiktok->view;
}
void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected) {
furi_assert(bt_hid_tiktok);
with_view_model(
bt_hid_tiktok->view, BtHidTikTokModel * model, { model->connected = connected; }, true);
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include <gui/view.h>
typedef struct BtHidTikTok BtHidTikTok;
BtHidTikTok* bt_hid_tiktok_alloc();
void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok);
View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok);
void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected);

View File

@@ -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"`
<details>
<summary>BluePill configuration example</summary>
```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",
],
},
```
</details>
<details>
<summary>Flipper Zero configuration example</summary>
```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",
],
},
```
</details>
### 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.
<details>
<summary>Flash BluePill</summary>
```
openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c init -c "program build/firmware.bin reset exit 0x8000000"
```
</details>
<details>
<summary>Flash Flipper Zero using DAP v2 protocol</summary>
```
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"
```
</details>
<details>
<summary>Reboot connected DAP-Link on Flipper named Oyevoxo</summary>
```
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"
```
</details>
### 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.
<details>
<summary>BluePill platformio.ini example</summary>
```
[env:bluepill_f103c8]
platform = ststm32
board = bluepill_f103c8
debug_tool = cmsis-dap
upload_protocol = cmsis-dap
```
</details>

View File

@@ -0,0 +1,24 @@
App(
appid="dap_link",
name="DAP Link",
apptype=FlipperAppType.PLUGIN,
entry_point="dap_link_app",
requires=[
"gui",
"dialogs",
],
stack_size=4 * 1024,
order=20,
fap_icon="dap_link.png",
fap_category="Tools",
fap_private_libs=[
Lib(
name="free-dap",
cincludes=["."],
sources=[
"dap.c",
],
),
],
fap_icon_assets="icons",
)

View File

@@ -0,0 +1,234 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2022, Alex Taradov <alex@taradov.com>. All rights reserved.
#ifndef _DAP_CONFIG_H_
#define _DAP_CONFIG_H_
/*- Includes ----------------------------------------------------------------*/
#include <furi_hal_gpio.h>
/*- 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_

View File

@@ -0,0 +1,521 @@
#include <dap.h>
#include <furi.h>
#include <furi_hal_version.h>
#include <furi_hal_gpio.h>
#include <furi_hal_uart.h>
#include <furi_hal_console.h>
#include <furi_hal_resources.h>
#include <furi_hal_power.h>
#include <stm32wbxx_ll_usart.h>
#include <stm32wbxx_ll_lpuart.h>
#include "dap_link.h"
#include "dap_config.h"
#include "gui/dap_gui.h"
#include "usb/dap_v2_usb.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_wait_for_deinit();
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 FuriThread* furi_thread_alloc_ex(
const char* name,
uint32_t stack_size,
FuriThreadCallback callback,
void* context) {
FuriThread* thread = furi_thread_alloc();
furi_thread_set_name(thread, name);
furi_thread_set_stack_size(thread, stack_size);
furi_thread_set_callback(thread, callback);
furi_thread_set_context(thread, context);
return thread;
}
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);
// 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;
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <stdint.h>
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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

View File

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

View File

@@ -0,0 +1,4 @@
#pragma once
#include <stdint.h>
int32_t dap_gui_thread(void* arg);

View File

@@ -0,0 +1,7 @@
#pragma once
typedef enum {
DapAppCustomEventConfig,
DapAppCustomEventHelp,
DapAppCustomEventAbout,
} DapAppCustomEvent;

View File

@@ -0,0 +1,34 @@
#pragma once
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#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;

View File

@@ -0,0 +1,30 @@
#include "dap_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const dap_scene_on_enter_handlers[])(void*) = {
#include "dap_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const dap_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "dap_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const dap_scene_on_exit_handlers[])(void* context) = {
#include "dap_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers dap_scene_handlers = {
.on_enter_handlers = dap_scene_on_enter_handlers,
.on_event_handlers = dap_scene_on_event_handlers,
.on_exit_handlers = dap_scene_on_exit_handlers,
.scene_num = DapSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) DapScene##id,
typedef enum {
#include "dap_scene_config.h"
DapSceneNum,
} DapScene;
#undef ADD_SCENE
extern const SceneManagerHandlers dap_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "dap_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "dap_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "dap_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,4 @@
ADD_SCENE(dap, main, Main)
ADD_SCENE(dap, config, Config)
ADD_SCENE(dap, help, Help)
ADD_SCENE(dap, about, About)

View File

@@ -0,0 +1,68 @@
#include "../dap_gui_i.h"
#define DAP_VERSION_APP "0.1.0"
#define DAP_DEVELOPED "Dr_Zlo"
#define DAP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
void dap_scene_about_on_enter(void* context) {
DapGuiApp* app = context;
FuriString* temp_str;
temp_str = furi_string_alloc();
furi_string_printf(temp_str, "\e#%s\n", "Information");
furi_string_cat_printf(temp_str, "Version: %s\n", DAP_VERSION_APP);
furi_string_cat_printf(temp_str, "Developed by: %s\n", DAP_DEVELOPED);
furi_string_cat_printf(temp_str, "Github: %s\n\n", DAP_GITHUB);
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
furi_string_cat_printf(
temp_str, "CMSIS-DAP debugger\nbased on Free-DAP\nThanks to Alex Taradov\n\n");
furi_string_cat_printf(
temp_str,
"Supported protocols:\n"
"SWD, JTAG, UART\n"
"DAP v1 (cmsis_backend hid), DAP v2 (cmsis_backend usb_bulk), VCP\n");
widget_add_text_box_element(
app->widget,
0,
0,
128,
14,
AlignCenter,
AlignBottom,
"\e#\e! \e!\n",
false);
widget_add_text_box_element(
app->widget,
0,
2,
128,
14,
AlignCenter,
AlignBottom,
"\e#\e! DAP Link \e!\n",
false);
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget);
}
bool dap_scene_about_on_event(void* context, SceneManagerEvent event) {
DapGuiApp* app = context;
bool consumed = false;
UNUSED(app);
UNUSED(event);
return consumed;
}
void dap_scene_about_on_exit(void* context) {
DapGuiApp* app = context;
// Clear views
widget_reset(app->widget);
}

View File

@@ -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]);
item = variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL);
item = 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);
}

View File

@@ -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 [С1]\r\n"
" RX: 16 [С0]\r\n");
} else {
furi_string_cat(
string,
" RX: 15 [С1]\r\n"
" TX: 16 [С0]\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);
}

View File

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

View File

@@ -0,0 +1,189 @@
#include "dap_main_view.h"
#include "dap_link_icons.h"
#include <gui/elements.h>
// 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);
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <gui/view.h>
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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

View File

@@ -0,0 +1,994 @@
#include <furi.h>
#include <usb.h>
#include <usb_std.h>
#include <usb_hid.h>
#include <usb_cdc.h>
#include <furi_hal_console.h>
#include "dap_v2_usb.h"
// #define DAP_USB_LOG
#define HID_EP_IN 0x80
#define HID_EP_OUT 0x00
#define DAP_HID_EP_SEND 1
#define DAP_HID_EP_RECV 2
#define DAP_HID_EP_BULK_RECV 3
#define DAP_HID_EP_BULK_SEND 4
#define DAP_CDC_EP_COMM 5
#define DAP_CDC_EP_SEND 6
#define DAP_CDC_EP_RECV 7
#define DAP_HID_EP_IN (HID_EP_IN | DAP_HID_EP_SEND)
#define DAP_HID_EP_OUT (HID_EP_OUT | DAP_HID_EP_RECV)
#define DAP_HID_EP_BULK_IN (HID_EP_IN | DAP_HID_EP_BULK_SEND)
#define DAP_HID_EP_BULK_OUT (HID_EP_OUT | DAP_HID_EP_BULK_RECV)
#define DAP_HID_EP_SIZE 64
#define DAP_CDC_COMM_EP_SIZE 8
#define DAP_CDC_EP_SIZE 64
#define DAP_BULK_INTERVAL 0
#define DAP_HID_INTERVAL 1
#define DAP_CDC_INTERVAL 0
#define DAP_CDC_COMM_INTERVAL 1
#define DAP_HID_VID 0x0483
#define DAP_HID_PID 0x5740
#define DAP_USB_EP0_SIZE 8
#define EP_CFG_DECONFIGURE 0
#define EP_CFG_CONFIGURE 1
enum {
USB_INTF_HID,
USB_INTF_BULK,
USB_INTF_CDC_COMM,
USB_INTF_CDC_DATA,
USB_INTF_COUNT,
};
enum {
USB_STR_ZERO,
USB_STR_MANUFACTURER,
USB_STR_PRODUCT,
USB_STR_SERIAL_NUMBER,
USB_STR_CMSIS_DAP_V1,
USB_STR_CMSIS_DAP_V2,
USB_STR_COM_PORT,
USB_STR_COUNT,
};
// static const char* usb_str[] = {
// [USB_STR_MANUFACTURER] = "Flipper Devices Inc.",
// [USB_STR_PRODUCT] = "Combined VCP and CMSIS-DAP Adapter",
// [USB_STR_COM_PORT] = "Virtual COM-Port",
// [USB_STR_CMSIS_DAP_V1] = "CMSIS-DAP v1 Adapter",
// [USB_STR_CMSIS_DAP_V2] = "CMSIS-DAP v2 Adapter",
// [USB_STR_SERIAL_NUMBER] = "01234567890ABCDEF",
// };
static const struct usb_string_descriptor dev_manuf_descr =
USB_STRING_DESC("Flipper Devices Inc.");
static const struct usb_string_descriptor dev_prod_descr =
USB_STRING_DESC("Combined VCP and CMSIS-DAP Adapter");
static struct usb_string_descriptor* dev_serial_descr = NULL;
static const struct usb_string_descriptor dev_dap_v1_descr =
USB_STRING_DESC("CMSIS-DAP v1 Adapter");
static const struct usb_string_descriptor dev_dap_v2_descr =
USB_STRING_DESC("CMSIS-DAP v2 Adapter");
static const struct usb_string_descriptor dev_com_descr = USB_STRING_DESC("Virtual COM-Port");
struct HidConfigDescriptor {
struct usb_config_descriptor configuration;
// CMSIS-DAP v1
struct usb_interface_descriptor hid_interface;
struct usb_hid_descriptor hid;
struct usb_endpoint_descriptor hid_ep_in;
struct usb_endpoint_descriptor hid_ep_out;
// CMSIS-DAP v2
struct usb_interface_descriptor bulk_interface;
struct usb_endpoint_descriptor bulk_ep_out;
struct usb_endpoint_descriptor bulk_ep_in;
// CDC
struct usb_iad_descriptor iad;
struct usb_interface_descriptor interface_comm;
struct usb_cdc_header_desc cdc_header;
struct usb_cdc_call_mgmt_desc cdc_acm;
struct usb_cdc_acm_desc cdc_call_mgmt;
struct usb_cdc_union_desc cdc_union;
struct usb_endpoint_descriptor ep_comm;
struct usb_interface_descriptor interface_data;
struct usb_endpoint_descriptor ep_in;
struct usb_endpoint_descriptor ep_out;
} __attribute__((packed));
static const struct usb_device_descriptor hid_device_desc = {
.bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DTYPE_DEVICE,
.bcdUSB = VERSION_BCD(2, 1, 0),
.bDeviceClass = USB_CLASS_MISC,
.bDeviceSubClass = USB_SUBCLASS_IAD,
.bDeviceProtocol = USB_PROTO_IAD,
.bMaxPacketSize0 = DAP_USB_EP0_SIZE,
.idVendor = DAP_HID_VID,
.idProduct = DAP_HID_PID,
.bcdDevice = VERSION_BCD(1, 0, 0),
.iManufacturer = USB_STR_MANUFACTURER,
.iProduct = USB_STR_PRODUCT,
.iSerialNumber = USB_STR_SERIAL_NUMBER,
.bNumConfigurations = 1,
};
static const uint8_t hid_report_desc[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x00, // Usage (Undefined)
0xa1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xff, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x40, // Report Count (64)
0x09, 0x00, // Usage (Undefined)
0x81, 0x82, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x95, 0x40, // Report Count (64)
0x09, 0x00, // Usage (Undefined)
0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0xc0, // End Collection
};
static const struct HidConfigDescriptor hid_cfg_desc = {
.configuration =
{
.bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DTYPE_CONFIGURATION,
.wTotalLength = sizeof(struct HidConfigDescriptor),
.bNumInterfaces = USB_INTF_COUNT,
.bConfigurationValue = 1,
.iConfiguration = NO_DESCRIPTOR,
.bmAttributes = USB_CFG_ATTR_RESERVED,
.bMaxPower = USB_CFG_POWER_MA(500),
},
// CMSIS-DAP v1
.hid_interface =
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = USB_INTF_HID,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT,
.bInterfaceProtocol = USB_HID_PROTO_NONBOOT,
.iInterface = USB_STR_CMSIS_DAP_V1,
},
.hid =
{
.bLength = sizeof(struct usb_hid_descriptor),
.bDescriptorType = USB_DTYPE_HID,
.bcdHID = VERSION_BCD(1, 1, 1),
.bCountryCode = USB_HID_COUNTRY_NONE,
.bNumDescriptors = 1,
.bDescriptorType0 = USB_DTYPE_HID_REPORT,
.wDescriptorLength0 = sizeof(hid_report_desc),
},
.hid_ep_in =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = DAP_HID_EP_IN,
.bmAttributes = USB_EPTYPE_INTERRUPT,
.wMaxPacketSize = DAP_HID_EP_SIZE,
.bInterval = DAP_HID_INTERVAL,
},
.hid_ep_out =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = DAP_HID_EP_OUT,
.bmAttributes = USB_EPTYPE_INTERRUPT,
.wMaxPacketSize = DAP_HID_EP_SIZE,
.bInterval = DAP_HID_INTERVAL,
},
// CMSIS-DAP v2
.bulk_interface =
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = USB_INTF_BULK,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = USB_STR_CMSIS_DAP_V2,
},
.bulk_ep_out =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = DAP_HID_EP_BULK_OUT,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = DAP_HID_EP_SIZE,
.bInterval = DAP_BULK_INTERVAL,
},
.bulk_ep_in =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = DAP_HID_EP_BULK_IN,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = DAP_HID_EP_SIZE,
.bInterval = DAP_BULK_INTERVAL,
},
// CDC
.iad =
{
.bLength = sizeof(struct usb_iad_descriptor),
.bDescriptorType = USB_DTYPE_INTERFASEASSOC,
.bFirstInterface = USB_INTF_CDC_COMM,
.bInterfaceCount = 2,
.bFunctionClass = USB_CLASS_CDC,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
.bFunctionProtocol = USB_PROTO_NONE,
.iFunction = USB_STR_COM_PORT,
},
.interface_comm =
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = USB_INTF_CDC_COMM,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = 0,
},
.cdc_header =
{
.bFunctionLength = sizeof(struct usb_cdc_header_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_HEADER,
.bcdCDC = VERSION_BCD(1, 1, 0),
},
.cdc_acm =
{
.bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT,
// .bmCapabilities = USB_CDC_CAP_LINE | USB_CDC_CAP_BRK,
.bmCapabilities = 0,
},
.cdc_call_mgmt =
{
.bFunctionLength = sizeof(struct usb_cdc_acm_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_ACM,
.bmCapabilities = USB_CDC_CALL_MGMT_CAP_DATA_INTF,
// .bDataInterface = USB_INTF_CDC_DATA,
},
.cdc_union =
{
.bFunctionLength = sizeof(struct usb_cdc_union_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_UNION,
.bMasterInterface0 = USB_INTF_CDC_COMM,
.bSlaveInterface0 = USB_INTF_CDC_DATA,
},
.ep_comm =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = HID_EP_IN | DAP_CDC_EP_COMM,
.bmAttributes = USB_EPTYPE_INTERRUPT,
.wMaxPacketSize = DAP_CDC_COMM_EP_SIZE,
.bInterval = DAP_CDC_COMM_INTERVAL,
},
.interface_data =
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = USB_INTF_CDC_DATA,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = USB_SUBCLASS_NONE,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = NO_DESCRIPTOR,
},
.ep_in =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = HID_EP_IN | DAP_CDC_EP_SEND,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = DAP_CDC_EP_SIZE,
.bInterval = DAP_CDC_INTERVAL,
},
.ep_out =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = HID_EP_OUT | DAP_CDC_EP_RECV,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = DAP_CDC_EP_SIZE,
.bInterval = DAP_CDC_INTERVAL,
},
};
// WinUSB
#include "usb_winusb.h"
typedef struct USB_PACK {
usb_binary_object_store_descriptor_t bos;
usb_winusb_capability_descriptor_t winusb;
} usb_bos_hierarchy_t;
typedef struct USB_PACK {
usb_winusb_subset_header_function_t header;
usb_winusb_feature_compatble_id_t comp_id;
usb_winusb_feature_reg_property_guids_t property;
} usb_msos_descriptor_subset_t;
typedef struct USB_PACK {
usb_winusb_set_header_descriptor_t header;
usb_msos_descriptor_subset_t subset;
} usb_msos_descriptor_set_t;
#define USB_DTYPE_BINARY_OBJECT_STORE 15
#define USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR 16
#define USB_DC_TYPE_PLATFORM 5
const usb_bos_hierarchy_t usb_bos_hierarchy = {
.bos =
{
.bLength = sizeof(usb_binary_object_store_descriptor_t),
.bDescriptorType = USB_DTYPE_BINARY_OBJECT_STORE,
.wTotalLength = sizeof(usb_bos_hierarchy_t),
.bNumDeviceCaps = 1,
},
.winusb =
{
.bLength = sizeof(usb_winusb_capability_descriptor_t),
.bDescriptorType = USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR,
.bDevCapabilityType = USB_DC_TYPE_PLATFORM,
.bReserved = 0,
.PlatformCapabilityUUID = USB_WINUSB_PLATFORM_CAPABILITY_ID,
.dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION,
.wMSOSDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t),
.bMS_VendorCode = USB_WINUSB_VENDOR_CODE,
.bAltEnumCode = 0,
},
};
const usb_msos_descriptor_set_t usb_msos_descriptor_set = {
.header =
{
.wLength = sizeof(usb_winusb_set_header_descriptor_t),
.wDescriptorType = USB_WINUSB_SET_HEADER_DESCRIPTOR,
.dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION,
.wDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t),
},
.subset =
{
.header =
{
.wLength = sizeof(usb_winusb_subset_header_function_t),
.wDescriptorType = USB_WINUSB_SUBSET_HEADER_FUNCTION,
.bFirstInterface = USB_INTF_BULK,
.bReserved = 0,
.wSubsetLength = sizeof(usb_msos_descriptor_subset_t),
},
.comp_id =
{
.wLength = sizeof(usb_winusb_feature_compatble_id_t),
.wDescriptorType = USB_WINUSB_FEATURE_COMPATBLE_ID,
.CompatibleID = "WINUSB\0\0",
.SubCompatibleID = {0},
},
.property =
{
.wLength = sizeof(usb_winusb_feature_reg_property_guids_t),
.wDescriptorType = USB_WINUSB_FEATURE_REG_PROPERTY,
.wPropertyDataType = USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ,
.wPropertyNameLength =
sizeof(usb_msos_descriptor_set.subset.property.PropertyName),
.PropertyName = {'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 'I', 0,
'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0,
'e', 0, 'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0, 0},
.wPropertyDataLength =
sizeof(usb_msos_descriptor_set.subset.property.PropertyData),
.PropertyData = {'{', 0, 'C', 0, 'D', 0, 'B', 0, '3', 0, 'B', 0, '5', 0,
'A', 0, 'D', 0, '-', 0, '2', 0, '9', 0, '3', 0, 'B', 0,
'-', 0, '4', 0, '6', 0, '6', 0, '3', 0, '-', 0, 'A', 0,
'A', 0, '3', 0, '6', 0, '-', 0, '1', 0, 'A', 0, 'A', 0,
'E', 0, '4', 0, '6', 0, '4', 0, '6', 0, '3', 0, '7', 0,
'7', 0, '6', 0, '}', 0, 0, 0, 0, 0},
},
},
};
typedef struct {
FuriSemaphore* semaphore_v1;
FuriSemaphore* semaphore_v2;
FuriSemaphore* semaphore_cdc;
bool connected;
usbd_device* usb_dev;
DapStateCallback state_callback;
DapRxCallback rx_callback_v1;
DapRxCallback rx_callback_v2;
DapRxCallback rx_callback_cdc;
DapCDCControlLineCallback control_line_callback_cdc;
DapCDCConfigCallback config_callback_cdc;
void* context;
void* context_cdc;
} DAPState;
static DAPState dap_state = {
.semaphore_v1 = NULL,
.semaphore_v2 = NULL,
.semaphore_cdc = NULL,
.connected = false,
.usb_dev = NULL,
.state_callback = NULL,
.rx_callback_v1 = NULL,
.rx_callback_v2 = NULL,
.rx_callback_cdc = NULL,
.control_line_callback_cdc = NULL,
.config_callback_cdc = NULL,
.context = NULL,
.context_cdc = NULL,
};
static struct usb_cdc_line_coding cdc_config = {0};
static uint8_t cdc_ctrl_line_state = 0;
#ifdef DAP_USB_LOG
void furi_console_log_printf(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2)));
void furi_console_log_printf(const char* format, ...) {
char buffer[256];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
furi_hal_console_puts(buffer);
furi_hal_console_puts("\r\n");
UNUSED(format);
}
#else
#define furi_console_log_printf(...)
#endif
int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size) {
if((dap_state.semaphore_v1 == NULL) || (dap_state.connected == false)) return 0;
furi_check(furi_semaphore_acquire(dap_state.semaphore_v1, FuriWaitForever) == FuriStatusOk);
if(dap_state.connected) {
int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_IN, buffer, size);
furi_console_log_printf("v1 tx %ld", len);
return len;
} else {
return 0;
}
}
int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size) {
if((dap_state.semaphore_v2 == NULL) || (dap_state.connected == false)) return 0;
furi_check(furi_semaphore_acquire(dap_state.semaphore_v2, FuriWaitForever) == FuriStatusOk);
if(dap_state.connected) {
int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_BULK_IN, buffer, size);
furi_console_log_printf("v2 tx %ld", len);
return len;
} else {
return 0;
}
}
int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size) {
if((dap_state.semaphore_cdc == NULL) || (dap_state.connected == false)) return 0;
furi_check(furi_semaphore_acquire(dap_state.semaphore_cdc, FuriWaitForever) == FuriStatusOk);
if(dap_state.connected) {
int32_t len = usbd_ep_write(dap_state.usb_dev, HID_EP_IN | DAP_CDC_EP_SEND, buffer, size);
furi_console_log_printf("cdc tx %ld", len);
return len;
} else {
return 0;
}
}
void dap_v1_usb_set_rx_callback(DapRxCallback callback) {
dap_state.rx_callback_v1 = callback;
}
void dap_v2_usb_set_rx_callback(DapRxCallback callback) {
dap_state.rx_callback_v2 = callback;
}
void dap_cdc_usb_set_rx_callback(DapRxCallback callback) {
dap_state.rx_callback_cdc = callback;
}
void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback) {
dap_state.control_line_callback_cdc = callback;
}
void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback) {
dap_state.config_callback_cdc = callback;
}
void dap_cdc_usb_set_context(void* context) {
dap_state.context_cdc = context;
}
void dap_common_usb_set_context(void* context) {
dap_state.context = context;
}
void dap_common_usb_set_state_callback(DapStateCallback callback) {
dap_state.state_callback = callback;
}
static void* dap_usb_alloc_string_descr(const char* str) {
furi_assert(str);
uint8_t len = strlen(str);
uint8_t wlen = (len + 1) * sizeof(uint16_t);
struct usb_string_descriptor* dev_str_desc = malloc(wlen);
dev_str_desc->bLength = wlen;
dev_str_desc->bDescriptorType = USB_DTYPE_STRING;
for(uint8_t i = 0; i < len; i++) {
dev_str_desc->wString[i] = str[i];
}
return dev_str_desc;
}
void dap_common_usb_alloc_name(const char* name) {
dev_serial_descr = dap_usb_alloc_string_descr(name);
}
void dap_common_usb_free_name() {
free(dev_serial_descr);
}
static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx);
static void hid_deinit(usbd_device* dev);
static void hid_on_wakeup(usbd_device* dev);
static void hid_on_suspend(usbd_device* dev);
static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg);
static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
FuriHalUsbInterface dap_v2_usb_hid = {
.init = hid_init,
.deinit = hid_deinit,
.wakeup = hid_on_wakeup,
.suspend = hid_on_suspend,
.dev_descr = (struct usb_device_descriptor*)&hid_device_desc,
.cfg_descr = (void*)&hid_cfg_desc,
};
static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
UNUSED(intf);
UNUSED(ctx);
dap_v2_usb_hid.str_manuf_descr = (void*)&dev_manuf_descr;
dap_v2_usb_hid.str_prod_descr = (void*)&dev_prod_descr;
dap_v2_usb_hid.str_serial_descr = (void*)dev_serial_descr;
dap_state.usb_dev = dev;
if(dap_state.semaphore_v1 == NULL) dap_state.semaphore_v1 = furi_semaphore_alloc(1, 1);
if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1);
if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1);
usb_hid.dev_descr->idVendor = DAP_HID_VID;
usb_hid.dev_descr->idProduct = DAP_HID_PID;
usbd_reg_config(dev, hid_ep_config);
usbd_reg_control(dev, hid_control);
usbd_connect(dev, true);
}
static bool deinit_flag = false;
void dap_common_wait_for_deinit() {
while(!deinit_flag) {
furi_delay_ms(50);
}
}
static void hid_deinit(usbd_device* dev) {
dap_state.usb_dev = NULL;
furi_semaphore_free(dap_state.semaphore_v1);
furi_semaphore_free(dap_state.semaphore_v2);
furi_semaphore_free(dap_state.semaphore_cdc);
dap_state.semaphore_v1 = NULL;
dap_state.semaphore_v2 = NULL;
dap_state.semaphore_cdc = NULL;
usbd_reg_config(dev, NULL);
usbd_reg_control(dev, NULL);
free(usb_hid.str_manuf_descr);
free(usb_hid.str_prod_descr);
FURI_SW_MEMBARRIER();
deinit_flag = true;
}
static void hid_on_wakeup(usbd_device* dev) {
UNUSED(dev);
if(!dap_state.connected) {
dap_state.connected = true;
if(dap_state.state_callback != NULL) {
dap_state.state_callback(dap_state.connected, dap_state.context);
}
}
}
static void hid_on_suspend(usbd_device* dev) {
UNUSED(dev);
if(dap_state.connected) {
dap_state.connected = false;
if(dap_state.state_callback != NULL) {
dap_state.state_callback(dap_state.connected, dap_state.context);
}
}
}
size_t dap_v1_usb_rx(uint8_t* buffer, size_t size) {
size_t len = 0;
if(dap_state.connected) {
len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_OUT, buffer, size);
}
return len;
}
size_t dap_v2_usb_rx(uint8_t* buffer, size_t size) {
size_t len = 0;
if(dap_state.connected) {
len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_BULK_OUT, buffer, size);
}
return len;
}
size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size) {
size_t len = 0;
if(dap_state.connected) {
len = usbd_ep_read(dap_state.usb_dev, HID_EP_OUT | DAP_CDC_EP_RECV, buffer, size);
}
return len;
}
static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
UNUSED(dev);
UNUSED(ep);
switch(event) {
case usbd_evt_eptx:
furi_semaphore_release(dap_state.semaphore_v1);
furi_console_log_printf("hid tx complete");
break;
case usbd_evt_eprx:
if(dap_state.rx_callback_v1 != NULL) {
dap_state.rx_callback_v1(dap_state.context);
}
break;
default:
furi_console_log_printf("hid %d, %d", event, ep);
break;
}
}
static void hid_txrx_ep_bulk_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
UNUSED(dev);
UNUSED(ep);
switch(event) {
case usbd_evt_eptx:
furi_semaphore_release(dap_state.semaphore_v2);
furi_console_log_printf("bulk tx complete");
break;
case usbd_evt_eprx:
if(dap_state.rx_callback_v2 != NULL) {
dap_state.rx_callback_v2(dap_state.context);
}
break;
default:
furi_console_log_printf("bulk %d, %d", event, ep);
break;
}
}
static void cdc_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
UNUSED(dev);
UNUSED(ep);
switch(event) {
case usbd_evt_eptx:
furi_semaphore_release(dap_state.semaphore_cdc);
furi_console_log_printf("cdc tx complete");
break;
case usbd_evt_eprx:
if(dap_state.rx_callback_cdc != NULL) {
dap_state.rx_callback_cdc(dap_state.context_cdc);
}
break;
default:
furi_console_log_printf("cdc %d, %d", event, ep);
break;
}
}
static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) {
switch(cfg) {
case EP_CFG_DECONFIGURE:
usbd_ep_deconfig(dev, DAP_HID_EP_OUT);
usbd_ep_deconfig(dev, DAP_HID_EP_IN);
usbd_ep_deconfig(dev, DAP_HID_EP_BULK_IN);
usbd_ep_deconfig(dev, DAP_HID_EP_BULK_OUT);
usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_COMM);
usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_SEND);
usbd_ep_deconfig(dev, HID_EP_OUT | DAP_CDC_EP_RECV);
usbd_reg_endpoint(dev, DAP_HID_EP_OUT, NULL);
usbd_reg_endpoint(dev, DAP_HID_EP_IN, NULL);
usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, NULL);
usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, NULL);
usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, 0);
usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, 0);
return usbd_ack;
case EP_CFG_CONFIGURE:
usbd_ep_config(dev, DAP_HID_EP_IN, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE);
usbd_ep_config(dev, DAP_HID_EP_OUT, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE);
usbd_ep_config(dev, DAP_HID_EP_BULK_OUT, USB_EPTYPE_BULK, DAP_HID_EP_SIZE);
usbd_ep_config(dev, DAP_HID_EP_BULK_IN, USB_EPTYPE_BULK, DAP_HID_EP_SIZE);
usbd_ep_config(dev, HID_EP_OUT | DAP_CDC_EP_RECV, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE);
usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_SEND, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE);
usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_COMM, USB_EPTYPE_INTERRUPT, DAP_CDC_EP_SIZE);
usbd_reg_endpoint(dev, DAP_HID_EP_IN, hid_txrx_ep_callback);
usbd_reg_endpoint(dev, DAP_HID_EP_OUT, hid_txrx_ep_callback);
usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, hid_txrx_ep_bulk_callback);
usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, hid_txrx_ep_bulk_callback);
usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, cdc_txrx_ep_callback);
usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, cdc_txrx_ep_callback);
// usbd_ep_write(dev, DAP_HID_EP_IN, NULL, 0);
// usbd_ep_write(dev, DAP_HID_EP_BULK_IN, NULL, 0);
// usbd_ep_write(dev, HID_EP_IN | DAP_CDC_EP_SEND, NULL, 0);
return usbd_ack;
default:
return usbd_fail;
}
}
#ifdef DAP_USB_LOG
static void dump_request_type(uint8_t type) {
switch(type & USB_REQ_DIRECTION) {
case USB_REQ_HOSTTODEV:
furi_hal_console_puts("host to dev, ");
break;
case USB_REQ_DEVTOHOST:
furi_hal_console_puts("dev to host, ");
break;
}
switch(type & USB_REQ_TYPE) {
case USB_REQ_STANDARD:
furi_hal_console_puts("standard, ");
break;
case USB_REQ_CLASS:
furi_hal_console_puts("class, ");
break;
case USB_REQ_VENDOR:
furi_hal_console_puts("vendor, ");
break;
}
switch(type & USB_REQ_RECIPIENT) {
case USB_REQ_DEVICE:
furi_hal_console_puts("device");
break;
case USB_REQ_INTERFACE:
furi_hal_console_puts("interface");
break;
case USB_REQ_ENDPOINT:
furi_hal_console_puts("endpoint");
break;
case USB_REQ_OTHER:
furi_hal_console_puts("other");
break;
}
furi_hal_console_puts("\r\n");
}
#else
#define dump_request_type(...)
#endif
static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
UNUSED(callback);
dump_request_type(req->bmRequestType);
furi_console_log_printf(
"control: RT %02x, R %02x, V %04x, I %04x, L %04x",
req->bmRequestType,
req->bRequest,
req->wValue,
req->wIndex,
req->wLength);
if(((USB_REQ_RECIPIENT | USB_REQ_TYPE | USB_REQ_DIRECTION) & req->bmRequestType) ==
(USB_REQ_STANDARD | USB_REQ_VENDOR | USB_REQ_DEVTOHOST)) {
// vendor request, device to host
furi_console_log_printf("vendor request");
if(USB_WINUSB_VENDOR_CODE == req->bRequest) {
// WINUSB request
if(USB_WINUSB_DESCRIPTOR_INDEX == req->wIndex) {
furi_console_log_printf("WINUSB descriptor");
uint16_t length = req->wLength;
if(length > sizeof(usb_msos_descriptor_set_t)) {
length = sizeof(usb_msos_descriptor_set_t);
}
dev->status.data_ptr = (uint8_t*)&usb_msos_descriptor_set;
dev->status.data_count = length;
return usbd_ack;
}
}
}
if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
(USB_REQ_STANDARD | USB_REQ_DEVICE)) {
// device request
if(req->bRequest == USB_STD_GET_DESCRIPTOR) {
const uint8_t dtype = req->wValue >> 8;
const uint8_t dnumber = req->wValue & 0xFF;
// get string descriptor
if(USB_DTYPE_STRING == dtype) {
if(dnumber == USB_STR_CMSIS_DAP_V1) {
furi_console_log_printf("str CMSIS-DAP v1");
dev->status.data_ptr = (uint8_t*)&dev_dap_v1_descr;
dev->status.data_count = dev_dap_v1_descr.bLength;
return usbd_ack;
} else if(dnumber == USB_STR_CMSIS_DAP_V2) {
furi_console_log_printf("str CMSIS-DAP v2");
dev->status.data_ptr = (uint8_t*)&dev_dap_v2_descr;
dev->status.data_count = dev_dap_v2_descr.bLength;
return usbd_ack;
} else if(dnumber == USB_STR_COM_PORT) {
furi_console_log_printf("str COM port");
dev->status.data_ptr = (uint8_t*)&dev_com_descr;
dev->status.data_count = dev_com_descr.bLength;
return usbd_ack;
}
} else if(USB_DTYPE_BINARY_OBJECT_STORE == dtype) {
furi_console_log_printf("BOS descriptor");
uint16_t length = req->wLength;
if(length > sizeof(usb_bos_hierarchy_t)) {
length = sizeof(usb_bos_hierarchy_t);
}
dev->status.data_ptr = (uint8_t*)&usb_bos_hierarchy;
dev->status.data_count = length;
return usbd_ack;
}
}
}
if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
(USB_REQ_INTERFACE | USB_REQ_CLASS) &&
req->wIndex == 0) {
// class request
switch(req->bRequest) {
// get hid descriptor
case USB_HID_GETREPORT:
furi_console_log_printf("get report");
return usbd_fail;
// set hid idle
case USB_HID_SETIDLE:
furi_console_log_printf("set idle");
return usbd_ack;
default:
break;
}
}
if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
(USB_REQ_INTERFACE | USB_REQ_CLASS) &&
req->wIndex == 2) {
// class request
switch(req->bRequest) {
// control line state
case USB_CDC_SET_CONTROL_LINE_STATE:
furi_console_log_printf("set control line state");
cdc_ctrl_line_state = req->wValue;
if(dap_state.control_line_callback_cdc != NULL) {
dap_state.control_line_callback_cdc(cdc_ctrl_line_state, dap_state.context_cdc);
}
return usbd_ack;
// set cdc line coding
case USB_CDC_SET_LINE_CODING:
furi_console_log_printf("set line coding");
memcpy(&cdc_config, req->data, sizeof(cdc_config));
if(dap_state.config_callback_cdc != NULL) {
dap_state.config_callback_cdc(&cdc_config, dap_state.context_cdc);
}
return usbd_ack;
// get cdc line coding
case USB_CDC_GET_LINE_CODING:
furi_console_log_printf("get line coding");
dev->status.data_ptr = &cdc_config;
dev->status.data_count = sizeof(cdc_config);
return usbd_ack;
default:
break;
}
}
if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
(USB_REQ_INTERFACE | USB_REQ_STANDARD) &&
req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) {
// standard request
switch(req->wValue >> 8) {
// get hid descriptor
case USB_DTYPE_HID:
furi_console_log_printf("get hid descriptor");
dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.hid);
dev->status.data_count = sizeof(hid_cfg_desc.hid);
return usbd_ack;
// get hid report descriptor
case USB_DTYPE_HID_REPORT:
furi_console_log_printf("get hid report descriptor");
dev->status.data_ptr = (uint8_t*)hid_report_desc;
dev->status.data_count = sizeof(hid_report_desc);
return usbd_ack;
default:
break;
}
}
return usbd_fail;
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <furi_hal_usb.h>
#include <usb_cdc.h>
extern FuriHalUsbInterface dap_v2_usb_hid;
// receive callback type
typedef void (*DapRxCallback)(void* context);
typedef void (*DapStateCallback)(bool state, void* context);
/************************************ V1 ***************************************/
int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size);
size_t dap_v1_usb_rx(uint8_t* buffer, size_t size);
void dap_v1_usb_set_rx_callback(DapRxCallback callback);
/************************************ V2 ***************************************/
int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size);
size_t dap_v2_usb_rx(uint8_t* buffer, size_t size);
void dap_v2_usb_set_rx_callback(DapRxCallback callback);
/************************************ CDC **************************************/
typedef void (*DapCDCControlLineCallback)(uint8_t state, void* context);
typedef void (*DapCDCConfigCallback)(struct usb_cdc_line_coding* config, void* context);
int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size);
size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size);
void dap_cdc_usb_set_rx_callback(DapRxCallback callback);
void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback);
void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback);
void dap_cdc_usb_set_context(void* context);
/*********************************** Common ************************************/
void dap_common_usb_set_context(void* context);
void dap_common_usb_set_state_callback(DapStateCallback callback);
void dap_common_usb_alloc_name(const char* name);
void dap_common_usb_free_name();
void dap_common_wait_for_deinit();

View File

@@ -0,0 +1,143 @@
#pragma once
#include <stdint.h>
/*- Definitions -------------------------------------------------------------*/
#define USB_PACK __attribute__((packed))
#define USB_WINUSB_VENDOR_CODE 0x20
#define USB_WINUSB_WINDOWS_VERSION 0x06030000 // Windows 8.1
#define USB_WINUSB_PLATFORM_CAPABILITY_ID \
{ \
0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, 0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, \
0x9f \
}
enum // WinUSB Microsoft OS 2.0 descriptor request codes
{
USB_WINUSB_DESCRIPTOR_INDEX = 0x07,
USB_WINUSB_SET_ALT_ENUMERATION = 0x08,
};
enum // wDescriptorType
{
USB_WINUSB_SET_HEADER_DESCRIPTOR = 0x00,
USB_WINUSB_SUBSET_HEADER_CONFIGURATION = 0x01,
USB_WINUSB_SUBSET_HEADER_FUNCTION = 0x02,
USB_WINUSB_FEATURE_COMPATBLE_ID = 0x03,
USB_WINUSB_FEATURE_REG_PROPERTY = 0x04,
USB_WINUSB_FEATURE_MIN_RESUME_TIME = 0x05,
USB_WINUSB_FEATURE_MODEL_ID = 0x06,
USB_WINUSB_FEATURE_CCGP_DEVICE = 0x07,
USB_WINUSB_FEATURE_VENDOR_REVISION = 0x08,
};
enum // wPropertyDataType
{
USB_WINUSB_PROPERTY_DATA_TYPE_SZ = 1,
USB_WINUSB_PROPERTY_DATA_TYPE_EXPAND_SZ = 2,
USB_WINUSB_PROPERTY_DATA_TYPE_BINARY = 3,
USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_LITTLE_ENDIAN = 4,
USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_BIG_ENDIAN = 5,
USB_WINUSB_PROPERTY_DATA_TYPE_LINK = 6,
USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ = 7,
};
/*- Types BOS -------------------------------------------------------------------*/
typedef struct USB_PACK {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumDeviceCaps;
} usb_binary_object_store_descriptor_t;
/*- Types WinUSB -------------------------------------------------------------------*/
typedef struct USB_PACK {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDevCapabilityType;
uint8_t bReserved;
uint8_t PlatformCapabilityUUID[16];
uint32_t dwWindowsVersion;
uint16_t wMSOSDescriptorSetTotalLength;
uint8_t bMS_VendorCode;
uint8_t bAltEnumCode;
} usb_winusb_capability_descriptor_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint32_t dwWindowsVersion;
uint16_t wDescriptorSetTotalLength;
} usb_winusb_set_header_descriptor_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint8_t bConfigurationValue;
uint8_t bReserved;
uint16_t wTotalLength;
} usb_winusb_subset_header_configuration_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint8_t bFirstInterface;
uint8_t bReserved;
uint16_t wSubsetLength;
} usb_winusb_subset_header_function_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint8_t CompatibleID[8];
uint8_t SubCompatibleID[8];
} usb_winusb_feature_compatble_id_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint16_t wPropertyDataType;
//uint16_t wPropertyNameLength;
//uint8_t PropertyName[...];
//uint16_t wPropertyDataLength
//uint8_t PropertyData[...];
} usb_winusb_feature_reg_property_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint16_t wPropertyDataType;
uint16_t wPropertyNameLength;
uint8_t PropertyName[42];
uint16_t wPropertyDataLength;
uint8_t PropertyData[80];
} usb_winusb_feature_reg_property_guids_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint8_t bResumeRecoveryTime;
uint8_t bResumeSignalingTime;
} usb_winusb_feature_min_resume_time_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint8_t ModelID[16];
} usb_winusb_feature_model_id_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
} usb_winusb_feature_ccgp_device_t;
typedef struct USB_PACK {
uint16_t wLength;
uint16_t wDescriptorType;
uint16_t VendorRevision;
} usb_winusb_feature_vendor_revision_t;

View File

@@ -0,0 +1,151 @@
#include "iclass_elite_dict.h"
#include <lib/toolbox/args.h>
#include <lib/flipper_format/flipper_format.h>
#define ICLASS_ELITE_DICT_FLIPPER_PATH EXT_PATH("picopass/assets/iclass_elite_dict.txt")
#define ICLASS_ELITE_DICT_USER_PATH EXT_PATH("picopass/assets/iclass_elite_dict_user.txt")
#define TAG "IclassEliteDict"
#define ICLASS_ELITE_KEY_LINE_LEN (17)
#define ICLASS_ELITE_KEY_LEN (8)
struct IclassEliteDict {
Stream* stream;
uint32_t total_keys;
};
bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool dict_present = false;
if(dict_type == IclassEliteDictTypeFlipper) {
dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) ==
FSE_OK;
} else if(dict_type == IclassEliteDictTypeUser) {
dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK;
}
furi_record_close(RECORD_STORAGE);
return dict_present;
}
IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) {
IclassEliteDict* dict = malloc(sizeof(IclassEliteDict));
Storage* storage = furi_record_open(RECORD_STORAGE);
dict->stream = buffered_file_stream_alloc(storage);
furi_record_close(RECORD_STORAGE);
FuriString* next_line = furi_string_alloc();
bool dict_loaded = false;
do {
if(dict_type == IclassEliteDictTypeFlipper) {
if(!buffered_file_stream_open(
dict->stream, ICLASS_ELITE_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
buffered_file_stream_close(dict->stream);
break;
}
} else if(dict_type == IclassEliteDictTypeUser) {
if(!buffered_file_stream_open(
dict->stream, ICLASS_ELITE_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
buffered_file_stream_close(dict->stream);
break;
}
}
// Read total amount of keys
while(true) {
if(!stream_read_line(dict->stream, next_line)) break;
if(furi_string_get_char(next_line, 0) == '#') continue;
if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue;
dict->total_keys++;
}
furi_string_reset(next_line);
stream_rewind(dict->stream);
dict_loaded = true;
FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys);
} while(false);
if(!dict_loaded) {
buffered_file_stream_close(dict->stream);
free(dict);
dict = NULL;
}
furi_string_free(next_line);
return dict;
}
void iclass_elite_dict_free(IclassEliteDict* dict) {
furi_assert(dict);
furi_assert(dict->stream);
buffered_file_stream_close(dict->stream);
stream_free(dict->stream);
free(dict);
}
uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict) {
furi_assert(dict);
return dict->total_keys;
}
bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
uint8_t key_byte_tmp = 0;
FuriString* next_line = furi_string_alloc();
bool key_read = false;
*key = 0ULL;
while(!key_read) {
if(!stream_read_line(dict->stream, next_line)) break;
if(furi_string_get_char(next_line, 0) == '#') continue;
if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue;
for(uint8_t i = 0; i < ICLASS_ELITE_KEY_LEN * 2; i += 2) {
args_char_to_hex(
furi_string_get_char(next_line, i),
furi_string_get_char(next_line, i + 1),
&key_byte_tmp);
key[i / 2] = key_byte_tmp;
}
key_read = true;
}
furi_string_free(next_line);
return key_read;
}
bool iclass_elite_dict_rewind(IclassEliteDict* dict) {
furi_assert(dict);
furi_assert(dict->stream);
return stream_rewind(dict->stream);
}
bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
FuriString* key_str = furi_string_alloc();
for(size_t i = 0; i < 6; i++) {
furi_string_cat_printf(key_str, "%02X", key[i]);
}
furi_string_cat_printf(key_str, "\n");
bool key_added = false;
do {
if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
if(!stream_insert_string(dict->stream, key_str)) break;
key_added = true;
} while(false);
furi_string_free(key_str);
return key_added;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <stdbool.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
#include <lib/toolbox/stream/file_stream.h>
#include <lib/toolbox/stream/buffered_file_stream.h>
typedef enum {
IclassEliteDictTypeUser,
IclassEliteDictTypeFlipper,
} IclassEliteDictType;
typedef struct IclassEliteDict IclassEliteDict;
bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type);
IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type);
void iclass_elite_dict_free(IclassEliteDict* dict);
uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict);
bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key);
bool iclass_elite_dict_rewind(IclassEliteDict* dict);
bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key);

View File

@@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8
* @param loclass_hash1 loclass_hash1 * @param loclass_hash1 loclass_hash1
* @param key_sel output key_sel=h[loclass_hash1[i]] * @param key_sel output key_sel=h[loclass_hash1[i]]
*/ */
void hash2(uint8_t* key64, uint8_t* outp_keytable) { void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) {
/** /**
*Expected: *Expected:
* High Security Key Table * High Security Key Table

View File

@@ -9,6 +9,7 @@
#include "rfal_picopass.h" #include "rfal_picopass.h"
#include <optimized_ikeys.h> #include <optimized_ikeys.h>
#include <optimized_cipher.h> #include <optimized_cipher.h>
#include "helpers/iclass_elite_dict.h"
#define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_DEV_NAME_MAX_LEN 22
#define PICOPASS_READER_DATA_MAX_SIZE 64 #define PICOPASS_READER_DATA_MAX_SIZE 64
@@ -49,6 +50,7 @@ typedef struct {
bool se_enabled; bool se_enabled;
bool sio; bool sio;
bool biometrics; bool biometrics;
uint8_t key[8];
uint8_t pin_length; uint8_t pin_length;
PicopassEncryption encryption; PicopassEncryption encryption;
uint8_t credential[8]; uint8_t credential[8];

View File

@@ -1,5 +1,7 @@
#include "picopass_worker_i.h" #include "picopass_worker_i.h"
#include <flipper_format/flipper_format.h>
#define TAG "PicopassWorker" #define TAG "PicopassWorker"
const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
@@ -176,7 +178,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) {
return ERR_NONE; return ERR_NONE;
} }
ReturnCode picopass_read_card(PicopassBlock* AA1) { ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
rfalPicoPassReadCheckRes rcRes; rfalPicoPassReadCheckRes rcRes;
rfalPicoPassCheckRes chkRes; rfalPicoPassCheckRes chkRes;
@@ -197,10 +199,68 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) {
loclass_opt_doReaderMAC(ccnr, div_key, mac); loclass_opt_doReaderMAC(ccnr, div_key, mac);
err = rfalPicoPassPollerCheck(mac, &chkRes); err = rfalPicoPassPollerCheck(mac, &chkRes);
if(err != ERR_NONE) { if(err == ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); return ERR_NONE;
return err;
} }
FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
FURI_LOG_E(TAG, "Starting dictionary attack");
size_t index = 0;
uint8_t key[PICOPASS_BLOCK_LEN] = {0};
if(!iclass_elite_dict_check_presence(IclassEliteDictTypeFlipper)) {
FURI_LOG_E(TAG, "Dictionary not found");
return ERR_PARAM;
}
IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper);
if(!dict) {
FURI_LOG_E(TAG, "Dictionary not allocated");
return ERR_PARAM;
}
FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict));
while(iclass_elite_dict_get_next_key(dict, key)) {
FURI_LOG_D(
TAG,
"Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x",
index++,
key[0],
key[1],
key[2],
key[3],
key[4],
key[5],
key[6],
key[7]);
err = rfalPicoPassPollerReadCheck(&rcRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
return err;
}
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
loclass_iclass_calc_div_key(AA1[PICOPASS_CSN_BLOCK_INDEX].data, key, div_key, true);
loclass_opt_doReaderMAC(ccnr, div_key, mac);
err = rfalPicoPassPollerCheck(mac, &chkRes);
if(err == ERR_NONE) {
memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
break;
}
}
if(dict) {
iclass_elite_dict_free(dict);
}
return err;
}
ReturnCode picopass_read_card(PicopassBlock* AA1) {
ReturnCode err;
size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ?
AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] :
@@ -352,28 +412,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
if(pacs->se_enabled) { if(pacs->se_enabled) {
FURI_LOG_D(TAG, "SE enabled"); FURI_LOG_D(TAG, "SE enabled");
nextState = PicopassWorkerEventFail;
} }
err = picopass_read_card(AA1); if(nextState == PicopassWorkerEventSuccess) {
if(err != ERR_NONE) { err = picopass_auth(AA1, pacs);
FURI_LOG_E(TAG, "picopass_read_card error %d", err); if(err != ERR_NONE) {
nextState = PicopassWorkerEventFail; FURI_LOG_E(TAG, "picopass_try_auth error %d", err);
nextState = PicopassWorkerEventFail;
}
}
if(nextState == PicopassWorkerEventSuccess) {
err = picopass_read_card(AA1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
nextState = PicopassWorkerEventFail;
}
} }
if(nextState == PicopassWorkerEventSuccess) { if(nextState == PicopassWorkerEventSuccess) {
err = picopass_device_parse_credential(AA1, pacs); err = picopass_device_parse_credential(AA1, pacs);
} if(err != ERR_NONE) {
if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); nextState = PicopassWorkerEventFail;
nextState = PicopassWorkerEventFail; }
} }
if(nextState == PicopassWorkerEventSuccess) { if(nextState == PicopassWorkerEventSuccess) {
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
} if(err != ERR_NONE) {
if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); nextState = PicopassWorkerEventFail;
nextState = PicopassWorkerEventFail; }
} }
// Notify caller and exit // Notify caller and exit

View File

@@ -18,6 +18,7 @@
struct PicopassWorker { struct PicopassWorker {
FuriThread* thread; FuriThread* thread;
Storage* storage; Storage* storage;
Stream* dict_stream;
PicopassDeviceData* dev_data; PicopassDeviceData* dev_data;
PicopassWorkerCallback callback; PicopassWorkerCallback callback;

View File

@@ -15,12 +15,10 @@ void picopass_scene_read_card_success_widget_callback(
void picopass_scene_read_card_success_on_enter(void* context) { void picopass_scene_read_card_success_on_enter(void* context) {
Picopass* picopass = context; Picopass* picopass = context;
FuriString* credential_str; FuriString* csn_str = furi_string_alloc_set("CSN:");
FuriString* wiegand_str; FuriString* credential_str = furi_string_alloc();
FuriString* sio_str; FuriString* wiegand_str = furi_string_alloc();
credential_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc();
wiegand_str = furi_string_alloc();
sio_str = furi_string_alloc();
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
@@ -28,10 +26,18 @@ void picopass_scene_read_card_success_on_enter(void* context) {
notification_message(picopass->notifications, &sequence_success); notification_message(picopass->notifications, &sequence_success);
// Setup view // Setup view
PicopassBlock* AA1 = picopass->dev->dev_data.AA1;
PicopassPacs* pacs = &picopass->dev->dev_data.pacs; PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
Widget* widget = picopass->widget; Widget* widget = picopass->widget;
if(pacs->record.bitLength == 0) { uint8_t csn[PICOPASS_BLOCK_LEN];
memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(csn_str, " %02X", csn[i]);
}
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff
if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
furi_string_cat_printf(wiegand_str, "Read Failed"); furi_string_cat_printf(wiegand_str, "Read Failed");
if(pacs->se_enabled) { if(pacs->se_enabled) {
@@ -79,18 +85,21 @@ void picopass_scene_read_card_success_on_enter(void* context) {
} }
widget_add_string_element( widget_add_string_element(
widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str));
widget_add_string_element(
widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str));
widget_add_string_element( widget_add_string_element(
widget, widget,
64, 64,
32, 36,
AlignCenter, AlignCenter,
AlignCenter, AlignCenter,
FontSecondary, FontSecondary,
furi_string_get_cstr(credential_str)); furi_string_get_cstr(credential_str));
widget_add_string_element( widget_add_string_element(
widget, 64, 42, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str));
furi_string_free(csn_str);
furi_string_free(credential_str); furi_string_free(credential_str);
furi_string_free(wiegand_str); furi_string_free(wiegand_str);
furi_string_free(sio_str); furi_string_free(sio_str);

View File

@@ -2,6 +2,7 @@
#include <gui/gui.h> #include <gui/gui.h>
#include <input/input.h> #include <input/input.h>
#include <stdlib.h> #include <stdlib.h>
#include <dolphin/dolphin.h>
#include <notification/notification.h> #include <notification/notification.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
@@ -346,6 +347,8 @@ int32_t snake_game_app(void* p) {
notification_message_block(notification, &sequence_display_backlight_enforce_on); notification_message_block(notification, &sequence_display_backlight_enforce_on);
DOLPHIN_DEED(DolphinDeedPluginGameStart);
SnakeEvent event; SnakeEvent event;
for(bool processing = true; processing;) { for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);

View File

@@ -0,0 +1,13 @@
App(
appid="weather_station",
name="Weather Station",
apptype=FlipperAppType.PLUGIN,
entry_point="weather_station_app",
cdefines=["APP_WEATHER_STATION"],
requires=["gui"],
stack_size=4 * 1024,
order=50,
fap_icon="weather_station_10px.png",
fap_category="Tools",
fap_icon_assets="images",
)

Some files were not shown because too many files have changed in this diff Show More