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

Merge branch 'dev' into release

This commit is contained in:
MX
2023-03-21 02:59:07 +03:00
99 changed files with 1101 additions and 957 deletions

View File

@@ -1,11 +1,13 @@
### New changes
* iButton: Add support for DS1420 (not fully tested)
* Desktop: Option to set Tertiary favourite app (Hold right arrow on desktop) - **Desktop settings (pin code, favourite app, autolock time) will be resetted!!! Only one time after installing - due to changes in settings structure, after installing of this release desktop settings will not be reset with next release**
* Desktop: Ability to disable favourite app (use last element in a main list `None (disable)`)
* Desktop: Fix issue #387 (Ok button was showing invalid file error in dummy mode)
* OFW PR: SD Driver: reinit sd card on error (by DrZlo13 | OFW PR 2493)
* OFW PR: iButton -> Fix a typo: use right shift instead of greater than (by gsurkov | OFW PR 2506)
* OFW: Optimize trailing slash check
* Plugins: Fix minesweeper freeze bugs, do some refactoring
* Plugins: Update WAV Player, 16 bit support by @LTVA1
* OFW PR: Fixed writing gen1a magic tags with invalid BCC [(PR 2511 by AloneLiberty)](https://github.com/AloneLiberty/flipperzero-firmware/tree/nfc-magic-write-fix)
* OFW: BadUSB: Script interpreter refactoring
* OFW: SubGhz: fix Incorrect comparison in subghz_setting_get_hopper_frequency
* OFW: Add one_wire lib to f18, separate ibutton
* OFW: Improved debugging experience for external apps
* OFW: SD Driver: reinit sd card on error
* OFW: OTP programmer: return exit code based on error type
#### [🎲 Download latest extra apps pack](https://github.com/xMasterX/unleashed-extra-pack/archive/refs/heads/main.zip)

View File

@@ -17,8 +17,8 @@
#include <iBtn_Fuzzer_icons.h>
#include <lib/one_wire/ibutton/ibutton_worker.h>
#include <lib/one_wire/ibutton/ibutton_key.h>
#include <lib/ibutton/ibutton_worker.h>
#include <lib/ibutton/ibutton_key.h>
#define TAG "iBtnFuzzer"

View File

@@ -47,9 +47,11 @@ typedef enum {
typedef enum { FieldEmpty, FieldMine } Field;
typedef struct {
FuriMutex* mutex;
DialogsApp* dialogs;
NotificationApp* notifications;
Field minefield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
TileType playfield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
FuriTimer* timer;
int cursor_x;
int cursor_y;
int mines_left;
@@ -57,16 +59,8 @@ typedef struct {
int flags_set;
bool game_started;
uint32_t game_started_tick;
FuriMutex* mutex;
} Minesweeper;
static void timer_callback(void* ctx) {
UNUSED(ctx);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message(notification, &sequence_reset_vibro);
furi_record_close(RECORD_NOTIFICATION);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
@@ -179,7 +173,7 @@ static void setup_playfield(Minesweeper* minesweeper_state) {
int rand_y = rand() % PLAYFIELD_HEIGHT;
// make sure first guess isn't a mine
if(minesweeper_state->minefield[rand_x][rand_y] == FieldEmpty &&
(minesweeper_state->cursor_x != rand_x && minesweeper_state->cursor_y != rand_y)) {
(minesweeper_state->cursor_x != rand_x || minesweeper_state->cursor_y != rand_y)) {
minesweeper_state->minefield[rand_x][rand_y] = FieldMine;
mines_left--;
}
@@ -208,37 +202,25 @@ static void place_flag(Minesweeper* minesweeper_state) {
static bool game_lost(Minesweeper* minesweeper_state) {
// returns true if the player wants to restart, otherwise false
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc();
const char* header_text = "Game Over";
const char* message_text = "You hit a mine!";
dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop);
dialog_message_set_text(message, message_text, 64, 32, AlignCenter, AlignCenter);
dialog_message_set_header(message, "Game Over", 64, 3, AlignCenter, AlignTop);
dialog_message_set_text(message, "You hit a mine!", 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, NULL, "Play again", NULL);
dialog_message_set_icon(message, NULL, 0, 10);
// Set cursor to initial position
minesweeper_state->cursor_x = 0;
minesweeper_state->cursor_y = 0;
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
notification_message(notifications, &sequence_set_vibro_on);
furi_record_close(RECORD_NOTIFICATION);
furi_timer_start(minesweeper_state->timer, (uint32_t)furi_kernel_get_tick_frequency() * 0.2);
notification_message(minesweeper_state->notifications, &sequence_single_vibro);
DialogMessageButton choice = dialog_message_show(dialogs, message);
DialogMessageButton choice = dialog_message_show(minesweeper_state->dialogs, message);
dialog_message_free(message);
furi_record_close(RECORD_DIALOGS);
return choice == DialogMessageButtonCenter;
}
static bool game_won(Minesweeper* minesweeper_state) {
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
FuriString* tempStr;
tempStr = furi_string_alloc();
@@ -256,15 +238,13 @@ static bool game_won(Minesweeper* minesweeper_state) {
dialog_message_set_text(
message, furi_string_get_cstr(tempStr), 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, NULL, "Play again", NULL);
dialog_message_set_icon(message, NULL, 72, 17);
// Call dolphin deed when we win the game
DOLPHIN_DEED(DolphinDeedPluginGameWin);
DialogMessageButton choice = dialog_message_show(dialogs, message);
DialogMessageButton choice = dialog_message_show(minesweeper_state->dialogs, message);
dialog_message_free(message);
furi_string_free(tempStr);
furi_record_close(RECORD_DIALOGS);
return choice == DialogMessageButtonCenter;
}
@@ -375,22 +355,6 @@ static void minesweeper_state_init(Minesweeper* const minesweeper_state) {
int32_t minesweeper_app(void* p) {
UNUSED(p);
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc();
const char* header_text = "Minesweeper";
const char* message_text = "Hold OK pressed to toggle flags.\ngithub.com/panki27";
dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop);
dialog_message_set_text(message, message_text, 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, NULL, "Play", NULL);
dialog_message_set_icon(message, NULL, 0, 10);
dialog_message_show(dialogs, message);
dialog_message_free(message);
furi_record_close(RECORD_DIALOGS);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
Minesweeper* minesweeper_state = malloc(sizeof(Minesweeper));
@@ -405,12 +369,28 @@ int32_t minesweeper_app(void* p) {
}
// BEGIN IMPLEMENTATION
minesweeper_state->dialogs = furi_record_open(RECORD_DIALOGS);
minesweeper_state->notifications = furi_record_open(RECORD_NOTIFICATION);
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Minesweeper", 64, 3, AlignCenter, AlignTop);
dialog_message_set_text(
message,
"Hold OK pressed to toggle flags.\ngithub.com/panki27",
64,
32,
AlignCenter,
AlignCenter);
dialog_message_set_buttons(message, NULL, "Play", NULL);
dialog_message_show(minesweeper_state->dialogs, message);
dialog_message_free(message);
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, minesweeper_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
minesweeper_state->timer =
furi_timer_alloc(timer_callback, FuriTimerTypeOnce, minesweeper_state);
// Open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
@@ -422,35 +402,42 @@ int32_t minesweeper_app(void* p) {
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypeShort) {
switch(event.input.key) {
case InputKeyUp:
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
minesweeper_state->cursor_y--;
if(minesweeper_state->cursor_y < 0) {
minesweeper_state->cursor_y = PLAYFIELD_HEIGHT - 1;
}
furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyDown:
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
minesweeper_state->cursor_y++;
if(minesweeper_state->cursor_y >= PLAYFIELD_HEIGHT) {
minesweeper_state->cursor_y = 0;
}
furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyRight:
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
minesweeper_state->cursor_x++;
if(minesweeper_state->cursor_x >= PLAYFIELD_WIDTH) {
minesweeper_state->cursor_x = 0;
}
furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyLeft:
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
minesweeper_state->cursor_x--;
if(minesweeper_state->cursor_x < 0) {
minesweeper_state->cursor_x = PLAYFIELD_WIDTH - 1;
}
furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyOk:
if(!minesweeper_state->game_started) {
@@ -500,7 +487,9 @@ int32_t minesweeper_app(void* p) {
break;
case InputKeyOk:
FURI_LOG_D("Minesweeper", "Toggling flag");
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
place_flag(minesweeper_state);
furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyBack:
processing = false;
@@ -512,15 +501,15 @@ int32_t minesweeper_app(void* p) {
}
}
view_port_update(view_port);
furi_mutex_release(minesweeper_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_DIALOGS);
furi_record_close(RECORD_NOTIFICATION);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_mutex_free(minesweeper_state->mutex);
furi_timer_free(minesweeper_state->timer);
free(minesweeper_state);
return 0;

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
# WAV player
A Flipper Zero application for playing wav files. My fork adds support for correct playback speed (for files with different sample rates) and for mono files (original wav player only plays stereo). You still need to convert your file to unsigned 8-bit PCM format for it to played correctly on flipper.
A Flipper Zero application for playing wav files. My fork adds support for correct playback speed (for files with different sample rates) and for mono files (original wav player only plays stereo). ~~You still need to convert your file to unsigned 8-bit PCM format for it to played correctly on flipper~~. Now supports 16-bit (ordinary) wav files too, both mono and stereo!
Original app by https://github.com/DrZlo13.
Original app by https://github.com/DrZlo13.

View File

@@ -127,7 +127,7 @@ static void app_free(WavPlayerApp* app) {
// TODO: that works only with 8-bit 2ch audio
static bool fill_data(WavPlayerApp* app, size_t index) {
if(app->num_channels == 1) {
if(app->num_channels == 1 && app->bits_per_sample == 8) {
uint16_t* sample_buffer_start = &app->sample_buffer[index];
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count_half);
@@ -166,7 +166,108 @@ static bool fill_data(WavPlayerApp* app, size_t index) {
return count != app->samples_count_half;
}
if(app->num_channels == 2) {
if(app->num_channels == 1 && app->bits_per_sample == 16) {
uint16_t* sample_buffer_start = &app->sample_buffer[index];
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
for(size_t i = count; i < app->samples_count; i++) {
//app->tmp_buffer[i] = 0;
}
for(size_t i = 0; i < app->samples_count; i += 2) {
int16_t int_16 =
(((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
float data = ((float)int_16 / 256.0 + 127.0);
data -= UINT8_MAX / 2; // to signed
data /= UINT8_MAX / 2; // scale -1..1
data *= app->volume; // volume
data = tanhf(data); // hyperbolic tangent limiter
data *= UINT8_MAX / 2; // scale -128..127
data += UINT8_MAX / 2; // to unsigned
if(data < 0) {
data = 0;
}
if(data > 255) {
data = 255;
}
sample_buffer_start[i / 2] = data;
}
wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half);
return count != app->samples_count;
}
if(app->num_channels == 2 && app->bits_per_sample == 16) {
uint16_t* sample_buffer_start = &app->sample_buffer[index];
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
for(size_t i = 0; i < app->samples_count; i += 4) {
int16_t L = (((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
int16_t R = (((int16_t)app->tmp_buffer[i + 3] << 8) + (int16_t)app->tmp_buffer[i + 2]);
int32_t int_16 = L / 2 + R / 2; // (L + R) / 2
float data = ((float)int_16 / 256.0 + 127.0);
data -= UINT8_MAX / 2; // to signed
data /= UINT8_MAX / 2; // scale -1..1
data *= app->volume; // volume
data = tanhf(data); // hyperbolic tangent limiter
data *= UINT8_MAX / 2; // scale -128..127
data += UINT8_MAX / 2; // to unsigned
if(data < 0) {
data = 0;
}
if(data > 255) {
data = 255;
}
sample_buffer_start[i / 4] = data;
}
count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
for(size_t i = 0; i < app->samples_count; i += 4) {
int16_t L = (((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
int16_t R = (((int16_t)app->tmp_buffer[i + 3] << 8) + (int16_t)app->tmp_buffer[i + 2]);
int32_t int_16 = L / 2 + R / 2; // (L + R) / 2
float data = ((float)int_16 / 256.0 + 127.0);
data -= UINT8_MAX / 2; // to signed
data /= UINT8_MAX / 2; // scale -1..1
data *= app->volume; // volume
data = tanhf(data); // hyperbolic tangent limiter
data *= UINT8_MAX / 2; // scale -128..127
data += UINT8_MAX / 2; // to unsigned
if(data < 0) {
data = 0;
}
if(data > 255) {
data = 255;
}
sample_buffer_start[i / 4 + app->samples_count / 4] = data;
}
wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half);
return count != app->samples_count;
}
if(app->num_channels == 2 && app->bits_per_sample == 8) {
uint16_t* sample_buffer_start = &app->sample_buffer[index];
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
@@ -270,6 +371,9 @@ static void app_run(WavPlayerApp* app) {
while(1) {
if(furi_message_queue_get(app->queue, &event, FuriWaitForever) == FuriStatusOk) {
if(event.type == WavPlayerEventHalfTransfer) {
wav_player_view_set_chans(app->view, app->num_channels);
wav_player_view_set_bits(app->view, app->bits_per_sample);
eof = fill_data(app, 0);
wav_player_view_set_current(app->view, stream_tell(app->stream));
if(eof) {
@@ -280,6 +384,9 @@ static void app_run(WavPlayerApp* app) {
}
} else if(event.type == WavPlayerEventFullTransfer) {
wav_player_view_set_chans(app->view, app->num_channels);
wav_player_view_set_bits(app->view, app->bits_per_sample);
eof = fill_data(app, app->samples_count_half);
wav_player_view_set_current(app->view, stream_tell(app->stream));
if(eof) {

View File

@@ -35,7 +35,7 @@ void wav_player_speaker_init(uint32_t sample_rate) {
TIM_InitStruct.Prescaler = 0;
//TIM_InitStruct.Autoreload = 1451; //64 000 000 / 1451 ~= 44100 Hz
TIM_InitStruct.Autoreload = 64000000 / sample_rate; //to support various sample rates
TIM_InitStruct.Autoreload = 64000000 / sample_rate - 1; //to support various sample rates
LL_TIM_Init(SAMPLE_RATE_TIMER, &TIM_InitStruct);
@@ -48,16 +48,12 @@ void wav_player_speaker_init(uint32_t sample_rate) {
//=========================================================
//configuring PA6 pin as TIM16 output
//furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedVeryHigh, GpioAltFn14TIM16);
//furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedLow, GpioAltFn14TIM16);
furi_hal_gpio_init_ex(
&gpio_ext_pa6,
GpioModeAltFunctionPushPull,
GpioPullNo,
GpioSpeedVeryHigh,
GpioAltFn14TIM16);
//furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull);
//furi_hal_gpio_write(&gpio_ext_pa6, false);
}
void wav_player_speaker_start() {

View File

@@ -12,6 +12,12 @@ static void wav_player_view_draw_callback(Canvas* canvas, void* _model) {
uint8_t x_pos = 0;
uint8_t y_pos = 0;
/*char buffer[20];
snprintf(buffer, sizeof(buffer), "%d", model->num_channels);
canvas_draw_str(canvas, 0, 10, buffer);
snprintf(buffer, sizeof(buffer), "%d", model->bits_per_sample);
canvas_draw_str(canvas, 0, 20, buffer);*/
// volume
x_pos = 123;
y_pos = 0;
@@ -156,6 +162,18 @@ void wav_player_view_set_play(WavPlayerView* wav_view, bool play) {
wav_view->view, WavPlayerViewModel * model, { model->play = play; }, true);
}
void wav_player_view_set_chans(WavPlayerView* wav_view, uint16_t chn) {
furi_assert(wav_view);
with_view_model(
wav_view->view, WavPlayerViewModel * model, { model->num_channels = chn; }, true);
}
void wav_player_view_set_bits(WavPlayerView* wav_view, uint16_t bit) {
furi_assert(wav_view);
with_view_model(
wav_view->view, WavPlayerViewModel * model, { model->bits_per_sample = bit; }, true);
}
void wav_player_view_set_data(WavPlayerView* wav_view, uint16_t* data, size_t data_count) {
furi_assert(wav_view);
with_view_model(

View File

@@ -43,6 +43,9 @@ typedef struct {
size_t end;
size_t current;
uint8_t data[DATA_COUNT];
uint16_t bits_per_sample;
uint16_t num_channels;
} WavPlayerViewModel;
WavPlayerView* wav_player_view_alloc();
@@ -63,6 +66,9 @@ void wav_player_view_set_play(WavPlayerView* wav_view, bool play);
void wav_player_view_set_data(WavPlayerView* wav_view, uint16_t* data, size_t data_count);
void wav_player_view_set_bits(WavPlayerView* wav_view, uint16_t bit);
void wav_player_view_set_chans(WavPlayerView* wav_view, uint16_t chn);
void wav_player_view_set_ctrl_callback(WavPlayerView* wav_view, WavPlayerCtrlCallback callback);
void wav_player_view_set_context(WavPlayerView* wav_view, void* context);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -77,7 +77,7 @@ static void storage_path_change_to_real_storage(FuriString* path, StorageType re
}
}
FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) {
static FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) {
StorageType type = storage_get_type_by_path(path);
if(storage_type_is_valid(type)) {

View File

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

View File

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

View File

@@ -32,6 +32,8 @@ Images and animated icons should follow the same [naming convention](../assets/R
With it, you can debug FAPs as if they were a part of the main firmware — inspect variables, set breakpoints, step through the code, etc.
If debugging session is active, firmware will trigger a breakpoint after loading a FAP it into memory, but before running any code from it. This allows you to set breakpoints in the FAP's code. Note that any breakpoints set before the FAP is loaded may need re-setting after the FAP is actually loaded, since before loading it debugger cannot know the exact address of the FAP's code.
### Setting up debugging environment
The debugging support script looks up debugging information in the latest firmware build directory (`build/latest`). That directory is symlinked by `fbt` to the latest firmware configuration (Debug or Release) build directory when you run `./fbt` for the chosen configuration. See [fbt docs](./fbt.md#nb) for details.

View File

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

View File

@@ -151,6 +151,10 @@ Header,+,lib/mlib/m-list.h,,
Header,+,lib/mlib/m-rbtree.h,,
Header,+,lib/mlib/m-tuple.h,,
Header,+,lib/mlib/m-variant.h,,
Header,+,lib/one_wire/maxim_crc.h,,
Header,+,lib/one_wire/one_wire_host.h,,
Header,+,lib/one_wire/one_wire_host_timing.h,,
Header,+,lib/one_wire/one_wire_slave.h,,
Header,+,lib/print/wrappers.h,,
Header,+,lib/toolbox/args.h,,
Header,+,lib/toolbox/crc32_calc.h,,
@@ -1394,6 +1398,7 @@ Function,+,manchester_advance,_Bool,"ManchesterState, ManchesterEvent, Mancheste
Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*"
Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState*
Function,+,manchester_encoder_reset,void,ManchesterEncoderState*
Function,+,maxim_crc8,uint8_t,"const uint8_t*, const uint8_t, const uint8_t"
Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*"
Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]"
Function,-,mbedtls_des3_free,void,mbedtls_des3_context*
@@ -1472,6 +1477,32 @@ Function,+,notification_message,void,"NotificationApp*, const NotificationSequen
Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*"
Function,-,nrand48,long,unsigned short[3]
Function,-,on_exit,int,"void (*)(int, void*), void*"
Function,+,onewire_host_alloc,OneWireHost*,const GpioPin*
Function,+,onewire_host_free,void,OneWireHost*
Function,+,onewire_host_read,uint8_t,OneWireHost*
Function,+,onewire_host_read_bit,_Bool,OneWireHost*
Function,+,onewire_host_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t"
Function,+,onewire_host_reset,_Bool,OneWireHost*
Function,+,onewire_host_reset_search,void,OneWireHost*
Function,+,onewire_host_search,uint8_t,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
Function,+,onewire_host_skip,void,OneWireHost*
Function,+,onewire_host_start,void,OneWireHost*
Function,+,onewire_host_stop,void,OneWireHost*
Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t"
Function,+,onewire_host_write,void,"OneWireHost*, uint8_t"
Function,+,onewire_host_write_bit,void,"OneWireHost*, _Bool"
Function,+,onewire_host_write_bytes,void,"OneWireHost*, const uint8_t*, uint16_t"
Function,+,onewire_slave_alloc,OneWireSlave*,const GpioPin*
Function,+,onewire_slave_free,void,OneWireSlave*
Function,+,onewire_slave_receive,_Bool,"OneWireSlave*, uint8_t*, size_t"
Function,+,onewire_slave_receive_bit,_Bool,OneWireSlave*
Function,+,onewire_slave_send,_Bool,"OneWireSlave*, const uint8_t*, size_t"
Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool"
Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*"
Function,+,onewire_slave_set_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*"
Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*"
Function,+,onewire_slave_start,void,OneWireSlave*
Function,+,onewire_slave_stop,void,OneWireSlave*
Function,-,open_memstream,FILE*,"char**, size_t*"
Function,+,path_append,void,"FuriString*, const char*"
Function,+,path_concat,void,"const char*, const char*, FuriString*"
1 entry status name type params
151 Header + lib/mlib/m-rbtree.h
152 Header + lib/mlib/m-tuple.h
153 Header + lib/mlib/m-variant.h
154 Header + lib/one_wire/maxim_crc.h
155 Header + lib/one_wire/one_wire_host.h
156 Header + lib/one_wire/one_wire_host_timing.h
157 Header + lib/one_wire/one_wire_slave.h
158 Header + lib/print/wrappers.h
159 Header + lib/toolbox/args.h
160 Header + lib/toolbox/crc32_calc.h
1398 Function + manchester_encoder_advance _Bool ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*
1399 Function + manchester_encoder_finish ManchesterEncoderResult ManchesterEncoderState*
1400 Function + manchester_encoder_reset void ManchesterEncoderState*
1401 Function + maxim_crc8 uint8_t const uint8_t*, const uint8_t, const uint8_t
1402 Function - mbedtls_des3_crypt_cbc int mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*
1403 Function - mbedtls_des3_crypt_ecb int mbedtls_des3_context*, const unsigned char[8], unsigned char[8]
1404 Function - mbedtls_des3_free void mbedtls_des3_context*
1477 Function + notification_message_block void NotificationApp*, const NotificationSequence*
1478 Function - nrand48 long unsigned short[3]
1479 Function - on_exit int void (*)(int, void*), void*
1480 Function + onewire_host_alloc OneWireHost* const GpioPin*
1481 Function + onewire_host_free void OneWireHost*
1482 Function + onewire_host_read uint8_t OneWireHost*
1483 Function + onewire_host_read_bit _Bool OneWireHost*
1484 Function + onewire_host_read_bytes void OneWireHost*, uint8_t*, uint16_t
1485 Function + onewire_host_reset _Bool OneWireHost*
1486 Function + onewire_host_reset_search void OneWireHost*
1487 Function + onewire_host_search uint8_t OneWireHost*, uint8_t*, OneWireHostSearchMode
1488 Function + onewire_host_skip void OneWireHost*
1489 Function + onewire_host_start void OneWireHost*
1490 Function + onewire_host_stop void OneWireHost*
1491 Function + onewire_host_target_search void OneWireHost*, uint8_t
1492 Function + onewire_host_write void OneWireHost*, uint8_t
1493 Function + onewire_host_write_bit void OneWireHost*, _Bool
1494 Function + onewire_host_write_bytes void OneWireHost*, const uint8_t*, uint16_t
1495 Function + onewire_slave_alloc OneWireSlave* const GpioPin*
1496 Function + onewire_slave_free void OneWireSlave*
1497 Function + onewire_slave_receive _Bool OneWireSlave*, uint8_t*, size_t
1498 Function + onewire_slave_receive_bit _Bool OneWireSlave*
1499 Function + onewire_slave_send _Bool OneWireSlave*, const uint8_t*, size_t
1500 Function + onewire_slave_send_bit _Bool OneWireSlave*, _Bool
1501 Function + onewire_slave_set_command_callback void OneWireSlave*, OneWireSlaveCommandCallback, void*
1502 Function + onewire_slave_set_reset_callback void OneWireSlave*, OneWireSlaveResetCallback, void*
1503 Function + onewire_slave_set_result_callback void OneWireSlave*, OneWireSlaveResultCallback, void*
1504 Function + onewire_slave_start void OneWireSlave*
1505 Function + onewire_slave_stop void OneWireSlave*
1506 Function - open_memstream FILE* char**, size_t*
1507 Function + path_append void FuriString*, const char*
1508 Function + path_concat void const char*, const char*, FuriString*

View File

@@ -3,8 +3,6 @@
#include <stm32wbxx_ll_cortex.h>
#include <fatfs.h>
#define TAG "FuriHal"
void furi_hal_init_early() {
@@ -74,10 +72,6 @@ void furi_hal_init() {
#endif
furi_hal_bt_init();
furi_hal_compress_icon_init();
// FatFS driver initialization
fatfs_init();
FURI_LOG_I(TAG, "FATFS OK");
}
void furi_hal_switch(void* address) {

View File

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

View File

@@ -121,6 +121,9 @@ Header,+,lib/flipper_application/plugins/composite_resolver.h,,
Header,+,lib/flipper_application/plugins/plugin_manager.h,,
Header,+,lib/flipper_format/flipper_format.h,,
Header,+,lib/flipper_format/flipper_format_i.h,,
Header,+,lib/ibutton/ibutton_key.h,,
Header,+,lib/ibutton/ibutton_protocols.h,,
Header,+,lib/ibutton/ibutton_worker.h,,
Header,+,lib/infrared/encoder_decoder/infrared.h,,
Header,+,lib/infrared/worker/infrared_transmit.h,,
Header,+,lib/infrared/worker/infrared_worker.h,,
@@ -170,9 +173,6 @@ Header,+,lib/mlib/m-rbtree.h,,
Header,+,lib/mlib/m-tuple.h,,
Header,+,lib/mlib/m-variant.h,,
Header,+,lib/nfc/nfc_device.h,,
Header,+,lib/one_wire/ibutton/ibutton_key.h,,
Header,+,lib/one_wire/ibutton/ibutton_protocols.h,,
Header,+,lib/one_wire/ibutton/ibutton_worker.h,,
Header,+,lib/one_wire/maxim_crc.h,,
Header,+,lib/one_wire/one_wire_host.h,,
Header,+,lib/one_wire/one_wire_host_timing.h,,
1 entry status name type params
121 Header + lib/flipper_application/plugins/plugin_manager.h
122 Header + lib/flipper_format/flipper_format.h
123 Header + lib/flipper_format/flipper_format_i.h
124 Header + lib/ibutton/ibutton_key.h
125 Header + lib/ibutton/ibutton_protocols.h
126 Header + lib/ibutton/ibutton_worker.h
127 Header + lib/infrared/encoder_decoder/infrared.h
128 Header + lib/infrared/worker/infrared_transmit.h
129 Header + lib/infrared/worker/infrared_worker.h
173 Header + lib/mlib/m-tuple.h
174 Header + lib/mlib/m-variant.h
175 Header + lib/nfc/nfc_device.h
Header + lib/one_wire/ibutton/ibutton_key.h
Header + lib/one_wire/ibutton/ibutton_protocols.h
Header + lib/one_wire/ibutton/ibutton_worker.h
176 Header + lib/one_wire/maxim_crc.h
177 Header + lib/one_wire/one_wire_host.h
178 Header + lib/one_wire/one_wire_host_timing.h

View File

@@ -1,21 +1,3 @@
/**
******************************************************************************
* @file fatfs.c
* @brief Code for fatfs applications
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
#include "fatfs.h"
/** logical drive path */
@@ -35,5 +17,3 @@ void fatfs_init(void) {
DWORD get_fattime(void) {
return 0;
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -1,21 +1,3 @@
/**
******************************************************************************
* @file fatfs.h
* @brief Header for fatfs applications
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
#pragma once
#include "fatfs/ff.h"
@@ -34,6 +16,4 @@ void fatfs_init(void);
#ifdef __cplusplus
}
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
#endif

View File

@@ -1,21 +1,3 @@
/**
******************************************************************************
* @file user_diskio.c
* @brief This file includes a diskio driver skeleton to be completed by the user.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
#include "user_diskio.h"
#include <furi_hal.h>
#include "sector_cache.h"
@@ -287,5 +269,3 @@ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
return res;
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -1,22 +1,3 @@
/**
******************************************************************************
* @file user_diskio.h
* @brief This file contains the common defines and functions prototypes for
* the user_diskio driver.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
#pragma once
#ifdef __cplusplus

View File

@@ -4,8 +4,6 @@
#include <stm32wbxx_ll_cortex.h>
#include <fatfs.h>
#define TAG "FuriHal"
void furi_hal_init_early() {
@@ -80,10 +78,6 @@ void furi_hal_init() {
furi_hal_nfc_init();
furi_hal_rfid_init();
#endif
// FatFS driver initialization
fatfs_init();
FURI_LOG_I(TAG, "FATFS OK");
}
void furi_hal_switch(void* address) {

View File

@@ -11,9 +11,6 @@
#define HID_EP_OUT 0x01
#define HID_EP_SZ 0x10
#define HID_KB_MAX_KEYS 6
#define HID_CONSUMER_MAX_KEYS 2
#define HID_INTERVAL 2
#define HID_VID_DEFAULT 0x046D

View File

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

View File

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

View File

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

View File

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

View File

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

24
lib/ibutton/SConscript Normal file
View File

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

View File

@@ -11,9 +11,6 @@ env.Append(
File("one_wire_host_timing.h"),
File("one_wire_host.h"),
File("one_wire_slave.h"),
File("ibutton/ibutton_key.h"),
File("ibutton/ibutton_worker.h"),
File("ibutton/ibutton_protocols.h"),
File("maxim_crc.h"),
],
)

View File

@@ -455,7 +455,7 @@ uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) {
uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) {
furi_assert(instance);
if(idx < FrequencyList_size(instance->frequencies)) {
if(idx < FrequencyList_size(instance->hopper_frequencies)) {
return *FrequencyList_get(instance->hopper_frequencies, idx);
} else {
return 0;

View File

@@ -1,6 +1,7 @@
import logging
import os
import typing
from enum import Enum
from flipper.utils.programmer import Programmer
from flipper.utils.openocd import OpenOCD
@@ -8,6 +9,14 @@ from flipper.utils.stm32wb55 import STM32WB55
from flipper.assets.obdata import OptionBytesData
class OpenOCDProgrammerResult(Enum):
Success = 0
ErrorGeneric = 1
ErrorAlignment = 2
ErrorAlreadyWritten = 3
ErrorValidation = 4
class OpenOCDProgrammer(Programmer):
def __init__(
self,
@@ -199,18 +208,18 @@ class OpenOCDProgrammer(Programmer):
return True
def otp_write(self, address: int, file_path: str) -> bool:
def otp_write(self, address: int, file_path: str) -> OpenOCDProgrammerResult:
# Open file, check that it aligned to 8 bytes
with open(file_path, "rb") as f:
data = f.read()
if len(data) % 8 != 0:
self.logger.error(f"File {file_path} is not aligned to 8 bytes")
return False
return OpenOCDProgrammerResult.ErrorAlignment
# Check that address is aligned to 8 bytes
if address % 8 != 0:
self.logger.error(f"Address {address} is not aligned to 8 bytes")
return False
return OpenOCDProgrammerResult.ErrorAlignment
# Get size of data
data_size = len(data)
@@ -218,7 +227,7 @@ class OpenOCDProgrammer(Programmer):
# Check that data size is aligned to 8 bytes
if data_size % 8 != 0:
self.logger.error(f"Data size {data_size} is not aligned to 8 bytes")
return False
return OpenOCDProgrammerResult.ErrorAlignment
self.logger.debug(f"Writing {data_size} bytes to OTP at {address:08X}")
self.logger.debug(f"Data: {data.hex().upper()}")
@@ -241,14 +250,14 @@ class OpenOCDProgrammer(Programmer):
self.logger.error(
f"OTP memory at {address + i:08X} is not empty: {device_word:08X}"
)
raise Exception("OTP memory is not empty")
return OpenOCDProgrammerResult.ErrorAlreadyWritten
if device_word != file_word:
already_written = False
if already_written:
self.logger.info(f"OTP memory is already written with the given data")
return True
return OpenOCDProgrammerResult.Success
self.reset(self.RunMode.Stop)
stm32.clear_flash_errors(oocd)
@@ -278,4 +287,8 @@ class OpenOCDProgrammer(Programmer):
stm32.reset(oocd, stm32.RunMode.Run)
oocd.stop()
return validation_result
return (
OpenOCDProgrammerResult.Success
if validation_result
else OpenOCDProgrammerResult.ErrorValidation
)

View File

@@ -34,8 +34,16 @@ OTP_DISPLAYS = {
}
from flipper.app import App
from flipper.cube import CubeProgrammer
from flipper.utils.programmer_openocd import OpenOCDProgrammer
from flipper.utils.programmer_openocd import OpenOCDProgrammer, OpenOCDProgrammerResult
class OTPException(Exception):
def __init__(self, message: str, result: OpenOCDProgrammerResult):
self.message = message
self.result = result
def get_exit_code(self) -> int:
return int(self.result.value)
class Main(App):
@@ -183,13 +191,14 @@ class Main(App):
self.args.serial,
)
if not openocd.otp_write(0x1FFF7000, filename):
raise Exception("Failed to flash OTP")
programmer_result = openocd.otp_write(0x1FFF7000, filename)
if programmer_result != OpenOCDProgrammerResult.Success:
raise OTPException("Failed to flash OTP", programmer_result)
self.logger.info(f"Flashed Successfully")
except Exception as e:
except OTPException as e:
self.logger.exception(e)
return 1
return e.get_exit_code()
finally:
os.remove(filename)
@@ -215,13 +224,14 @@ class Main(App):
self.args.serial,
)
if not openocd.otp_write(0x1FFF7010, filename):
raise Exception("Failed to flash OTP")
programmer_result = openocd.otp_write(0x1FFF7010, filename)
if programmer_result != OpenOCDProgrammerResult.Success:
raise OTPException("Failed to flash OTP", programmer_result)
self.logger.info(f"Flashed Successfully")
except Exception as e:
except OTPException as e:
self.logger.exception(e)
return 1
return e.get_exit_code()
finally:
os.remove(filename)
@@ -249,13 +259,14 @@ class Main(App):
self.args.serial,
)
if not openocd.otp_write(0x1FFF7000, filename):
raise Exception("Failed to flash OTP")
programmer_result = openocd.otp_write(0x1FFF7000, filename)
if programmer_result != OpenOCDProgrammerResult.Success:
raise OTPException("Failed to flash OTP", programmer_result)
self.logger.info(f"Flashed Successfully")
except Exception as e:
except OTPException as e:
self.logger.exception(e)
return 1
return e.get_exit_code()
finally:
os.remove(filename)